Я использую простую реализацию CrawlSpider для сканирования веб-сайтов. По умолчанию Scrapy следует 302 редиректам в целевые местоположения и игнорирует первоначально запрошенную ссылку. На определенном сайте я обнаружил страницу, которая 302 перенаправляет на другую страницу. Я хочу зарегистрировать как исходную ссылку (которая отвечает 302), так и целевое местоположение (указанное в заголовке ответа HTTP) и обработать их в методе parse_item для CrawlSpider. Пожалуйста, помогите мне, как мне этого добиться?

Я столкнулся с решениями, в которых упоминается использование dont_redirect=True или REDIRECT_ENABLE=False, но на самом деле я не хочу игнорировать перенаправления, на самом деле я хочу учитывать (т.е. не игнорировать) страницу перенаправления.

Например: я захожу в http://www.example.com/page1, который отправляет HTTP-ответ 302 редиректа и перенаправляет на http://www.example.com/page2. По умолчанию scrapy игнорирует page1, следует за page2 и обрабатывает его. Я хочу обработать как page1, так и page2 в parse_item.

ИЗМЕНИТЬ Я уже использую handle_httpstatus_list = [500, 404] в определении класса паука для обработки кодов ответов 500 и 404 в parse_item, но то же самое не работает для 302, если я укажите это в handle_httpstatus_list.

4
bawejakunal 11 Фев 2016 в 07:18

2 ответа

Лучший ответ

Scrapy 1.0.5 (последняя официальная версия на момент написания этих строк) не использует handle_httpstatus_list во встроенном RedirectMiddleware - см. эту проблему . Из Scrapy 1.1.0 (доступно 1.1.0rc1) проблема устранена.

Даже если вы отключите перенаправления, вы все равно можете имитировать его поведение в обратном вызове, проверяя заголовок Location и возвращая Request в перенаправление

Пример паука:

$ cat redirecttest.py
import scrapy


class RedirectTest(scrapy.Spider):

    name = "redirecttest"
    start_urls = [
        'http://httpbin.org/get',
        'https://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fip'
    ]
    handle_httpstatus_list = [302]

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(url, dont_filter=True, callback=self.parse_page)

    def parse_page(self, response):
        self.logger.debug("(parse_page) response: status=%d, URL=%s" % (response.status, response.url))
        if response.status in (302,) and 'Location' in response.headers:
            self.logger.debug("(parse_page) Location header: %r" % response.headers['Location'])
            yield scrapy.Request(
                response.urljoin(response.headers['Location']),
                callback=self.parse_page)

Журнал консоли:

$ scrapy runspider redirecttest.py -s REDIRECT_ENABLED=0
[scrapy] INFO: Scrapy 1.0.5 started (bot: scrapybot)
[scrapy] INFO: Optional features available: ssl, http11
[scrapy] INFO: Overridden settings: {'REDIRECT_ENABLED': '0'}
[scrapy] INFO: Enabled extensions: CloseSpider, TelnetConsole, LogStats, CoreStats, SpiderState
[scrapy] INFO: Enabled downloader middlewares: HttpAuthMiddleware, DownloadTimeoutMiddleware, UserAgentMiddleware, RetryMiddleware, DefaultHeadersMiddleware, MetaRefreshMiddleware, HttpCompressionMiddleware, CookiesMiddleware, ChunkedTransferMiddleware, DownloaderStats
[scrapy] INFO: Enabled spider middlewares: HttpErrorMiddleware, OffsiteMiddleware, RefererMiddleware, UrlLengthMiddleware, DepthMiddleware
[scrapy] INFO: Enabled item pipelines: 
[scrapy] INFO: Spider opened
[scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
[scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
[scrapy] DEBUG: Crawled (200) <GET http://httpbin.org/get> (referer: None)
[redirecttest] DEBUG: (parse_page) response: status=200, URL=http://httpbin.org/get
[scrapy] DEBUG: Crawled (302) <GET https://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fip> (referer: None)
[redirecttest] DEBUG: (parse_page) response: status=302, URL=https://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fip
[redirecttest] DEBUG: (parse_page) Location header: 'http://httpbin.org/ip'
[scrapy] DEBUG: Crawled (200) <GET http://httpbin.org/ip> (referer: https://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fip)
[redirecttest] DEBUG: (parse_page) response: status=200, URL=http://httpbin.org/ip
[scrapy] INFO: Closing spider (finished)

Обратите внимание, что вам понадобится http_handlestatus_list с 302 в нем, иначе вы увидите такой журнал (исходящий от HttpErrorMiddleware):

[scrapy] DEBUG: Crawled (302) <GET https://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fip> (referer: None)
[scrapy] DEBUG: Ignoring response <302 https://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fip>: HTTP status code is not handled or not allowed
9
paul trmbrth 11 Фев 2016 в 12:13

Промежуточное ПО перенаправления «поймает» ответ до того, как он достигнет вашего промежуточного ПО httperror, и запустит новый запрос с URL-адресом перенаправления. В то же время исходный ответ не возвращается, т.е. вы даже не «видите» коды 302, поскольку они не достигают httperror. Таким образом, наличие 302 в handle_httpstatus_list не имеет никакого эффекта.

Взгляните на его источник в scrapy.downloadermiddlewares.redirect.RedirectMiddleware: в process_response () вы видите, что происходит. Он запускает новый запрос и заменяет исходный URL на redirected_url. Нет «ответного ответа» -> исходный ответ просто отбрасывается.

По сути, вам просто нужно перезаписать функцию process_response (), добавив строку с «ответом на возврат» в дополнение к отправке другого запроса с redirected_url.

В parse_item вы, вероятно, захотите установить некоторые условные операторы, в зависимости от того, перенаправление это или нет? Я полагаю, он будет выглядеть не так, как обычно, поэтому, возможно, ваш предмет также будет выглядеть иначе. Другой вариант также может заключаться в использовании другого парсера для любого ответа (в зависимости от того, является ли исходный или перенаправленный URL-адрес «специальными страницами»), все, что вам тогда нужно, это иметь другую функцию синтаксического анализа, например, parse_redirected_urls (), в вашем пауке и вызвать эту функцию синтаксического анализа через обратный вызов в запросе перенаправления

1
Ruehri 11 Фев 2016 в 09:37