在Klein/Twisted框架中运行多个Scrapy爬虫的问题
解决Klein+Scrapy架构下多爬虫结果丢失问题
你遇到的核心问题是当前实现仅等待第一个爬虫完成就返回HTTP响应,没有等待所有启动的爬虫全部结束。当你多次调用runner.crawl()时,每个调用都会生成独立的Deferred,但你只绑定了第一个Deferred的回调逻辑,导致第一个爬虫结束就触发了响应,后续爬虫的结果自然无法被收集返回。
以下是具体的解决方案和规范纠正:
1. 重构TwistedRunner以支持多爬虫结果收集
首先需要让Runner能够跟踪所有爬虫的结果,而不是单个爬虫的。调整存储结构为全局累积列表,同时确保每个HTTP请求的Runner实例完全独立(避免并发请求时结果混淆):
import json import os from klein import Klein from scrapy import signals from scrapy.crawler import CrawlerRunner from twisted.web.server import Site Site.displayTracebacks = False class TwistedRunner(CrawlerRunner): def __init__(self, settings=None, crawler=None): super().__init__(settings, crawler) self.all_items = [] # 存储所有爬虫的爬取结果 def crawl(self, Spider, *args, **kwargs): # 创建爬虫实例并绑定结果收集信号 crawler = self.create_crawler(Spider) crawler.signals.connect(self.store_item, signals.item_scraped) return self._crawl(crawler, *args, **kwargs) def store_item(self, item): self.all_items.append(item) def format_results(self): # 将所有结果转换为JSON可序列化格式 return json.dumps([dict(item) for item in self.all_items])
2. 在路由中等待所有爬虫完成
在路由处理函数中,启动所有需要的爬虫后,必须调用runner.join()等待所有爬虫任务结束,再返回完整结果:
def getSpiderResult(output): """Format spider result""" return json.dumps([dict(item) for item in output]) class Router(object): app = Klein() scrapeArgument = os.getenv('argument', 'product').encode() @app.route('/<path:catchall>', methods=['POST', 'GET']) def catchAll(self, request, catchall): """catch-all route""" request.redirect('/') @app.route('/', methods=['GET', 'POST']) def scrape(self, request): """Serve request for scrape""" if self.scrapeArgument not in request.args: return None runner = TwistedRunner() product = request.args.get(self.scrapeArgument).pop() # 启动多个爬虫 runner.crawl(MySpiderCls, product=product) runner.crawl(MyOtherSpiderCls, product=product) # 第二个示例爬虫 # 等待所有爬虫任务完成 deferred = runner.join() # 所有任务完成后返回格式化结果 deferred.addCallback(lambda _: runner.format_results()) return deferred if __name__ == '__main__': Router = Router() Router.app.run(os.getenv('address', '0.0.0.0'), os.getenv('port', 8080))
3. 原实现的规范问题纠正
- 并发数据隔离:原代码中每个请求创建独立的
Runner实例是正确的,但单爬虫的结果存储逻辑不适用于多爬虫场景,现在改为全局累积列表后可以覆盖多爬虫需求。 - Scrapy API使用:原代码重写
crawl方法时的逻辑是合规的,但原实现只处理单个爬虫的回调,不符合多爬虫场景的需求,调整后更贴合Scrapy官方推荐的多爬虫运行方式。 - 调试便利性:
Site.displayTracebacks = False在生产环境是合理配置,但开发阶段建议暂时开启,方便排查爬虫运行中的异常。
修改完成后,HTTP请求会等待所有启动的爬虫完成后再返回完整结果,日志中所有save item记录都会出现在响应日志之前,不会再出现结果丢失的情况。
内容的提问来源于stack exchange,提问作者yusuke.yengkong
相关产品推荐
相关产品推荐

