Не всегда выходит получить код страницы простым запросом. Иногда на сайте присутствуют некоторые ограничения, не позволяющие это сделать. Часть из них можно обойти, подставив User-Agent, другие решаются подстановкой необходимых куки и заголовков. Разберем на примере auto.ru, как работать с такими сайтами.
Первый способ (быстрый)
Для начала попробуем сделать обычный запрос, допустим, через парсер Net::HTTP, и посмотрим, что придет нам в ответ. Для этих целей будет использовать Тестовый парсинг в режиме дебага. Так будет удобно видеть все, что происходит от запроса к ответу, в том числе заголовки и куки. Отправим запрос на http://auto.ru.
Ответ пришел с кодом 200 ОК. Видим, что вес ответа составляет 5553 байта. Раскрываем код страницы через See full data или листаем чуть ниже и смотрим, что нам пришло.
Видим, что нужного нам контента нет. Чтобы продолжить работу, нужно согласиться с правилами сайта. Об этом нам подсказывает текст, выделенный красным. Смотрим дальше. Разработчики оставили нам подсказку, она выделена синим. Понимаем, что необходимо вместе с запросом передавать какие-то куки, но где их взять? Снова обратимся к дебагу и посмотрим данные об ответе, который пришел.
Находим заголовки и видим, что среди них есть set-cookie. Это то, что нам нужно. Теперь надо создать простейший JavaScript-парсер, который первым запросом будет получать эти куки и подставлять их во все последующие запросы.
Для удобства, создадим отдельный метод *getCookie(), в котором будет происходить вся работа с их получением. Делаем запрос на http://auto.ru без каких-либо дополнительных заголовков. Если запрос удачный, возвращаем response.headers[‘set-cookie’], который мы видели в Тестовом парсинге. Далее, в методе *parse() обращаемся к методу getCookie() и помещаем наши куки в константу cookie. Пробуем отправить второй запрос, указав в headers наши cookie и смотрим в дебаге, какой теперь будет ответ.
Видим, что ответ не изменился. Значит чего-то не хватает. Смотрим дальше код страницы, пытаемся понять, что мы могли упустить.
Обнаруживаем вот такую строчку, которую мы пропустили в первый раз. Попробуем подставить в наши куки «;autoru_gdpr=1;path=/;max-age=31536000;secure;domain=auto.ru».
Вот так. Снова делаем запрос и смотрим, что получилось.
Размер ответа стал значительно больше. Если раскрыть код, станет ясно, что все прошло успешно. Таким образом, требовалось лишь понять, каких заголовков не хватает запросу, чтобы auto.ru его пропустил и отдал нам нужные данные. Теперь мы можем передавать cookie в объект set и использовать их между запросами, единожды получив. Если в какой-то момент заголовки (куки) устареют, можно попытаться идентифицировать это по характерному ответу и получить заново, т.к. для этого будет достаточно обратиться к методу getCookie(), который мы вынесли отдельно.
Второй способ (простой, но ресурсоемкий)
Также существует альтернативный и в какой-то мере более простой вариант получения заголовков и куки через модуль puppeteer. Он может быть уместен в случаях, когда заголовков слишком много и невозможно понять, откуда они приходят. Такое довольно часто происходит с CSRF-токеном, когда он скрыт в скриптах, подгружаемых страницей динамически. Рассмотрим этот способ на примере.
Перед началом необходимо установить puppeteer в директории /aparser/files/ командой npm I puppeteer. Также необходимо создать файл config.txt с содержимым «allow_dangerous_node_modules: 1» в директории /aparser/config/, чтобы puppeteer корректно работал.
Теперь подключим puppeteer внутри нашего парсера. И сразу объявим переменную browser.
Переменную browser мы объявили вне класса парсера для того, чтобы можно было «аварийно» завершить его работу в методе destroy() в случае, когда по какой-то причине она не была завершена в ходе работы парсера, иначе процессы chrome.exe (chromium) будут висеть в памяти даже после окончания парсинга и нагружать компьютер.
Сразу напишем метод destroy().
Не забываем, что puppeteer – асинхронный модуль, значит перед его методами следует ставить await, а перед функцией\методом, в котором происходит работа с ним, нужно писать async.
Данный метод вызывается при завершении работы парсера и в случае, если переменная browser не пуста, он завершает работу (закрывает) браузер, который в ней объявлен.
Объявим внутри класса новую асинхронную функцию – getCookieByPuppeteer().
На вход подаем set, чтобы вытащить из него наш запрос (http://auto.ru).
Теперь по порядку. Сначала запускаем браузер с помощью метода puppeteer.launch() и помещаем его в browser. Передаем в качестве аргумента объект с параметром headless равным false. Этот параметр указывает на то, чтобы браузер запустился вместе с интерфейсом. Удобно использовать при отладке. Значение true соответственно скроет интерфейс.
Далее создаем новую страницу (browser.newPage()) и помещаем ее в константу page. Метод page.goto(url) позволяет перейти по заданной ссылке. Передаем туда set.query. Т.к. необходимо дождаться загрузки страницы, используем page.waitForSelector(selector). Где selector – указываем класс кнопки, на которую нужно нажать, чтобы согласиться с правилами. Следующим действием нажимает на эту кнопку по тому же селектору методом page.click(selector). Затем дожидаемся полной загрузки страницы (page.waitForNavigation()). После того, как страница была загружена, вытаскиваем куки методом page.cookies() и помещаем их в переменную cookie. Закрываем браузер и возвращаем cookie.
Далее немного изменим метод *parse(). Во-первых, добавим async перед названием метода. Во-вторых, объявим переменную cookie, как и прежде, но вместо yield* перед this.getCookieByPuppeteer() пишем await, т. к. это уже не генератор, а асинхронная функция. Больше ничего менять не нужно. Пробуем запустить тестовый парсинг.
Можем наблюдать, как puppeteer запускает браузер, нажимает на кнопку и переходит на главную страницу. Затем браузер закрывается и куки передаются в парсер.
Смотрим, прошел ли запрос успешно.
Видим, что все отлично.
Мы разобрали два способа работы с куки и заголовками: простой и чуть сложнее. Оба имеют право на жизнь, но рекомендуется все же использовать просто запросы, т. к. они быстрее и менее подвержены ошибкам.
-
Вступайте в наш Telegram чат: https://t.me/a_parser Нас уже 2600+ и мы растем!Скрыть объявление
Работа с куками и заголовками на примере соглашения с правилами сайта auto.ru
Рассмотрим два варианта: быстрый (this.request) и простой, но ресурсоемкий (puppeteer).