Lzh on GitHub

版本 2.0 新增。

Scrapy 对 asyncio 有部分支持。在您安装 asyncio reactor 后,您可以在任何协程中使用 asyncio 和由 asyncio 驱动的库。

Installing the asyncio reactor (安装 asyncio reactor)

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

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

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

Handling a pre-installed reactor (处理预安装的 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

Integrating Deferred code and asyncio code (集成 Deferred 代码和 asyncio 代码)

协程函数可以通过将 Deferred 对象包装到 asyncio.Future 对象中来等待它们。Scrapy 为此提供了两个辅助函数:

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

版本 2.6.0 新增。

返回一个包装 dasyncio.Future 对象。

使用 asyncio reactor 时,您不能从定义为协程的 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 的值:

  • 使用 asyncio reactor 时,您只能等待 asyncio.Future 对象。
  • 当不使用 asyncio reactor 时,您只能等待 Deferred 对象。

如果您想编写使用 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)兼容的代码中使用这些函数,您可以将这些函数的实现复制到您自己的代码中。

协程和 Future 可以包装到 Deferred 中(例如,当 Scrapy API 需要向其传递 Deferred 时),使用以下辅助函数:

scrapy.utils.defer.deferred_from_coro(o: _CT) → Deferred[source]

scrapy.utils.defer.deferred_from_coro(o: _T) → _T

将协程或其他可等待对象转换为 Deferred,如果它不是协程,则按原样返回对象。

scrapy.utils.defer.deferred_f_from_coro_f(coro_f: Callable[_P, Coroutine[Any, Any, _T]]) → Callable[_P, Deferred[_T]][source]

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

协程函数将在包装器被调用时被调用。包装器参数将传递给它。这对于回调链很有用,因为回调函数是以前一个回调结果调用的。

Enforcing asyncio as a requirement (强制要求 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[source]

检查已安装的 reactor 是否为 AsyncioSelectorReactor

如果未安装 reactor,则引发 RuntimeError

版本 2.13 更改:在早期的 Scrapy 版本中,如果未安装 reactor,此函数会静默安装默认 reactor。现在它会引发异常以防止在这种情况下出现静默问题。

Windows-specific notes (Windows 特有注意事项)

asyncio 的 Windows 实现可以使用两种事件循环实现:ProactorEventLoop(默认)和 SelectorEventLoop。然而,只有 SelectorEventLoop 才能与 Twisted 一起工作。

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

注意

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

Using custom asyncio loops (使用自定义 asyncio 循环)

您还可以将自定义 asyncio 事件循环与 asyncio reactor 一起使用。将 ASYNCIO_EVENT_LOOP 设置为所需事件循环类的导入路径,以使用它而不是默认的 asyncio 事件循环。

Switching to a non-asyncio reactor (切换到非 asyncio reactor)

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