Скрапинг бесконечно прокручивающейся страницы
Добро пожаловать в советы по Scrapy от профессионалов! В этом месяце мы поделимся несколькими уловками, чтобы помочь ускорить вашу работу связанную с веб-скрапингом. Как ведущие мэйнтейнеры Scrapy мы сталкиваемся с каждыми препятствием, которое вы можете себе представить. Так что не волнуйтесь — вы в надёжных руках. Не стесняйтесь контактировать с нами в твиттере или фейсбуке с любыми предложениями для будущих статей.

В эру одностраничных приложений и тонн AJAX-запросов на одной странице множество веб-сайтов заменили кнопку навигации "вперёд/назад" на причудливый механизм бесконечной прокрутки страницы. Веб-сайты использующие этот механизм загружают новую сущность каждый раз, когда пользователь достигает конца страницы при вертикальной прокрутке(вспомните Twitter, Facebook, Google Images). Даже несмотря на то, что UX-эксперты утверждают что механизм бесконечной прокрутки предоставляет чрезмерное количество данных для пользователей, мы видим увеличивающееся количество веб-страниц прибегающих к предоставлению бесконечного списка результатов.
Одна из первых вещей которую мы делаем при разработке своего веб-скрапера — мы ищем на сайте компоненты пользовательского интерфейса, которые содержат в себе ссылки ведущие на следующую страницу результатов. К сожалению, та��ие ссылки не представлены на страницах с бесконечной прокруткой.
Хотя такой сценарий и может показаться классическим случаем для таких JavaScript-фреймворков как Splash или Selenium, это, действительно, легко внедрить. Всё что вам нужно сделать вместо имитации взаимодействия с пользователем через один из тех фреймворков — это исследовать AJAX-запросы вашего браузера при прокрутке страницы и затем воссоздать эти запросы в вашем пауке в Scrapy.
Давайте используем Spidy Quotes в качестве примера и построим паука, который получит все элементы перечисленные на странице.
Инспектирование страницы
Первым делом нам надо понять как работает бесконечная прокрутка на странице. И мы можем сделать это используя панель Network в инструментах разработчика в браузере. Откроем панель и прокрутим страницу, чтобы увидеть запросы отправляемые браузером:

Нажмём на запрос, чтобы рассмотреть его подробнее. Как мы можем видеть, браузер посылает запрос на /api/quotes?page=x и получает в ответе JSON-объект подобный этому:
{ "has_next":true, "page":8, "quotes":[ { "author":{ "goodreads_link":"/author/show/1244.Mark_Twain", "name":"Mark Twain" }, "tags":["individuality", "majority", "minority", "wisdom"], "text":"Whenever you find yourself on the side of the ..." }, { "author":{ "goodreads_link":"/author/show/1244.Mark_Twain", "name":"Mark Twain" }, "tags":["books", "contentment", "friends"], "text":"Good friends, good books, and a sleepy ..." } ], "tag":null, "top_ten_tags":[["love", 49], ["inspirational", 43], ...] }
Это информация которая нужна нам для нашего паука. Всё что нужно сделать в нём — это генерировать запросы на /api/quotes?page=x увеличивая x пока значение поля has_next не станет false. Самое прекрасное в этом — что нам даже не надо скрапить содержимое HTML, чтобы получить данные которые нам нужны. Это всё содержится в красивом машиночитаемом формате JSON.
Построение паука
Вот наш паук. Он извлекает целевые данные из содержимого в JSON-формате, которое мы получили в ответе от сервера. Такой подход проще и более надёжен, чем копаться в HTML дереве страницы, надеясь что изменение разметки не сломает наших пауков.
import json import scrapy class SpidyQuotesSpider(scrapy.Spider): name = 'spidyquotes' quotes_base_url = 'http://spidyquotes.herokuapp.com/api/quotes?page=%s' start_urls = [quotes_base_url % 1] download_delay = 1.5 def parse(self, response): data = json.loads(response.body) for item in data.get('quotes', []): yield { 'text': item.get('text'), 'author': item.get('author', {}).get('name'), 'tags': item.get('tags'), } if data['has_next']: next_page = data['page'] + 1 yield scrapy.Request(self.quotes_base_url % next_page)
Если вы захотите сделать что-то ещё, используя полученные знания, то вы можете поэкспериментировать с созданием паука для нашего блога, так как он тоже использует бесконечную прокрутку для загрузки старых постов
Заключение
Если вы чувствовали себя несколько обескураженно относительно перспективы скрапинга веб-сайтов имеющих бесконечную прокрутку, то, надеюсь, теперь вы чувствуете себя более уверенно. В следующий раз, когда вы будете иметь дело со страницей базируемой на вызовах AJAX-запросов в результате действий пользователя, посмотрите какой запрос делает ваш браузер и воспроизведите его в вашем пауке. Ответ на запрос, как правило, имеет формат JSON, что делает вашего паука гораздо проще.
