Шаблонные правила, обратный вызов для allowed_domains и другой обратный вызов для запрещенных доменов

#python #scrapy

#python #scrapy

Вопрос:

В Scrapy как я могу использовать разные функции обратного вызова для разрешенных доменов и запрещенных доменов.

Я использую следующие правила:

 rules = [Rule(LinkExtractor(allow=(), deny_domains = allowed_domains), callback='parse_denied_item', follow=True),
Rule(LinkExtractor(allow_domains = allowed_domains), callback='parse_item', follow=True)]
  

В принципе, я хочу, parse_item чтобы меня вызывали всякий раз, когда есть запрос от allowed_domain (или поддомена одного из этих доменов). Затем я хочу, parse_denied_item чтобы меня вызывали для всех запросов, которые не занесены в белый список allowed_domains .

Как я могу это сделать?

Ответ №1:

Я считаю, что лучший подход — не использовать allowed_domains on LinkExtractor , а вместо этого разбирать домен из response.url в вашем parse_* методе и выполнять другую логику в зависимости от домена.

Вы можете сохранить отдельные parse_* методы и метод сортировки, который в зависимости от доменов вызывает yield from self.parse_*(response) (Python 3) соответствующий parse_* метод:

 rules = [Rule(LinkExtractor(), callback='parse_all', follow=True)]

def parse_all(self, response):
    # [Get domain out of response.url]
    if domain in allowed_domains:
        yield from self.parse_item(response)
    else:
        yield from self.parse_denied_item(response)
  

Комментарии:

1. Можно ли использовать process_request атрибут a Rule для захвата запроса до его выполнения? Затем фильтровать с этой точки? docs.scrapy.org/en/latest/topics /…

2. Действительно, я об этом не подумал. Пожалуйста, добавьте это как ответ на ваш собственный вопрос, я, например, проголосую за это.

Ответ №2:

Основано на ответе Gallaecio. Альтернативным вариантом является использование process_request of Rule . process_request перехватит запрос до его отправки.

Насколько я понимаю (что может быть неверно) Scrapy будет сканировать только домены, перечисленные в self.allowed_domains (при условии, что он используется). Однако, если на очищенной странице встречается удаленная ссылка, Scrapy в некоторых случаях отправит один запрос на эту удаленную ссылку [1]. Я не уверен, почему это происходит. Я думаю, это, возможно, происходит потому, что целевой сайт выполняет перенаправление 301 или 302, и поисковый робот автоматически следует по этому URL. В противном случае, это, вероятно, ошибка.

process_request может использоваться для выполнения обработки запроса перед его выполнением. В моем случае я хотел регистрировать все ссылки, которые не просматриваются. Итак, я проверяю, включен ли разрешенный домен request.url , прежде чем продолжить, и регистрирую любой из тех, которые не включены.

Вот пример:

 rules = [Rule(LinkExtractor(), callback='parse_item', process_request='process_item', follow=True)]

def process_item(self, request):
    found = False
    for url in self.allowed_domains:
        if url in request.url:
            #an allowed domain is in the request.url, proceed
            found = True

    if found == False: #otherwise log it
        self.logDeniedDomain(urlparse(request.url).netloc)

        # according to: https://docs.scrapy.org/en/latest/topics/spiders.html#scrapy.spiders.Rule
        # setting request to None should prevent this call from being executed (which is not the case for all)
        # middleware is used to catch these few requests
        request = None

    return request
  

[1]: Если вы столкнулись с этой проблемой, использование process_request промежуточного программного обеспечения in Downloader, по-видимому, решает ее.

Мое Downloader промежуточное программное обеспечение:

 def process_request(self, request, spider):
    #catch any requests that should be filtered, and ignore them
    found = False
    for url in spider.allowed_domains:
        if url in request.url:
            #an allowed domain is in the request.url, proceed
            found = True

    if found == False:
        print("[ignored] " request.url)
        raise IgnoreRequest('Offsite link, ignore')

    return None
  

Убедитесь, что вы также импортируете IgnoreRequest :

 from scrapy.exceptions import IgnoreRequest
  

и включите промежуточное программное обеспечение загрузчика в settings.py .

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