Захват кодов состояния http с помощью паука scrapy

Я новичок в скрапе. Я пишу паука, предназначенного для проверки длинного списка URL-адресов на наличие кодов состояния сервера и, при необходимости, на какие URL-адреса они перенаправляются. Важно, если есть цепочка редиректов, мне нужно знать код состояния и url при каждом переходе. Я использую response.meta['redirect_urls'] для захвата URL-адресов, но не знаю, как захватить коды состояния - похоже, для него нет мета-ключа ответа.

Я понимаю, что мне может понадобиться написать какое-то пользовательское промежуточное ПО для отображения этих значений, но я не совсем понимаю, как регистрировать коды состояния для каждого прыжка и как получить доступ к этим значениям из паука. Я посмотрел, но не могу найти пример того, как кто-то это делает. Если кто-нибудь может указать мне в правильном направлении, это было бы очень признательно.

Например,

    items = []
    item = RedirectItem()
    item['url'] = response.url
    item['redirected_urls'] = response.meta['redirect_urls']     
    item['status_codes'] = #????
    items.append(item)

Редактировать. Основываясь на отзывах от warawauk и активной помощи ребят на IRC-канале (freenode #scrappy), мне удалось это сделать. Я считаю, что это немного взломано, поэтому приветствуются любые комментарии по улучшению:

(1) Отключите промежуточное ПО по умолчанию в настройках и добавьте свое собственное:

DOWNLOADER_MIDDLEWARES = {
    'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware': None,
    'myproject.middlewares.CustomRedirectMiddleware': 100,
}

(2) Создайте свой CustomRedirectMiddleware в файле middlewares.py. Он наследуется от основного класса промежуточного программного обеспечения перенаправления и фиксирует перенаправление:

class CustomRedirectMiddleware(RedirectMiddleware):
    """Handle redirection of requests based on response status and meta-refresh html tag"""

    def process_response(self, request, response, spider):
        #Get the redirect status codes
        request.meta.setdefault('redirect_status', []).append(response.status)
        if 'dont_redirect' in request.meta:
            return response
        if request.method.upper() == 'HEAD':
            if response.status in [301, 302, 303, 307] and 'Location' in response.headers:
                redirected_url = urljoin(request.url, response.headers['location'])
                redirected = request.replace(url=redirected_url)

                return self._redirect(redirected, request, spider, response.status)
            else:
                return response

        if response.status in [302, 303] and 'Location' in response.headers:
            redirected_url = urljoin(request.url, response.headers['location'])
            redirected = self._redirect_request_using_get(request, redirected_url)
            return self._redirect(redirected, request, spider, response.status)

        if response.status in [301, 307] and 'Location' in response.headers:
            redirected_url = urljoin(request.url, response.headers['location'])
            redirected = request.replace(url=redirected_url)
            return self._redirect(redirected, request, spider, response.status)

        if isinstance(response, HtmlResponse):
            interval, url = get_meta_refresh(response)
            if url and interval < self.max_metarefresh_delay:
                redirected = self._redirect_request_using_get(request, url)
                return self._redirect(redirected, request, spider, 'meta refresh')


        return response

(3) Теперь вы можете получить доступ к списку перенаправлений в вашем пауке с помощью

request.meta['redirect_status']

person reportingmonkey    schedule 11.06.2012    source источник
comment
Вы должны опубликовать свое решение в качестве ответа   -  person raben    schedule 14.03.2013


Ответы (3)


Я считаю, что это доступно как

response.status

См. http://doc.scrapy.org/en/0.14/topics/request-response.html#scrapy.http.Response

person lindelof    schedule 11.06.2012
comment
Линделоф спасибо за ответ. Моя трудность заключается в том, что типичное использование response.status дает вам статус ответа окончательного ответа после всех перенаправлений. Мне нужен response.status для каждого прыжка, и я не понимаю, как захватить их все. Имеет ли это смысл? - person reportingmonkey; 11.06.2012
comment
Вы также можете добавить код состояния так же, как вы, по-видимому, заполняете ['redirect_urls'];) - person Sjaak Trekhaak; 11.06.2012
comment
О, я вижу, я неправильно понял. Тогда, я думаю, вам нужно создать подкласс scrapy.contrib.spidermiddleware.SpiderMiddleware в соответствии с doc.scrapy.org/en/0.14/topics/ и переопределите process_spider_input, чтобы добавить промежуточные коды состояния, скажем, к response.meta['status_codes'], который должен быть инициализирован как пустой список. Но я не пробовал это. - person lindelof; 11.06.2012
comment
Спасибо Сяак. Это идеальный вариант, однако я не заполняю request.meta['redirect_urls'] - он заполняется где-то по умолчанию промежуточным программным обеспечением. Это специальный ключ запроса. а>. В документации указано, что метаполе может содержать произвольные данные, но я не уверен, как его расширить, чтобы фиксировать response.status для каждого перенаправления. - person reportingmonkey; 11.06.2012
comment
Линделоф спасибо. Я слежу за вами и попробовал несколько вариантов. Моя проблема в том, что мне не удается создать экземпляр response.meta['status_codes'] по какой-то (вероятно, очень простой) причине. Я получаю исключения.KeyError: 'status_codes'. Не могли бы вы посоветовать, что добавить к приведенному ниже, чтобы решить эту проблему? класс RedirectMiddleware(объект): def process_spider_input(ответ, паук): response.meta['status_codes'].append(response.status) - person reportingmonkey; 11.06.2012
comment
Вероятно, вам нужно инициализировать запись словаря meta для ключа status_codes: if 'status_codes' in meta: meta['status_codes'].append(XXX) else: meta['status_codes'] = [XXXX] - person lindelof; 12.06.2012

response.meta['redirect_urls' заполняется RedirectMiddleware. Ваш обратный вызов паука никогда не будет получать промежуточные ответы, только последний после всех перенаправлений.

Если вы хотите контролировать процесс, создайте подкласс RedirectMiddleware, отключите исходный и включите свой. Затем вы сможете контролировать процесс перенаправления, в том числе отслеживать статусы ответов.

Вот исходная реализация (scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware):

class RedirectMiddleware(object):
    """Handle redirection of requests based on response status and meta-refresh html tag"""

    def _redirect(self, redirected, request, spider, reason):
        ...
            redirected.meta['redirect_urls'] = request.meta.get('redirect_urls', []) + \
                [request.url]

Как видите, метод _redirect, который вызывается из разных частей, создает meta['redirect_urls']

А в process_response вызывается метод return self._redirect(redirected, request, spider, response.status), что означает, что исходный ответ не передается пауку.

person warvariuc    schedule 12.06.2012
comment
Спасибо, Варварук, это имеет смысл. Я смотрел на промежуточное программное обеспечение перенаправления. Я думаю, что смогу реконструировать эту часть. Я думаю, что мне все еще что-то здесь не хватает, поскольку этот класс ссылается на request.meta.get['redirect_urls'], поэтому я думаю, что значения передаются для каждого запроса. Это тоже имеет смысл, но я не могу найти, где это на самом деле происходит. Я отредактирую свой исходный пост, чтобы узнать, могу ли я уточнить, где я борюсь. - person reportingmonkey; 13.06.2012
comment
@user1449163 user1449163, это промежуточное ПО создает meta['redirect_urls'] - см. обновление к ответу - person warvariuc; 13.06.2012

Решение KISS: я подумал, что лучше добавить строгий минимум кода для захвата нового поля перенаправления и позволить RedirectMiddleware сделать все остальное:

from scrapy.contrib.downloadermiddleware.redirect import RedirectMiddleware

class CustomRedirectMiddleware(RedirectMiddleware):
  """Handle redirection of requests based on response status and meta-refresh html tag"""

  def process_response(self, request, response, spider):
    #Get the redirect status codes
    request.meta.setdefault('redirect_status', []).append(response.status)
    response = super(CustomRedirectMiddleware, self).process_response(request, response, spider)
    return response

Затем, создав подкласс BaseSpider, вы можете получить доступ к redirect_status со следующим:

    def parse(self, response):
      item = ScrapyGoogleindexItem()
      item['redirections'] = response.meta.get('redirect_times', 0)
      item['redirect_status'] = response.meta['redirect_status']
      return item
person Antoine Brunel    schedule 21.03.2016