【问题标题】:how to process all kinds of exception in a scrapy project, in errback and callback?如何处理scrapy项目中的各种异常,在errback和回调中?
【发布时间】:2012-06-19 14:42:41
【问题描述】:

我目前正在做一个爬虫项目,这对于确保每个请求都得到正确处理非常重要,即记录错误或保存成功的结果。我已经实现了基本的爬虫,现在我可以成功处理 99% 的请求,但我可能会收到验证码、50x、30x 之类的错误,甚至结果中没有足够的字段(然后我会尝试另一个网站找到缺失的字段)。

起初,我认为在解析回调中引发异常并在 errback 中处理它们更“合乎逻辑”,这可以使代码更具可读性。但是我尝试了才发现 errback 只能捕获下载器模块中的错误,例如非 200 响应状态。如果我在回调中引发一个自行实现的 ParseError,蜘蛛只会引发它并停止。

即使我必须直接在回调中处理解析请求,我也不知道如何在回调中以干净的方式立即重试请求。你知道,我可能必须包含一个不同的代理来发送另一个请求,或者修改一些请求标头。

我承认我对scrapy比较陌生,但我已经来回尝试了好几天,仍然无法让它发挥作用……我已经检查了关于 SO 的每一个问题,但没有人匹配,在此先感谢您的帮助.

更新:我意识到这可能是一个非常复杂的问题,所以我尝试用下面的伪代码来说明这个场景,希望这会有所帮助:

from scraper.myexceptions import *

def parseRound1(self, response):

    .... some parsing routines ...
    if something wrong happened:
       # this causes the spider raises a SpiderException and stops
       raise CaptchaError
    ...

    if no enough fields scraped:
       raise ParseError(task, "no enough fields")
    else:
       return items

def parseRound2(self, response):
    ...some other parsing routines...

def errHandler(self, failure):
    # how to trap all the exceptions?
    r = failure.trap()
    # cannot trap ParseError here
    if r == CaptchaError:
       # how to enqueue the original request here?
       retry
    elif r == ParseError:
        if raised from parseRound1:
            new request for Round2
        else:
            some other retry mechanism
    elif r == HTTPError:
       ignore or retry

【问题讨论】:

    标签: python scrapy


    【解决方案1】:

    2012 年 11 月 16 日编辑:Scrapy >=0.16 使用不同的方法将方法附加到信号,添加了额外的示例

    最简单的解决方案是编写一个扩展程序,使用 Scrapy 信号在其中捕获故障。 例如;以下扩展将捕获所有错误并打印回溯。

    你可以对失败做任何事情——比如保存到你的数据库,或者发送一封电子邮件——这本身就是twisted.python.failure.Failure的一个实例。

    对于 0.16 之前的 Scrapy 版本:

    from scrapy import signals
    from scrapy.xlib.pydispatch import dispatcher
    
    class FailLogger(object):
      def __init__(self):
        """ 
        Attach appropriate handlers to the signals
        """
        dispatcher.connect(self.spider_error, signal=signals.spider_error)
    
      def spider_error(self, failure, response, spider):
        print "Error on {0}, traceback: {1}".format(response.url, failure.getTraceback())
    

    对于 0.16 及以上的 Scrapy 版本:

    from scrapy import signals
    
    class FailLogger(object):
    
      @classmethod
      def from_crawler(cls, crawler):
        ext = cls()
    
        crawler.signals.connect(ext.spider_error, signal=signals.spider_error)
    
        return ext
    
      def spider_error(self, failure, response, spider):
        print "Error on {0}, traceback: {1}".format(response.url, failure.getTraceback())  
    

    您可以在设置中启用扩展程序,例如:

    EXTENSIONS = {
    'spiders.extensions.faillog.FailLogger': 599,
    }
    

    【讨论】:

    • 感谢 Sjaak,我只是更仔细地查看了有关 scrapy 扩展的文档,这听起来很符合我的要求!太糟糕了,一周前我开始scrapy的时候还不太明白。稍后我会试一试,我正在考虑使用redis列表进行调度。
    • 不确定这是否需要另一个问题,但您能解释一下需要进行哪些修改才能将FailLogger 类合并到scrapy 的其余部分中吗?我修改了settings.py中的EXTENSIONS,并将faillog.py添加到extensions目录中。 Scrapy 输出2016-01-29 16:24:07 [scrapy] INFO: Enabled extensions: FailLogger,所以我认为我的实现还可以。我正在测试this link,它返回一个DNSLookupError,但是FailLogger 类中的打印语句没有被返回...
    【解决方案2】:

    起初,我认为在 解析回调并在errback中处理它们,这可能会使 代码更具可读性。但是我试了才发现errback只能trap 下载器模块中的错误,例如非 200 响应状态。如果 我在回调中提出了一个自我实现的 ParseError,蜘蛛只是 抬起它然后停止。

    是的,你是对的 - callbackerrback 仅用于下载器,因为 twisted 用于下载资源,而 twisted 使用延迟 - 这就是需要回调的原因。

    scrapy 中唯一的异步部分通常是下载器,所有其他部分同步工作。

    所以,如果您想捕获所有非下载器错误 - 请自己动手:

    • 在回调中尝试/除外
    • 或者为你的回调做一个装饰器来做这个(我更喜欢这个想法)

    【讨论】:

    • 感谢澄清callbackerr back的职责!这让我困惑了很长时间。我想我应该早点写一些测试蜘蛛来解决这个问题...
    猜你喜欢
    • 2021-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-01
    • 1970-01-01
    • 2012-02-18
    相关资源
    最近更新 更多