【问题标题】:Is there a way to force Scrapy into the parse method even if there is an unhandled exception?即使有未处理的异常,有没有办法强制 Scrapy 进入 parse 方法?
【发布时间】:2013-04-10 18:10:01
【问题描述】:

我有一个使用 Scrapy 的相当复杂的多处理程序。它在大约 95% 的时间里都能正常工作,但偶尔我会在 Twisted 遇到导致 DNSLookupError/TCPTimedOutError 的不良站点时收到未处理的异常。

这不是问题,但不幸的是,这些错误导致 Scrapy 跳过 BaseSpider 解析方法,我在其中设置了一个队列来处理响应。由于它跳过了队列,我无法确定哪些 URL 被跳过了。

有没有办法始终确保每个 Scrapy 请求都以该解析方法结束?我只需要一种方法来访问那些失败的响应对象并将它们放入队列中。

这是我的蜘蛛代码示例:

class SeerSpider(BaseSpider):
    """Scrapy-based html retrieval system."""

    name = "SeerSpider"

    def __init__(self, spider_queue, url_list):
        self.queue = spider_queue
        self.allowed_domains = []
        self.start_urls = []
        for url in url_list:
            self.allowed_domains.append(str(urlparse(url).netloc))
            self.start_urls.append(url)
        super(SeerSpider, self).__init__()

    def parse(self, response):
        """Extracts information from each website in start_urls."""
        self.queue.put(response)

如您所见,它非常基础。

稍后,队列是这样处理的:

while True:
    response = spider_queue.get()
    ### DO STUFF HERE ###
    results_queue.put(result)

然后……

while True:
    try:
        result = results_queue.get(True, 60)
    except:
        print 'HALP', sys.exc_info()
        result = ['ERROR']
    self.results.append(result)
    counter -= 1
    if counter <= 0 or self.exit == True:
        for process in process_list:
            process.terminate()
        break

我添加了队列超时作为临时解决方案,因此它不会在等待队列中不存在的项目时无限期挂起。如果我能保证某种响应对象会进入 start_urls 列表中每个 URL 的队列,它会解决我所有的问题。

谢谢

【问题讨论】:

  • 听起来像是scrapy中间件和process_spider_exception方法的工作,看看docs。希望对您有所帮助。
  • 不,我添加了一个自定义中间件,但没有处理错误。每次运行程序时我都会得到一堆这些,它会阻止队列完成:延迟中的未处理错误:未处理错误失败:twisted.internet.error.TCPTimedOutError:TCP 连接超时:10060:等等...等等...
  • 我想通了,中间件是正确的轨道,但它是下载器中间件而不是scrapy中间件。在我用 process_exception 方法实现了一个下载器中间件之后,我设法让它工作了。
  • 好消息!考虑为后代添加答案:)

标签: python queue multiprocessing twisted scrapy


【解决方案1】:

我想通了,中间件是正确的轨道,但它是下载器中间件而不是 scrapy 中间件。在我用 process_exception 方法实现了一个下载器中间件之后,我设法让它工作了。

代码在这里:

class SpiderFailSignal(object):

    def process_exception(self, request, exception, spider):
        response = Response(request.url, status=666, body='error')
        spider.queue.put(response)
        return response

然后我添加了

settings.overrides['DOWNLOADER_MIDDLEWARES'] = {'seerspider.SpiderFailSignal': 901}

它成功了。

虽然,一旦scrapy spider_idle 信号触发,我最终也向队列添加了一个虚拟值,然后创建了一个if 语句以在遇到该项目时退出队列。所以这两个解决方案一起 = 全部固定。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多