Scrapy 如何切换 Twisted reactor(如 EPollReactor)并处理已安装其他 reactor 的冲突?
2026/9/15 16:03:15 网站建设 项目流程

Scrapy 如何切换 Twisted reactor(如 EPollReactor)并处理已安装其他 reactor 的冲突?

【免费下载链接】scrapyScrapy, a fast high-level web crawling & scraping framework for Python.项目地址: https://gitcode.com/GitHub_Trending/sc/scrapy

当 Scrapy 默认的 asyncio reactor 不适用于你的代码、或者默认 reactor 的某些限制(例如 macOS 大规模爬取时的文件描述符问题)影响运行时,需要把 Scrapy 切换到另一个 Twisted reactor,例如twisted.internet.epollreactor.EPollReactor。整个切换围绕TWISTED_REACTOR设置完成,但有一个关键约束:一旦某个 Twisted reactor 在进程中被安装,运行时就不可能再切换到别的 reactor。如果你的代码或其他库抢先安装了 reactor,Scrapy 启动时会直接报错。本文说明切换步骤、冲突的判定与修复方式。

reactor 的选择与安装机制

先弄清默认行为,才能判断该在哪里动手:

  • TWISTED_REACTOR的默认值是"twisted.internet.asyncioreactor.AsyncioSelectorReactor",即启用 asyncio 支持的 reactor(见 scrapy/settings/default_settings.py)。
  • 当调用scrapyCLI,或使用CrawlerProcess/AsyncCrawlerProcess时,如果此时还没有其他 reactor 被安装,Scrapy 会按TWISTED_REACTOR设置的值安装 reactor。
  • 使用CrawlerRunner/AsyncCrawlerRunner时,reactor 必须事先安装,需要你用 scrapy.utils.reactor.install_reactor 手动安装。如果已有 reactor 安装,install_reactor没有效果。
  • 一个进程只能使用一个 reactor,因此 reactor 相关设置不能按 spider 分别取不同值:只能使用第一个运行的 spider 的值,后续 spider 若要求不同的 reactor 或事件循环会抛出异常。
  • 运行需要CrawlerProcess的命令时,TWISTED_REACTOR同时充当 pre-crawler setting(项目级值决定使用哪个 crawler process 类),不能从 spider 的 per-spider settings 中设置。

以上机制说明来自 docs/topics/settings.rst 的 Reactor settings 与TWISTED_REACTOR章节。

切换到 EPollReactor 等 reactor 的操作步骤

最短主路径(通过 CLI 或CrawlerProcess运行):

  1. 在项目的settings.py中把TWISTED_REACTOR设为目标 reactor 的 import path:
# settings.py TWISTED_REACTOR = "twisted.internet.epollreactor.EPollReactor"

把它设为None则使用你平台的默认 reactor(见 docs/topics/asyncio.rst 的 “Switching to a non-asyncio reactor” 章节)。

  1. 如果当前代码使用AsyncCrawlerRunnerAsyncCrawlerProcess,需要同时切换到它们的 Deferred 版本CrawlerRunner/CrawlerProcess。注意AsyncCrawlerProcess在实例化时会固定安装AsyncioSelectorReactor,忽略TWISTED_REACTOR的值;之后若再传入不同的TWISTED_REACTOR值(例如通过 per-spider settings),会抛出异常。

  2. 如果你使用CrawlerRunner,在创建 crawler 前手动安装目标 reactor:

from scrapy.crawler import CrawlerRunner from scrapy.utils.reactor import install_reactor # 按 TWISTED_REACTOR 相同的 import path 安装目标 reactor install_reactor("twisted.internet.epollreactor.EPollReactor")

install_reactor(reactor_path)按指定 import path 安装 reactor;传None或不传则安装 asyncio reactor(见 scrapy/utils/reactor.py)。

  1. 正常启动:
scrapy crawl my_spider

一个切换的典型动机:docs/faq.rst 提到在 macOS 上运行大规模爬取、且默认 Twisted reactor 是twisted.internet.selectreactor.SelectReactor时,曾报告出现ValueError: filedescriptor out of range in select()。如果你之前已用TWISTED_REACTOR切换过 reactor,按同样的方式切换到另一个即可。

已安装其他 reactor 的冲突:现象与修复

现象:你已按上文配置了 reactor,但运行时 Scrapy 抱怨“已经安装了另一个 reactor”;AsyncCrawlerRunner等类在已安装的 reactor 与TWISTED_REACTOR设置不匹配时也会抛出异常。源码中的校验函数verify_installed_reactor会抛出形如The installed reactor (...) does not match the requested one (...)RuntimeError(见 scrapy/utils/reactor.py)。

原因twisted.internet.reactor以及其他一些 Twisted 导入会以副作用安装默认的 Twisted reactor。reactor 一旦安装,运行时不可能再切换。文档指出:如果你在代码里存在模块级(module-level)的 Twisted 导入,很可能就是冲突来源;项目文件和所导入的第三方库顶层的twisted.internet.reactor导入都会导致 Scrapy 在检查已安装 reactor 时抛异常。

修复方法一(文档推荐):把有问题的模块级 Twisted 导入移到实际使用它的方法或函数内部。文档给出的示例:

修改前:

from twisted.internet import reactor def my_function(): reactor.callLater(...)

修改后:

def my_function(): from twisted.internet import reactor reactor.callLater(...)

修复方法二:在这些导入发生之前,先用install_reactor手动安装你想要的 reactor。注意前提是“抢在导入之前”——如果此时 reactor 已经被安装,install_reactor没有效果,只能回到方法一处理导入顺序。

验证与判断

  • 切换后按正常方式启动爬取。如果抛出RuntimeError且信息是“已安装 reactor 与请求的不匹配”,说明有别的代码抢先安装了 reactor,回到上一节检查模块级导入。
  • 使用CrawlerRunner/AsyncCrawlerRunner时,reactor 必须事先安装,这两个类只把TWISTED_REACTOR设置用来校验已安装的 reactor 是否匹配;启动时通过校验、爬取正常进行,即说明 reactor 检查已通过。
  • 如果你在排查 macOS 上的filedescriptor out of range in select(),文档给出的判断路径就是:确认当前默认 reactor 是否为SelectReactor,然后按本文方式切换。

边界与限制

  • 同一进程内的多个 spider 不能要求不同 reactor:reactor 设置只取第一个运行 spider 的值,之后不同值会触发异常。
  • AsyncCrawlerProcess会忽略TWISTED_REACTOR,要切换 reactor 应使用CrawlerProcess
  • 如果你的目标不是“换一个 reactor”而是“完全不安装 reactor”,那是另一个机制:把TWISTED_REACTOR_ENABLED设为False(2.15.0 引入,当前为实验特性,可能不适合生产使用),此时TWISTED_REACTOR不生效。该模式会禁用 telnet console、FTP 下载器等功能,默认 HTTP(S) 下载器换成AiohttpDownloadHandler,详见 docs/topics/asyncio.rst 的 “Using Scrapy without a Twisted reactor” 章节。

相关文档:docs/topics/asyncio.rst、docs/topics/settings.rst、docs/faq.rst。

【免费下载链接】scrapyScrapy, a fast high-level web crawling & scraping framework for Python.项目地址: https://gitcode.com/GitHub_Trending/sc/scrapy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询