我有一个使用Scrapy的相当复杂的多处理程序。它大约95%的时间都可以正常工作,但偶尔会遇到来自Twisted的未处理异常,当它遇到导致DNSLookupError / TCPTimedOutError的坏站点时。
这不是一个问题,但不幸的是,这些错误导致Scrapy跳过BaseSpider解析方法,我已经设置了一个队列来处理响应。由于它会跳过队列,我无法确定跳过了哪些网址。
有没有办法始终确保每个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的Queue,它将解决我所有的问题。
由于
答案 0 :(得分: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语句,一旦遇到该项就退出队列。所以这两个解决方案在一起=全部固定。