CoderCastrov logo
CoderCastrov
Парсер

Отправка нескольких POST-запросов со страницы, полученной с помощью парсера Scrapy

Отправка нескольких POST-запросов со страницы, полученной с помощью парсера Scrapy
просмотров
5 мин чтение
#Парсер

Шаблон для программного воспроизведения AJAX POST-запросов при парсинге веб-страниц с помощью Scrapy

Вы когда-нибудь пытались извлечь данные, значение которых меняется в зависимости от других доступных на веб-странице опций? В этой статье я покажу возможный шаблон для извлечения всех возможных значений точки данных, когда они получаются через AJAX-запросы.

Более конкретно, я использовал этот шаблон, создавая smarthiker.co.uk, веб-сайт для сравнения цен на походное, альпинистское и горное снаряжение. При парсинге деталей продуктов я понял, что цена на один и тот же продукт может измениться в зависимости от цвета или размера. Два ниже приведенных изображения (которые вымышленные) должны помочь вам понять, о чем я говорю.

Which is probably not worth 10 extra £ (this is just a thought experiment though, they are both charged the same price)

Основной парсинг с помощью Scrapy

Scrapy - это библиотека на языке Python, которая предоставляет простой в использовании фреймворк для парсинга. Одним из основных компонентов библиотеки является класс Spider, который позволяет указать, с какого URL начинать парсинг, как парсить полученные HTML-страницы и, возможно, отправлять другие запросы из них.

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

Ниже вы можете найти некоторый псевдокод на языке Python для парсера, который парсит интернет-магазин походного снаряжения BananaFingers.co.uk. Все интересующие нас товары доступны на страницах, относящихся к конкретным брендам, на каждой из которых представлен пагинированный список товаров. Приведенный ниже код является упрощенной версией кода, используемого в производстве, и предназначен только для более легкого понимания того, как парсить этот веб-сайт, а не для его фактического парсинга.

Таким образом, используя модифицированную версию приведенного выше фрагмента кода, вы сможете успешно спарсить все продаваемые товары на веб-сайте. Однако для каждого товара парсится только одна цена, та, которая указана для комбинации цвета и размера по умолчанию, загруженной на веб-страницу. Чтобы получить цены для конкретных комбинаций цвета и размера, вам необходимо повторить AJAX-запросы, отправляемые для каждой комбинации. Когда вы выбираете цвет из выпадающего списка на веб-странице, отправляется AJAX-запрос POST, и значение цены обновляется на странице. Таким образом, чтобы получить полную информацию, нам нужно отправить столько запросов, сколько доступно комбинаций цвета и размера, и каким-то образом вернуть их результаты с исходным ответом для страницы товара.

Определение спецификаций POST-запроса

Используя инструменты разработчика вашего предпочтительного браузера, должно быть относительно легко увидеть спецификации POST-запроса (целевой URL, данные запроса и т. д.). Как видно на рисунке ниже, мы легко можем получить доступ к содержимому данных запроса и понять, что нам нужно изменить, чтобы получить цены на цвет и размер. В данном случае нам необходимо указать поле "цвет", "размер" и идентификатор товара.

Проверка запросов через инструменты разработчика

Ключи и значения данных запроса POST для конкретного товара ("attributes[field_waist_size]": 1595, "attributes[field_color]": 730) можно автоматически извлечь из DOM. Как видно на рисунке ниже, ключ для поля "цвет/размер" доступен в атрибуте "name" элемента HTML "select". Значение можно получить, используя атрибут "value" элемента HTML "option".

Проверка элементов цвета и размера в DOM

После получения ключей и значений для всех доступных вариантов должно быть относительно просто создать список возможных комбинаций и собрать необходимые данные запросов для каждой комбинации.

Теперь у нас должен быть список пар ключ-значение, которые можно использовать в качестве данных запросов для различных POST-запросов, которые мы отправим. Теперь мы отправим запросы и сохраняем результаты где-то, чтобы они могли быть возвращены вместе с конечным товаром.

Два возможных подхода

Я лично считаю, что наиболее удобным способом получения всех цен на цвета и размеры для продукта является последовательная отправка POST-запросов, передача полученных цен для каждой комбинации и, по истечении списка комбинаций, возврат товара с исходным HTML-кодом страницы продукта и списком полученных цен. В качестве альтернативы, мы можем возвращать для каждого POST-запроса товар с исходным HTML-кодом и относительной ценой, а затем объединять все товары для продукта на следующем этапе обработки.

В первом случае для каждого POST-запроса мы будем ожидать ответа (и разбирать его) перед отправкой следующего запроса. В то время как во втором случае запросы будут отправлены примерно одновременно (например, через цикл по итерируемому объекту, содержащему данные запросов), и каждый ответ будет разбираться независимо от других, как только будет получен. Дополнительное время ожидания в первом подходе, вероятно, сделает его более длительным, чем второй подход, при сканировании всего веб-сайта, хотя я не тестировал это.

cb_kwargs (ранее известный как meta) спасает ситуацию!

Если мы хотим пойти по первому пути, мы можем использовать аргумент cb_kwargs в объекте Request, чтобы передать все детали продукта в ответ. Таким образом, мы сможем передать список уже полученных цен, список пакетов, которые нам нужно отправить, и HTML исходной страницы. Приведенный ниже псевдокод должен дать вам представление о возможном шаблоне для этого.

Как показано в приведенном выше фрагменте кода, метод parse_product_item вызывается сразу же после достижения страницы продукта пауком. Если это первый раз, когда этот метод вызывается для этой страницы, список response_params_for_post не будет существовать, поэтому он будет создан и заполнен списком пакетов, которые должны быть отправлены для каждого POST-запроса. В противном случае, если список уже существует, будет извлечен и использован следующий элемент в качестве пакета для следующего POST-запроса, ответ на который будет обработан снова через метод parse_product_item. Этот процесс будет продолжаться, пока список пакетов для POST-запросов не будет исчерпан. В этот момент исходное тело HTML будет передано вместе со всеми полученными ценами в пользовательский ItemLoader, чтобы значения для элемента были извлечены.

Заключение

Для краткого изложения, следуя нижеприведенным шагам, можно получить значения, загруженные через несколько AJAX-запросов, вызванных интерактивными компонентами:

  • Понять детали POST/GET-запросов, используя данные страницы продукта
  • Создать список пакетов/параметров, содержащий один элемент для каждого запроса, который вы хотите отправить
  • Добавить один оператор if-else в метод разбора, чтобы вы могли либо создать список пакетов/параметров, либо разобрать ответ, полученный от предыдущего POST/GET-запроса
  • Добавить еще один оператор if-else в метод разбора, чтобы вы могли либо отправить следующий POST/GET-запрос, либо вернуть элемент с исходным HTML и всеми полученными значениями

Это, вероятно, одна из самых интересных проблем парсинга, с которой я сталкивался при создании smarthiker.co.uk. Я был довольно удивлен тем, что один и тот же продукт для походов/восхождений/горных походов может продаваться по разной цене в зависимости от цвета или размера. Тем не менее, надежный сайт сравнения цен должен уметь обрабатывать и такие ситуации.