asyncio

版本 2.0 新增。

Scrapy 对 asyncio 提供部分支持。在您安装 asyncio reactor 后,您可以在任何协程中使用 asyncio 以及依赖 asyncio 的库。

安装 asyncio reactor

要启用 asyncio 支持,您的 TWISTED_REACTOR 设置需要设置为 'twisted.internet.asyncioreactor.AsyncioSelectorReactor',这是默认值。

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

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

处理已预安装的 reactor

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

如果您配置了 asyncio Twisted 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()手动安装 asyncio reactor

整合 Deferred 代码和 asyncio 代码

协程函数可以通过将 Deferred 对象封装到 asyncio.Future 对象中来等待 Deferred 对象。Scrapy 提供了两个辅助函数用于此目的

scrapy.utils.defer.deferred_to_future(d: Deferred[_T]) Future[_T][源代码]

版本 2.6.0 新增。

返回一个封装了 dasyncio.Future 对象。

使用 asyncio reactor 时,您不能从定义为协程的 Scrapy 可调用对象中直接 await Deferred 对象,只能 await 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][源代码]

版本 2.6.0 新增。

d 作为可以在定义为协程的 Scrapy 可调用对象中 await 的对象返回。

在定义为协程的 Scrapy 可调用对象中可以 await 什么取决于 TWISTED_REACTOR 的值:

如果您想编写使用 Deferred 对象但适用于任何 reactor 的代码,请对所有 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)

提示

如果您不需要支持默认 AsyncioSelectorReactor 之外的 reactor,您可以使用 deferred_to_future(),否则您应该使用 maybe_deferred_to_future()

提示

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

可以使用以下辅助函数将协程和 futures 封装到 Deferreds 中(例如,当 Scrapy API 要求传递一个 Deferred 时):

scrapy.utils.defer.deferred_from_coro(o: _CT) Deferred[源代码]
scrapy.utils.defer.deferred_from_coro(o: _T) _T

将协程或其他 awaitable 对象转换为 Deferred,如果不是协程则原样返回。

scrapy.utils.defer.deferred_f_from_coro_f(coro_f: Callable[_P, Coroutine[Any, Any, _T]]) Callable[_P, Deferred[_T]][源代码]

将协程函数转换为返回 Deferred 的函数。

协程函数将在 wrapper 调用时被调用。wrapper 的参数将传递给它。这对于回调链很有用,因为回调函数会使用上一个回调的结果进行调用。

强制要求使用 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."
            )
scrapy.utils.reactor.is_asyncio_reactor_installed() bool[源代码]

检查已安装的 reactor 是否为 AsyncioSelectorReactor

如果未安装 reactor,则引发 RuntimeError

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 事件循环。

切换到非 asyncio reactor

如果由于某些原因您的代码无法与 asyncio reactor 一起工作,您可以通过将 TWISTED_REACTOR 设置为其导入路径(例如 'twisted.internet.epollreactor.EPollReactor')或设置为 None 来使用不同的 reactor,后者将使用您平台的默认 reactor。