Отладка пауков

В этом документе объясняются наиболее распространенные методы отладки пауков. Рассмотрим следующего ниже паука Scrapy:

import scrapy
from myproject.items import MyItem

class MySpider(scrapy.Spider):
    name = 'myspider'
    start_urls = (
        'http://example.com/page1',
        'http://example.com/page2',
        )

    def parse(self, response):
        # <processing code not shown>
        # collect `item_urls`
        for item_url in item_urls:
            yield scrapy.Request(item_url, self.parse_item)

    def parse_item(self, response):
        # <processing code not shown>
        item = MyItem()
        # populate `item` fields
        # and extract item_details_url
        yield scrapy.Request(item_details_url, self.parse_details, cb_kwargs={'item': item})

    def parse_details(self, response, item):
        # populate more `item` fields
        return item

По сути, это простой паук, который анализирует две страницы элементов (start_urls). Элементы также имеют страницу сведений с дополнительной информацией, поэтому мы используем функциональность cb_kwargs Request для передачи частично заполненного элемента.

Команда синтаксического анализа

Самый простой способ проверить вывод вашего паука — использовать команду parse. Это позволяет проверять поведение различных частей паука на уровне метода. Он имеет то преимущество, что он гибкий и простой в использовании, но не позволяет отлаживать код внутри метода.

Чтобы увидеть, как элемент был извлечен с определённого URL:

$ scrapy parse --spider=myspider -c parse_item -d 2 <item_url>
[ ... scrapy log lines crawling example.com spider ... ]

>>> STATUS DEPTH LEVEL 2 <<<
# Scraped Items  ------------------------------------------------------------
[{'url': <item_url>}]

# Requests  -----------------------------------------------------------------
[]

Использование опции --verbose или -v, мы можем видеть статус на каждом уровне глубины:

$ scrapy parse --spider=myspider -c parse_item -d 2 -v <item_url>
[ ... scrapy log lines crawling example.com spider ... ]

>>> DEPTH LEVEL: 1 <<<
# Scraped Items  ------------------------------------------------------------
[]

# Requests  -----------------------------------------------------------------
[<GET item_details_url>]


>>> DEPTH LEVEL: 2 <<<
# Scraped Items  ------------------------------------------------------------
[{'url': <item_url>}]

# Requests  -----------------------------------------------------------------
[]

Проверка элементов, извлеченных из одного start_url, также может быть легко выполнена с помощью:

$ scrapy parse --spider=myspider -d 3 'http://example.com/page1'

Scrapy Оболочка

Хотя команда parse очень полезна для проверки поведения паука, она мало помогает проверить, что происходит внутри обратного вызова, помимо отображения полученного ответа и вывода. Как отладить ситуацию, когда parse_details иногда не получает элемент?

К счастью, в данном случае shell — ваш хлеб с маслом (см. Вызов оболочки от пауков для проверки ответов):

from scrapy.shell import inspect_response

def parse_details(self, response, item=None):
    if item:
        # populate more `item` fields
        return item
    else:
        inspect_response(response, self)

См. также: Вызов оболочки от пауков для проверки ответов.

Открыть в браузере

Иногда вы просто хотите увидеть, как определенный ответ выглядит в браузере, для этого можно использовать функцию open_in_browser. Вот пример того, как вы могли бы его использовать:

from scrapy.utils.response import open_in_browser

def parse_details(self, response):
    if "item name" not in response.body:
        open_in_browser(response)

open_in_browser откроет браузер с ответом, полученным Scrapy в данный момент, настроив базовый тег так, чтобы изображения и стили отображались правильно.

Логирование

Ведение журнала — ещё один полезный вариант для получения информации о вашем работающем пауке. Хотя это не так удобно, но имеет то преимущество, что журналы будут доступны во всех будущих запусках, если они снова понадобятся:

def parse_details(self, response, item=None):
    if item:
        # populate more `item` fields
        return item
    else:
        self.logger.warning('No item received for %s', response.url)

Дополнительные сведения см. в разделе Логирование.