在Django中集成Scrapy爬虫与同步SDK,无法获取Scrapy爬虫结果的问题求助
问题分析与解决方案
首先,你的核心问题出在两个地方:多进程内存隔离和Twisted Reactor的不当处理,导致你无法从Scrapy爬虫中获取结果。让我一步步拆解并解决:
1. 核心问题根源
- 多进程内存隔离:你传递给子进程的普通
result列表,在子进程中是独立的内存拷贝——子进程里append的内容不会同步到父进程的列表中,所以父进程最终拿到的还是空列表。 - Twisted Reactor处理错误:你用
inlineCallbacks的方式没有正确等待爬虫完成,而且reactor.run()的停止逻辑不够严谨,可能导致爬虫还没完成就退出了。
2. 修复方案:使用进程安全的共享容器
我们可以用multiprocessing.Manager创建进程安全的共享列表,让子进程的修改能被父进程感知到。同时修正Scrapy爬虫和多进程的实现逻辑:
修改后的完整代码
1. 爬虫解析器类 (scrapyParser)
from multiprocessing import Process, Manager class scrapyParser(Parser): def __init__(self, keywords=None, n_items=None): super().__init__(keywords, n_items) def parse(self): if not super().parse(): return False # 使用Manager创建进程安全的共享列表 with Manager() as manager: shared_result = manager.list() crawler = UrlCrawlerScript(shared_result, [BASE_PATH + self.keywords]) crawler.start() crawler.join() # 等待子进程完全结束 # 转换为普通列表后返回前n条结果 return list(shared_result)[:self.n_items]
2. 多进程爬虫执行类 (UrlCrawlerScript)
from twisted.internet import reactor from scrapy.crawler import Crawler from scrapy import signals from scrapy.utils.project import get_project_settings class UrlCrawlerScript(Process): def __init__(self, shared_result, urls): super().__init__() self.shared_result = shared_result self.urls = urls self.settings = get_project_settings() self.spider_cls = Parser1 # 关联你的爬虫类 def run(self): # 创建Crawler实例 crawler = Crawler(self.spider_cls, self.settings) # 绑定信号:爬虫关闭时停止Reactor crawler.signals.connect(reactor.stop, signal=signals.spider_closed) # 启动爬虫,传入共享列表作为结果容器 crawler.crawl(self.spider_cls, outputResponse=self.shared_result, start_urls=self.urls) # 运行Reactor直到爬虫完成 reactor.run() # 确保Reactor彻底停止,避免资源泄漏 reactor.stop()
3. Scrapy爬虫类 (Parser1)
import scrapy class Parser1(scrapy.Spider): name = 'items' allowed_domains = ['domain.com'] # 初始化时接收共享列表和起始URL def __init__(self, outputResponse=None, start_urls=None, *args, **kwargs): super().__init__(*args, **kwargs) self.outputResponse = outputResponse self.start_urls = start_urls or [] def parse(self, response): # 你的页面解析逻辑(示例) row_data = response.css('your-target-selector') for item in row_data: scraped_info = { 'title': item.css('h2::text').get().strip(), 'url': item.css('a::attr(href)').get(), # 其他字段根据你的需求补充 } # 将结果写入共享列表 if self.outputResponse is not None: self.outputResponse.append(scraped_info)
3. 更简单的替代方案:避免多进程
如果你的场景不需要多进程(比如爬虫耗时不长),可以直接在主线程用CrawlerRunner运行Scrapy,彻底规避多进程共享的问题,更适合Django同步视图的场景:
from scrapy.crawler import CrawlerRunner from twisted.internet import reactor class scrapyParser(Parser): def __init__(self, keywords=None, n_items=None): super().__init__(keywords, n_items) def parse(self): if not super().parse(): return False result = [] runner = CrawlerRunner(get_project_settings()) # 定义爬虫完成后的回调:停止Reactor def on_finish(_): reactor.stop() # 启动爬虫 deferred = runner.crawl(Parser1, outputResponse=result, start_urls=[BASE_PATH + self.keywords]) deferred.addCallback(on_finish) # 运行Reactor直到爬虫结束 reactor.run() return result[:self.n_items]
4. 关键注意事项
- 进程安全容器:
Manager.list()是进程安全的,保证子进程的修改能被父进程读取;普通列表在多进程中是独立拷贝,无法共享状态。 - Reactor管理:无论用哪种方式,都要确保爬虫完成后正确停止Reactor,避免资源泄漏或程序挂起。
- Scrapy配置:确保
get_project_settings()能正确加载你的Scrapy项目配置(比如settings.py中的ROBOTSTXT_OBEY、USER_AGENT等设置)。
内容的提问来源于stack exchange,提问作者sphinks
相关产品推荐
相关产品推荐

