循环运行Scrapy Spider遇ReactorNotRestartable错误及挂起问题求助
解决Scrapy循环爬取触发ReactorNotRestartable的问题
为啥会出问题
Twisted的反应器(reactor)是单例模式,只能启动一次。你原来的代码在循环里每次都新建CrawlerProcess并调用process.start(),第一次启动后反应器就没法再重启了,直接抛ReactorNotRestartable错误。换成stop_after_crawl=False后,反应器一直保持运行,但后续的爬取没通过正确的异步方式调度,程序就直接挂住不动了。
靠谱的解决办法
办法1:用同一个反应器搞定所有爬取
把爬虫类移到循环外面,每次循环给爬虫传不同的参数(比如你的i值),然后用同一个CrawlerProcess把所有爬取任务都调度好,最后只启动一次反应器就行。
改完的代码示例:
import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from scrapy_splash import SplashRequest from abc import ABC # 爬虫类只定义一次,别放循环里 class MySpider(scrapy.Spider, ABC): name = "my_spider" start_urls = ["http://example.com"] def __init__(self, target_i, *args, **kwargs): super().__init__(*args, **kwargs) self.target_i = target_i # 接收循环传过来的i值 def start_requests(self): for url in self.start_urls: # 把你原来的dec判断换成用self.target_i的逻辑,这里先写个示例匹配 if self.target_i == self.target_i: # 替换成你实际的判断条件 yield SplashRequest(url=url, callback=self.parse_data, args={"wait": 1.5}) def parse_data(self, response): items = {} # 这里要初始化items,原代码没定义 data = response.css("td.right.data").extract() items["Data"] = data yield items if __name__ == "__main__": settings = get_project_settings() process = CrawlerProcess(settings) # 循环调度爬取任务 for i in range(0, 10): settings["FEED_URI"] = f"/../Data/data_{i}.json" # 把i传给爬虫实例 process.crawl(MySpider, target_i=i) # 只启动一次反应器,所有任务会挨个执行 process.start()
办法2:每次爬起开个新进程(简单粗暴但有效)
如果就想每次爬取都是独立的,可以用子进程来跑,这样每个子进程里的反应器都是全新的,不会有重启问题。
示例代码:
import subprocess import sys # 循环启动子进程执行爬取 for i in range(0, 10): subprocess.run([ sys.executable, __file__, str(i) ]) # 下面是单独的爬取逻辑,只有子进程会执行这段 if __name__ == "__main__": import scrapy from scrapy.crawler import CrawlerProcess from scrapy.utils.project import get_project_settings from scrapy_splash import SplashRequest from abc import ABC class MySpider(scrapy.Spider, ABC): name = "my_spider" start_urls = ["http://example.com"] def __init__(self, target_i, *args, **kwargs): super().__init__(*args, **kwargs) self.target_i = target_i def start_requests(self): for url in self.start_urls: if self.target_i == self.target_i: # 替换成你的实际判断逻辑 yield SplashRequest(url=url, callback=self.parse_data, args={"wait": 1.5}) def parse_data(self, response): items = {} data = response.css("td.right.data").extract() items["Data"] = data yield items # 拿到命令行传过来的i值 target_i = int(sys.argv[1]) settings = get_project_settings() settings["FEED_URI"] = f"/../Data/data_{target_i}.json" process = CrawlerProcess(settings) process.crawl(MySpider, target_i=target_i) process.start()
顺便提几个原代码的小问题
- 原代码里用了
self.urls,但只定义了start_urls,应该改成self.start_urls。 items变量没初始化,得在parse_data里先创建字典或者Scrapy的Item实例。dec变量没定义,记得换成你实际要判断的逻辑。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

