【问题标题】:Trigger errback when process_exception() is called in Middleware在中间件中调用 process_exception() 时触发 errback
【发布时间】:2019-11-13 15:42:00
【问题描述】:

使用 Scrapy,我正在实现一个 CrawlSpider,它将抓取各种网站,因此,有时非常慢的网站最终会产生超时。
我的问题是,如果发生这样的twisted.internet.error.TimeoutError,我想触发我的蜘蛛的 errback。我不想引发这个异常,我也不想返回一个可能表明抓取成功的虚拟响应对象。
请注意,我已经能够完成所有这些工作,但只能使用“肮脏”的解决方法

myspider.py(摘录)

class MySpider(CrawlSpider):
name = 'my-spider'

rules = (
    Rule(
        link_extractor=LinkExtractor(unique=True),
        callback='_my_callback', follow=True
    ),
)

def parse_start_url(self, response):
    # (...)

def errback(self, failure):
    log.warning('Failed scraping following link: {}'
        .format(failure.request.url))

middlewares.py(摘录)

from twisted.internet.error import DNSLookupError, TimeoutError

# (...)

class MyDownloaderMiddleware(object):

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        s = cls()
        crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
        return s

    def process_request(self, request, spider):
        return None

    def process_response(self, request, response, spider):
        return response

    def process_exception(self, request, exception, spider):

        if (isinstance(exception, TimeoutError)
            or (isinstance(exception, DNSLookupError))):
            # just 2 examples of errors i want to catch

            # set status=500 to enforce errback() call
            return Response(request.url, status=500)

我的自定义中间件已经启用,设置应该没问题。

现在您可以通过使用return Response(request.url, status=500) 看到,我可以根据需要在 MySpider 中触发我的errback() 函数。但是,状态代码 500 非常具有误导性,因为它不仅不正确,而且从技术上讲,我根本没有收到任何响应。

所以我的问题是,我怎样才能以干净的方式通过DownloaderMiddleware.process_exception() 触发我的errback() 函数?

编辑:我很快发现对于类似的异常,如DNSLookupError,我希望有相同的行为。我已经相应地更新了编码 sn-ps。

【问题讨论】:

    标签: scrapy scrapy-middleware


    【解决方案1】:

    Rule 类的__init__ 方法接受process_request 参数,您可以使用该参数将errback 附加到请求:

    class MySpider(CrawlSpider):
        name = 'my-spider'
    
        rules = (
            Rule(
                # …
                process_request='process_request',
            ),
        )
    
        def process_request(self, request, response):
            return request.replace(errback=self.errback)
    
        def errback(self, failure):
            pass
    

    【讨论】:

      【解决方案2】:

      我没有在文档中找到它,但查看源代码我发现了 DownloaderMiddleware.process_exception() can return twisted.python.failure.Failure objects 以及 Request 或 Response 对象。

      这意味着您可以通过将异常包装在 Failure 对象中来返回要由 errback 处理的 Failure 对象。

      这比创建一个假的 Response 对象更干净,请参阅此处执行此操作的示例中间件实现:https://github.com/miguelsimon/site2graph/blob/master/site2graph/middlewares.py

      核心思想:

      from twisted.python.failure import Failure
      
      class MyDownloaderMiddleware:
      
          def process_exception(self, request, exception, spider):
              return Failure(exception)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-09-30
        • 1970-01-01
        • 2013-05-31
        • 1970-01-01
        • 1970-01-01
        • 2020-01-11
        • 2012-10-16
        相关资源
        最近更新 更多