【问题标题】:Capturing http status codes with scrapy spider使用爬虫抓取 http 状态码
【发布时间】:2012-06-14 11:52:15
【问题描述】:

我是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) 上的人提供的一些非常主动的帮助,我设法做到了这一点。我认为这有点 hacky,所以欢迎任何改进的 cmets:

(1) 在设置中禁用默认中间件,添加自己的:

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

(2) 在你的 middlewares.py 中创建你的 CustomRedirectMiddleware。它继承自主重定向中间件类并捕获重定向:

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']

【问题讨论】:

  • 您应该发布您的解决方案作为答案

标签: python web-scraping scrapy


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    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),意思是原始响应没有传递给蜘蛛。

    【讨论】:

    • 感谢warwaruk,这是有道理的。我在看重定向中间件。我想我可以对这部分进行逆向工程。我想我仍然在这里遗漏了一些东西,因为这个类引用了 request.meta.get['redirect_urls'] 所以我认为这些值正在为每个请求传递。这也有道理,但我找不到实际发生的地方。我将编辑我的原始帖子,看看我是否可以澄清我在哪里挣扎
    • @user1449163,这个中间件是创建 meta['redirect_urls'] 的中间件 - 查看答案的更新
    【解决方案3】:

    【讨论】:

    • 感谢您的回复林德洛夫。我的困难是 response.status 的典型用法在所有重定向之后为您提供最终响应的响应状态。我需要 response.status 每一跳,我不清楚如何捕获所有这些。这有意义吗?
    • 您也可以像填充 ['redirect_urls'] 一样附加状态码;)
    • 哦,我明白了,我误会了。然后我认为您需要根据doc.scrapy.org/en/0.14/topics/… 子类scrapy.contrib.spidermiddleware.SpiderMiddleware 并覆盖process_spider_input 以将中间状态代码附加到response.meta['status_codes'],它应该被初始化为一个空列表。但我没试过。
    • 谢谢 Sjaak。这是理想的,但是,我没有填充 request.meta['redirect_urls'] - 这是默认情况下填充的scrapy中间件。它是一个 request.meta 特殊键 -scrapy request-response docs。那里的文档指出元字段可以包含任意数据,但我不确定如何扩展它以捕获每个重定向的 response.status。
    • 谢谢林德洛夫。我正在关注您,并尝试了几种变体。我的问题是由于某些(可能非常简单)原因,我未能实例化 response.meta['status_codes'] 。我得到一个异常。KeyError:'status_codes'。您对在下面添加什么内容来解决这个问题有什么建议吗?类 RedirectMiddleware(object): def process_spider_input(response, spider): response.meta['status_codes'].append(response.status)
    猜你喜欢
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-02
    • 1970-01-01
    • 2020-11-09
    • 2018-09-11
    相关资源
    最近更新 更多