Выбор динамически загружаемого контента¶
Некоторые веб-страницы показывают нужные данные, когда вы загружаете их в веб-браузер. Однако, когда вы загружаете их с помощью Scrapy, вы не можете получить желаемые данные с помощью селекторов.
В этом случае рекомендуется обратиться к поиску источника данных и извлечь из него данные.
Если вы этого не сделаете, но, тем не менее, можете получить доступ к нужным данным через DOM из своего веб-браузера, см. Предварительный рендеринг JavaScript.
Поиск источника данных¶
Чтобы извлечь желаемые данные, вы должны сначала найти их исходное местоположение.
Если данные представлены в нетекстовом формате, таком как изображение или документ PDF, используйте сетевой инструмент своего веб-браузера, чтобы найти соответствующий запрос, и воспроизвести его.
Если ваш веб-браузер позволяет выбрать желаемые данные в виде текста, данные могут быть определены во встроенном коде JavaScript или загружены из внешнего ресурса в текстовом формате.
В этом случае вы можете использовать такой инструмент, как wgrep, чтобы найти URL-адрес этого ресурса.
Если оказывается, что данные поступают с исходного URL-адреса, вы должны проверить исходный код веб-страницы определить, где находятся данные.
Если данные поступают с другого URL-адреса, вам потребуется воспроизвести соответствующий запрос.
Проверка исходного кода веб-страницы¶
Иногда вам нужно проверить исходный код веб-страницы (не DOM), чтобы определить, где находятся некоторые требуемые данные.
Используйте команду fetch
Scrapy, чтобы загрузить содержимое веб-страницы в том виде, в каком его видит Scrapy:
scrapy fetch --nolog https://example.com > response.html
Если желаемые данные находятся во встроенном коде JavaScript в элементе <script/>
, см. Разбор кода JavaScript.
Если вы не можете найти нужные данные, сначала убедиться, что это не просто Scrapy: загрузить веб-страницу с помощью HTTP-клиента, такого как curl или wget, и посмотрите, можно ли найти информацию в полученном ими ответе.
Если они получат ответ с желаемыми данными, изменить свой Scrapy Request
, чтобы он соответствовал таковому у другого HTTP-клиента. Например, попробовать использовать ту же строку пользовательского агента (USER_AGENT
) или ту же headers
.
Если они также получат ответ без требуемых данных, вам нужно будет предпринять шаги, чтобы сделать ваш запрос более похожим на запрос веб-браузера. См. Воспроизведение запросов.
Воспроизведение запросов¶
Иногда нам нужно воспроизвести запрос так, как его выполняет наш веб-браузер.
Используйте сетевой инструмент своего веб-браузера, чтобы увидеть, как ваш веб-браузер выполняет желаемый запрос, и попробовать воспроизвести данный запрос с помощью Scrapy.
Может быть достаточно получить Request
с тем же методом HTTP и URL-адресом. Однако вам может также потребоваться воспроизвести тело, заголовки и параметры формы (см. FormRequest
) этого запроса.
Поскольку все основные браузеры позволяют экспортировать запросы в формате cURL, Scrapy включает метод from_curl()
для генерации эквивалентного Request
из команды cURL. Для получения дополнительной информации посетите запрос от curl в разделе сетевых инструментов.
Как только вы получить ожидаемый ответ, можете извлечь желаемые данные из него.
Вы можете воспроизвести любой запрос с помощью Scrapy. Однако иногда воспроизведение всех необходимых запросов может показаться неэффективным для разработчиков. Если это ваш случай и скорость сканирования не является для вас серьезной проблемой, в качестве альтернативы можно рассмотреть предварительный рендеринг JavaScript.
Если вы получаете ожидаемый ответ «иногда», но не всегда, возможно, проблема не в вашем запросе, а в целевом сервере. Целевой сервер может быть глючным, перегруженным или запрещённым по некоторым вашим запросам.
Обратите внимание, что для преобразования команды cURL в запрос Scrapy вы можете использовать curl2scrapy.
Обработка разных форматов ответов¶
Если у вас есть ответ с желаемыми данными, то, как вы извлечете из него нужные данные, зависит от типа ответа:
Если ответ — HTML или XML, используйте селекторы как обычно.
Если ответ — JSON, используйте
json.loads()
для загрузки требуемых данных изresponse.text
:data = json.loads(response.text)
Если желаемые данные находятся внутри кода HTML или XML, встроенного в данные JSON, вы можете загрузить данный код HTML или XML в
Selector
, а затем используйте это, как обычно:selector = Selector(data['html'])
Если ответ представляет собой JavaScript или HTML с элементом
<script/>
, содержащим желаемые данные, см. Разбор кода JavaScript.Если ответ — CSS, используйте регулярное выражение, чтобы извлечь нужные данные из
response.text
.
Если ответ представляет собой изображение или другой формат, основанный на изображениях (например, PDF), прочитать ответ как байты из
response.body
и используйте решение OCR для извлечения желаемых данных в виде текста.Например, вы можете использовать pytesseract. Для чтения таблицы из PDF-файла tabula-py может быть лучшим выбором.
Если ответом является SVG или HTML со встроенным SVG, содержащим желаемые данные, вы можете извлечь желаемые данные с помощью селекторов, поскольку SVG основан на XML.
В противном случае вам может потребоваться преобразовать код SVG в растровое изображение, а обрабатывать это растровое изображение.
Разбор кода JavaScript¶
Если желаемые данные жестко запрограммированы в JavaScript, вам сначала нужно получить код JavaScript:
Если код JavaScript находится в файле JavaScript, просто прочитайте
response.text
.Если код JavaScript находится в элементе
<script/>
страницы HTML, используйте селекторы для извлечения текста внутри этого элемента<script/>
.
Если у вас есть строка с кодом JavaScript, вы можете извлечь из нее нужные данные:
Возможно, вы сможете использовать регулярное выражение для извлечения нужных данных в формате JSON, которые затем можно будет проанализировать с помощью
json.loads()
.Например, если код JavaScript содержит отдельную строку, например
var data = {"field": "value"};
, вы можете извлечь данные данные следующим образом:>>> pattern = r'\bvar\s+data\s*=\s*(\{.*?\})\s*;\s*\n' >>> json_data = response.css('script::text').re_first(pattern) >>> json.loads(json_data) {'field': 'value'}
chompjs предоставляет API для синтаксического анализа объектов JavaScript в
dict
.Например, если код JavaScript содержит
var data = {field: "value", secondField: "second value"};
, вы можете извлечь данные данные следующим образом:>>> import chompjs >>> javascript = response.css('script::text').get() >>> data = chompjs.parse_js_object(javascript) >>> data {'field': 'value', 'secondField': 'second value'}
В противном случае используйте js2xml для преобразования кода JavaScript в XML-документ, который можно проанализировать с помощью селекторов.
Например, если код JavaScript содержит
var data = {field: "value"};
, вы можете извлечь данные данные следующим образом:>>> import js2xml >>> import lxml.etree >>> from parsel import Selector >>> javascript = response.css('script::text').get() >>> xml = lxml.etree.tostring(js2xml.parse(javascript), encoding='unicode') >>> selector = Selector(text=xml) >>> selector.css('var[name="data"]').get() '<var name="data"><object><property name="field"><string>value</string></property></object></var>'
Предварительный рендеринг JavaScript¶
На веб-страницах, которые получают данные из дополнительных запросов, воспроизведение тех запросов, которые содержат нужные данные, является предпочтительным подходом. Усилия часто окупаются: структурированные, полные данные с минимальным временем анализа и передачей по сети.
Однако иногда бывает очень сложно воспроизвести определённые запросы. Или вам может понадобиться что-то, что вам не может предоставить ни один запрос, например снимок экрана веб-страницы, отображаемый в веб-браузере.
В данных случаях используйте службу Splash JavaScript-рендеринга вместе с scrapy-splash для бесшовной интеграции.
Splash возвращает в формате HTML DOM веб-страницы, чтобы вы могли проанализировать его с помощью селекторы. Он обеспечивает большую гибкость за счет конфигурации или сценариев.
Если вам нужно что-то помимо того, что предлагает Splash, например, взаимодействие с DOM на лету из кода Python вместо использования ранее написанного сценария или обработка нескольких окон веб-браузера, вам может потребоваться использовать безголовый браузер вместо этого.
Использование безголового браузера¶
Безголовый браузер — это специальный веб-браузер, который предоставляет API для автоматизации. Установив asyncio реактор, можно интегрировать библиотеки на основе asyncio
, которые работают с автономными браузерами.
Одна из таких библиотек — playwright-python (официальный порт Python для playwright). Ниже приведён простой фрагмент, иллюстрирующий его использование в пауке Scrapy:
import scrapy
from playwright.async_api import async_playwright
class PlaywrightSpider(scrapy.Spider):
name = "playwright"
start_urls = ["data:,"] # avoid using the default Scrapy downloader
async def parse(self, response):
async with async_playwright() as pw:
browser = await pw.chromium.launch()
page = await browser.new_page()
await page.goto("https:/example.org")
title = await page.title()
return {"title": title}
Однако использование playwright-python напрямую, как в приведённом выше примере, позволяет обойти большинство компонентов Scrapy (промежуточное ПО, dupefilter и т. д.). Мы рекомендуем использовать scrapy-playwright для лучшей интеграции.