Python3 запрашивает библиотеку для отправки формы, которая запрещает запрос post

#javascript #forms #python-3.x #web-scraping #python-requests

#javascript #формы #python-3.x #веб-очистка #python-запросы

Вопрос:

Я пытаюсь получить полицейский участок из заданного местоположения на веб-странице полиции Филадельфии. У меня слишком много мест, чтобы сделать это вручную, поэтому я пытаюсь автоматизировать процесс, используя библиотеку запросов Python. Форма веб-страницы, содержащая значение местоположения, выглядит следующим образом:

 <form id="search-form" method="post" action="districts/searchAddress">
<fieldset>
    <div class="clearfix">
        <label for="search-address-box"><span>Enter Your Street Address</span></label>
        <div class="input">
            <input tabindex="1" class="district-street-address-input" id="search-address-box" name="name" type="text" value="">
        </div>
    </div>
    <div class="actions" style="float: left;">
        <button tabindex="3" type="submit" class="btn btn-success">Search</button>
    </div>
    <a id="use-location" href="https://www.phillypolice.com/districts/index.html?_ID=7amp;_ClassName=DistrictsHomePage#" style="float: left; margin: 7px 0 0 12px;"><i class="icon-location-arrow"></i>Use Current Location</a>
    <div id="current-location-display" style="display: none;"><p>Where I am right now.</p></div>
</fieldset>
</form>
  

Однако, когда я пытаюсь опубликовать или поместить на веб-страницу, используя следующее:

 r = requests.post('http://www.phillypolice.com/districts',data={'search-address-box':'425 E. Roosevelt Blvd'})
  

Я получаю ошибку 405, сообщение не разрешено. Затем я отключил Javascript и попытался найти район на веб-странице, и когда я нажал отправить, я получил то же сообщение об ошибке 405. Поэтому форма определенно не отправлена, и район найден с помощью JavaScript.

Есть ли способ имитировать «нажатие» кнопки отправки для запуска JavaScript с использованием библиотеки запросов?

Ответ №1:

Данные извлекаются после первого запроса Google maps к координатам, где конечным запросом является get, как показано ниже:

введите описание изображения здесь

Вы можете настроить бесплатную учетную запись с помощью bing maps api и получить координаты, необходимые для отправки запроса get:

 import requests

key = "my_key"
coord_params = {"output": "json",
                "key": key}

# This provides the coordinates.
coords_url = "https://dev.virtualearth.net/REST/v1/Locations"

# Template to pass each address to in your actual loop.
template = "{add},US"
url = "https://api.phillypolice.com/jsonservice/Map/searchAddress.json"
with requests.Session() as s:
    # Add the query param passing in each zipcode
    coord_params["query"] = template.format(add="425 E. Roosevelt Blvd")
    js = s.get(coords_url, params=coord_params).json()
    # Parse latitude and longitude from the returned json.
    # Call str to make make it into `(lat, lon)`
    latitude_longitude = str((js[u'resourceSets'][0][u'resources'][0]["point"][u'coordinates']))
    data = s.get(url, params={"latlng": latitude_longitude})

    print(data.json())
  

Если мы запустим его без моего ключа:

 In [2]: import requests
   ...: 
   ...: key = "my_key..."
   ...: 
   ...: coord_params = {"output": "json",
   ...:                 "key": key}
   ...: coords_url = "https://dev.virtualearth.net/REST/v1/Locations"
   ...: template = "{add},US"
   ...: url = "https://api.phillypolice.com/jsonservice/Map/searchAddress.json"
   ...: with requests.Session() as s:
   ...:     coord_params["query"] = template.format(add="425 E. Roosevelt Blvd")
   ...: 
   ...:     js = s.get(coords_url, params=coord_params).json()
   ...:     latitude_longitude = str(js[u'resourceSets'][0][u'resources'][0]["po
   ...: int"][u'coordinates'])
   ...:     print(latitude_longitude)
   ...:     data = s.get(url, params={"latlng": latitude_longitude})
   ...:     print(data.json())
   ...:     
[40.02735900878906, -75.1153564453125]
{'response': ['35', '2', 'Marques Newsome', 'PPD.35_PSA2@phila.gov ', '267-357-1436']}
  

Вы можете увидеть, что он соответствует ответу, который вы видите, если посмотрите на запрос в своем браузере.

Комментарии:

1. Нет, это несправедливо — не использовать Google geocode api! Хорошая идея!

2. @alecxe, что я могу сказать, мне нравятся бесплатные вещи!

Ответ №2:

При нажатии кнопки «отправить» происходит 2 основные вещи — возникает запрос к службе геокодирования Google и запрос XHR к конечной точке «searchAddress.json», которая использует координаты, возвращаемые службой геокодирования.

Вы можете попытаться имитировать вышеуказанные запросы, тщательно обрабатывая все ключи API и требуемые параметры, или вы можете остаться на более высоком уровне и использовать автоматизацию браузера через selenium .

Рабочий пример использования PhantomJS безголового браузера:

 In [2]: from selenium import webdriver

In [3]: driver = webdriver.PhantomJS()

In [4]: driver.get("https://www.phillypolice.com/districts/")

In [5]: address = "425 E. Roosevelt Blvd"

In [6]: search_box = driver.find_element_by_id("search-address-box")

In [7]: search_box.send_keys(address)

In [8]: search_box.submit()

In [9]: driver.find_element_by_css_selector("#district-menu h2").text
Out[9]: u'35th District'

In [10]: driver.find_element_by_css_selector("#district-menu h4").text
Out[10]: u'PSA 2'
  

И вам может потребоваться явное ожидание для обработки проблем с «синхронизацией».

Комментарии:

1. Спасибо за пример, поскольку в ответе используется selenium, можно ли с уверенностью предположить, что requests — не лучший инструмент для этой работы?

2. @Turtle ну, вы все равно можете выполнять оба запроса в Google и обратно в phillypolice с requests помощью — это просто более низкоуровневая вещь, и для нее потребуется больше движущихся частей. Я посмотрю, смогу ли я предоставить вам и эту опцию. Спасибо.

3. На самом деле это не работает. Я получаю исключение «элемент не найден» с помощью selenium. Я только что попытался перебрать адреса, используя этот код в функции. Должен ли я сбрасывать драйвер каждый раз?

4. @Turtle нет, это часть «явного ожидания», о которой я вас предупреждал. Пожалуйста, ознакомьтесь с полным кодом по адресу gist.github.com/alecxe/354616c694d08f7cd40b9d3c77a9f7ca . Спасибо.