2

我有一个使用 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 的队列,它将解决我所有的问题。

谢谢

4

1 回答 1

3

我想通了,中间件是正确的轨道,但它是下载器中间件而不是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语句以在遇到该项目时退出队列。所以这两个解决方案一起=全部固定。

于 2013-04-12T21:45:36.740 回答