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运行):
- 在项目的
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” 章节)。
如果当前代码使用
AsyncCrawlerRunner或AsyncCrawlerProcess,需要同时切换到它们的 Deferred 版本CrawlerRunner/CrawlerProcess。注意AsyncCrawlerProcess在实例化时会固定安装AsyncioSelectorReactor,忽略TWISTED_REACTOR的值;之后若再传入不同的TWISTED_REACTOR值(例如通过 per-spider settings),会抛出异常。如果你使用
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)。
- 正常启动:
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),仅供参考