如何配置scrapy CrawlerProcess与asyncio脚本在同一事件循环运行
解决方案
错误原因说明
- 第一次报错:
asyncio.run()执行完毕后会主动关闭当前线程的事件循环,后续Scrapy初始化AsyncioSelectorReactor时,找不到可用的活跃事件循环,因此抛出There is no current event loop in thread 'MainThread'错误。 - 第二次报错:
CrawlerProcess.start()方法默认会启动Twisted框架自己的事件循环,而你已经通过asyncio.run()启动了一个事件循环,同一个线程内不能同时运行两个事件循环,因此抛出This event loop is already running错误。
解决步骤
1. 启用Asyncio Reactor配置
首先取消你settings.py中对应配置的注释,确保Asyncio Reactor生效:
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
2. 替换为CrawlerRunner
不要使用CrawlerProcess,改用专门用于嵌入场景的CrawlerRunner,它不会主动创建和启动新的事件循环,可以直接复用当前已有的asyncio事件循环。
3. 编写并发运行代码
如果你使用的是Scrapy 2.6及以上版本,可以直接用以下最简写法,将两个协程放到同一个事件循环中运行即可:
import asyncio from scrapy.crawler import CrawlerRunner from scrapy.utils.project import get_project_settings # 导入你自己的爬虫类 from your_spider_file import TestSpider # 你的原有playwright协程 async def playwright_script(): # 此处保留你原有的playwright脚本逻辑 pass async def run_scrapy(): runner = CrawlerRunner(get_project_settings()) # 直接await爬取任务完成 await runner.crawl(TestSpider) async def main(): # 两个任务并发运行,如果需要先后执行,分别await即可 await asyncio.gather( playwright_script(), run_scrapy() ) if __name__ == "__main__": asyncio.run(main())
如果你使用的是更低版本的Scrapy,需要手动绑定reactor到asyncio循环,使用以下写法:
import asyncio import sys from scrapy.crawler import CrawlerRunner from scrapy.utils.project import get_project_settings from twisted.internet import asyncioreactor # 导入你自己的爬虫类 from your_spider_file import TestSpider # 提前将Twisted reactor绑定到当前asyncio事件循环 if 'twisted.internet.reactor' not in sys.modules: asyncioreactor.install(asyncio.get_event_loop()) from twisted.internet import reactor # 你的原有playwright协程 async def playwright_script(): # 此处保留你原有的playwright脚本逻辑 pass async def run_scrapy(): runner = CrawlerRunner(get_project_settings()) await runner.crawl(TestSpider) # 爬取完成后停止reactor reactor.stop() if __name__ == "__main__": loop = asyncio.get_event_loop() # 同时启动两个任务 loop.create_task(playwright_script()) reactor.callWhenRunning(lambda: loop.create_task(run_scrapy())) # 启动reactor阻塞运行 reactor.run()
注意事项
- 不要在已有asyncio事件循环运行的场景下调用
CrawlerProcess.start(),也不要在asyncio.run()执行完毕后再启动Scrapy,保证两个任务共享同一个事件循环。 - 如果需要两个任务先后执行,只需要调整
main函数中的await顺序即可,不需要额外修改配置。
内容的提问来源于stack exchange,提问作者fogx
相关产品推荐
相关产品推荐

