Автор оригинала: Codementor Team.
Веб-скребок-это метод, используемый для извлечения информации с веб-страницы с помощью программного обеспечения. Существует множество инструментов для выполнения веб-скребка с помощью Python, некоторые из них:
Проблема большинства этих инструментов заключается в том, что они извлекают только статический HTML-код, поступающий с сервера, а не динамическую часть, которая визуализируется с помощью JavaScript.
Вот несколько вариантов преодоления этой проблемы:
Автоматизированные браузеры
Автоматизированные веб-браузеры, такие как Selenium или Splash , являются полными браузерами, которые работают “без головы”.” Настройка этого немного сложна. Существуют решения для смягчения этой проблемы, такие как docker . Их настройка выходит за рамки данного документа, и поэтому мы сосредоточим наше решение на втором варианте ниже.
Перехват вызовов AJAX
Попробуйте перехватить AJAX-вызовы со страницы и воспроизвести/воспроизвести их.
Область применения этого учебника
Этот учебник научит вас ловить вызовы AJAX и воспроизводить их с помощью библиотеки requests
и браузера Google Chrome. В то время как фреймворки типа scrapy
обеспечивают более надежное решение для веб-скребка, это не обязательно для всех случаев. Вызовы AJAX в основном выполняются против API, который возвращает объект JSON, который может быть легко обработан библиотекой requests
. Этот учебник можно сделать с любым другим браузером, таким как Firefox — процесс тот же самый, единственное, что меняется, – это пользовательский интерфейс dev tools.
Для этого урока мы будем использовать реальный пример: извлечение всех местоположений магазинов Whole foods с их веб-сайта .
Давайте начнем
Первое, что вам нужно знать, это то, что попытка воспроизвести вызов AJAX похожа на использование недокументированного API, поэтому вы должны посмотреть на вызов, который делают страницы. Зайдите на сайт и поиграйте с ним, перейдите по некоторым страницам и посмотрите, как отображается некоторая информация без необходимости перехода на другой URL. После того, как вы закончите играть, вернитесь сюда, чтобы начать выскабливание.
Прежде чем приступить к кодированию, нам сначала нужно узнать, как работает страница. Для этого мы перейдем к страницам, содержащим информацию о магазине.
Во-первых, давайте перейдем к разделу найти магазин сайта:
Давайте ограничим наш скребок магазинами, расположенными в США по штатам:
Теперь мы видим магазины по штату США. Выберите любое состояние, и страница отобразит информацию о магазине, находящемся там. Попробуй, а потом возвращайся. Существует также возможность просмотра историй постранично, это тот вариант, который мы будем использовать для удаления страницы.
Как вы можете видеть, каждый раз, когда вы выбираете состояние или страницу, веб-сайт отображает новые магазины, заменяя старые. Это делается с помощью AJAX-вызова сервера с запросом новых магазинов. Теперь наша задача-поймать этот зов и воспроизвести его. Для этого откройте консоль Chrome DevTools и перейдите в раздел network и подраздел XHR:
XHR (XMLHttpRequest) – это интерфейс для выполнения HTTP-и HTTPS-запросов, поэтому, скорее всего, здесь будет показан ajax-запрос.
Теперь, во время мониторинга сети, выберите вторую страницу, чтобы увидеть, что происходит. Вы должны увидеть что-то вроде этого:
Если вы дважды щелкнете по вызову AJAX, то увидите, что там много информации о магазинах. Вы также можете просмотреть запросы. Давайте сделаем это, чтобы увидеть, есть ли там информация, которая нам нужна. Вызов AJAX не всегда имеет имя “AJAX”, он может иметь любое имя, поэтому вы должны посмотреть, какие данные поступают в каждый вызов подраздела XHR.
Ответ приходит в формате JSON. Он имеет 3 элемента, и информация, которая нам нужна, находится в последнем. Этот элемент содержит ключ данных, содержащий HTML-код, вставляемый на страницу при выборе страницы. Как вы заметите, HTML находится в очень длинной строке текста. Вы можете использовать что-то вроде Code Beautify , чтобы “приукрасить” его. Блок кода, соответствующий каждому хранилищу, находится в этом gist . В этом блоке есть все, что нам нужно.
Теперь мы смотрим на раздел заголовков, чтобы увидеть всю информацию, которая поступает на сервер в вызове.
Здесь мы видим URL-адрес запроса и то, что метод запроса-post. Давайте теперь посмотрим на данные формы, которые отправляются на сервер.
Как видите, на сервер отправляется очень много данных, но не волнуйтесь — не все они нужны. Чтобы увидеть, какие данные действительно нужны, вы можете открыть консоль и сделать различные post-запросы к URL-адресу запроса с помощью библиотеки запросов, я уже сделал это для нас и выделил необходимые данные.
Написание собственного скребка для сайта
Теперь, когда мы знаем, как работает страница и расшифровали вызов AJAX, пришло время начать писать наш скребок. Весь код, написанный здесь, доступен в этом репозитории github .
Мы будем использовать xml CSS selector для извлечения нужной нам информации. Мы извлекем следующее:
- Название магазина
- Полный адрес
- Номер телефона
- Часы работы
Давайте сначала создадим проект и компакт-диск к нему.
$ mrkdir wholefoods-scraper $ cd wholefoods-scraper
Мы должны создать virtualenv.
$ virtualenv venv $ source venv/bin/activate
Теперь мы можем установить библиотеку запросов и сделать файл Python для скребка.
$ pip install requests $ pip install lxml $ pip install cssselect $ touch scraper.py
Теперь откройте файл Python с помощью вашего любимого редактора . Давайте начнем создавать ваш скребок с класса и делать функцию для репликации вызова AJAX:
# Importing the dependencies # This is needed to create a lxml object that uses the css selector from lxml.etree import fromstring # The requests library import requests class WholeFoodsScraper: API_url = 'http://www.wholefoodsmarket.com/views/ajax' scraped_stores = [] def get_stores_info(self, page): # This is the only data required by the api # To send back the stores info data = { 'view_name': 'store_locations_by_state', 'view_display_id': 'state', 'page': page } # Making the post request response = requests.post(self.API_url, data=data) # The data that we are looking is in the second # Element of the response and has the key 'data', # so that is what's returned return response.json()[1]['data']
Теперь, когда у нас есть функция, которая извлекает данные из хранилищ, давайте сделаем ее для анализа этих данных с помощью CSS-селектора, посмотрите на gist , если вы заблудились.
import .... class WholeFoodsScraper: # Same ... # Array to store the scraped stores scraped_stores = [] # get_store_info function def parse_stores(self, data): # Creating an lxml Element instance element = fromstring(data) for store in element.cssselect('.views-row'): store_info = {} # The lxml etree css selector always returns a list, so we get # just the first item store_name = store.cssselect('.views-field-title a')[0].text street_address = store.cssselect('.street-block div')[0].text address_locality = store.cssselect('.locality')[0].text address_state = store.cssselect('.state')[0].text address_postal_code = store.cssselect('.postal-code')[0].text phone_number = store.cssselect('.views-field-field-phone-number div')[0].text try: opening_hours = store.cssselect('.views-field-field-store-hours div')[0].text except IndexError: # Stores that doesn't have opening hours are closed and should not be saved. # This is found while debugging, so don't worry if you get errors when you # run a scraper opening_hours = 'STORE CLOSED' continue full_address = "{}, {}, {} {}".format(street_address, address_locality, address_state, address_postal_code) # now we add all the info to a dict store_info = { 'name': store_name, 'full_address': full_address, 'phone': phone_number, 'hours': opening_hours } # We add the store to the scraped stores list self.scraped_stores.append(store_info)
Теперь нам просто нужна функция для вызова get_stores_info
для каждой из 22 страниц, на которых есть Wholefoods stores by US state site, а затем разбора данных с помощью parse_stores
. Мы также сделаем небольшую функцию для сохранения всех очищенных данных в файл JSON.
import json import ... All stays the same class WholeFoodsScraper: # Same ... def run(self): for page in range(22): # Retrieving the data data = self.get_stores_info(page) # Parsing it self.parse_stores(data) print('scraped the page' + str(page)) self.save_data() def save_data(self): with open('wholefoods_stores.json', 'w') as json_file: json.dump(self.scraped_stores, json_file, indent=4)
Теперь наш скребок готов! Нам просто нужно запустить его. Добавьте это в конец файла python:
if __name__ == '__main__': scraper = WholeFoodsScraper() scraper.run()
Вы можете увидеть результаты в whole foods_stores.json
файл.
Вывод
Теперь вы, должно быть, спрашиваете: ” Когда я должен использовать автоматический браузер и, ну, должен ли я попытаться воспроизвести вызовы AJAX? ” Ответ прост: всегда старайтесь воспроизвести вызовы AJAX, прежде чем пытаться сделать что — то более сложное или тяжелое, например, использовать автоматический браузер-попробуйте этот более простой и легкий подход!
Точно так же вы также можете попробовать создать свой собственный веб-скребок с помощью Node.js .
Биография автора
Хулио Алехандро-внештатный разработчик программного обеспечения и инженер-химик из Венесуэлы с растущими навыками. Он любит заниматься веб-разработкой, веб-скребком и автоматизацией! Он работает в основном на серверном программировании с Python/Django/Flask. Он также занимается front-end разработкой с помощью JavaScript-фреймворков, таких как Vue или React.