asyncio

版本 2.0 中的新功能。

Scrapy 部分支持 asyncio。在您 安装 asyncio 反应器 后,您可以在任何 协程 中使用 asyncioasyncio 支持的库。

安装 asyncio 反应器

要启用 asyncio 支持,请将 TWISTED_REACTOR 设置设置为 'twisted.internet.asyncioreactor.AsyncioSelectorReactor'

如果您正在使用 CrawlerRunner,您还需要手动安装 AsyncioSelectorReactor 反应器。您可以使用 install_reactor() 来完成此操作。

install_reactor('twisted.internet.asyncioreactor.AsyncioSelectorReactor')

处理预安装的反应器

twisted.internet.reactor 和一些其他 Twisted 导入会将默认的 Twisted 反应器作为副作用安装。一旦安装了 Twisted 反应器,就无法在运行时切换到其他反应器。

如果您 配置了 asyncio Twisted 反应器,并且在运行时 Scrapy 抱怨已安装了其他反应器,则可能是您的代码中有一些此类模块级 Twisted 导入。

您通常可以通过将这些有问题的模块级 Twisted 导入移动到使用它们的方法或函数定义中来解决此问题。例如,如果您有类似以下内容:

from twisted.internet import reactor


def my_function():
    reactor.callLater(...)

切换到类似以下内容:

def my_function():
    from twisted.internet import reactor

    reactor.callLater(...)

或者,您也可以尝试在这些导入发生之前,使用 install_reactor() 手动安装 asyncio 反应器

等待 Deferreds

当未安装 asyncio 反应器时,您可以在协程中直接等待 Deferreds。当它安装后,这将不再可能,因为 Scrapy 协程集成的细节(协程被包装到 asyncio.Future 对象中,而不是直接包装到 Deferred 中),您需要将它们包装到 Futures 中。Scrapy 为此提供了两个帮助程序

scrapy.utils.defer.deferred_to_future(d: Deferred[_T]) Future[_T][source]

版本 2.6.0 中的新功能。

返回一个 asyncio.Future 对象,该对象包装了 d

使用 asyncio 反应器 时,您不能从 定义为协程的 Scrapy 可调用对象 中等待 Deferred 对象,您只能等待 Future 对象。将 Deferred 对象包装到 Future 对象中允许您等待它们。

class MySpider(Spider):
    ...
    async def parse(self, response):
        additional_request = scrapy.Request('https://example.org/price')
        deferred = self.crawler.engine.download(additional_request)
        additional_response = await deferred_to_future(deferred)
scrapy.utils.defer.maybe_deferred_to_future(d: Deferred[_T]) Deferred[_T] | Future[_T][source]

版本 2.6.0 中的新功能。

返回 d 作为可以从 定义为协程的 Scrapy 可调用对象 中等待的对象。

您可以在定义为协程的 Scrapy 可调用对象中等待的内容取决于 TWISTED_REACTOR 的值。

如果您想编写使用 Deferred 对象但适用于任何反应器的代码,请对所有 Deferred 对象使用此函数。

class MySpider(Spider):
    ...
    async def parse(self, response):
        additional_request = scrapy.Request('https://example.org/price')
        deferred = self.crawler.engine.download(additional_request)
        additional_response = await maybe_deferred_to_future(deferred)

提示

如果您需要在旨在与较低版本的 Scrapy(不提供这些函数,直至 Scrapy 2.0,早期版本不支持 asyncio)兼容的代码中使用这些函数,您可以将这些函数的实现复制到您自己的代码中。

强制 asyncio 作为需求

如果您正在编写一个 组件,该组件需要 asyncio 才能工作,请使用 scrapy.utils.reactor.is_asyncio_reactor_installed()强制它作为需求。例如

from scrapy.utils.reactor import is_asyncio_reactor_installed


class MyComponent:
    def __init__(self):
        if not is_asyncio_reactor_installed():
            raise ValueError(
                f"{MyComponent.__qualname__} requires the asyncio Twisted "
                f"reactor. Make sure you have it configured in the "
                f"TWISTED_REACTOR setting. See the asyncio documentation "
                f"of Scrapy for more information."
            )

Windows 特定说明

Windows 的 asyncio 实现可以使用两种事件循环实现,ProactorEventLoop(默认)和 SelectorEventLoop。但是,只有 SelectorEventLoop 可与 Twisted 配合使用。

当您更改 TWISTED_REACTOR 设置或调用 install_reactor() 时,Scrapy 会自动将事件循环类更改为 SelectorEventLoop

注意

您使用的其他库可能需要 ProactorEventLoop,例如,因为它支持子进程(playwright 就是这种情况),因此您不能在 Windows 上将它们与 Scrapy 结合使用(但您应该能够在 WSL 或原生 Linux 上使用它们)。

使用自定义 asyncio 循环

您还可以将自定义的 asyncio 事件循环与 asyncio reactor 结合使用。将 ASYNCIO_EVENT_LOOP 设置设置为所需事件循环类的导入路径,以使用它代替默认的 asyncio 事件循环。