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 新增。
返回一个封装了 d 的
asyncio.Future
对象。使用 asyncio reactor 时,您不能从定义为协程的 Scrapy 可调用对象中直接 await
Deferred
对象,只能 awaitFuture
对象。将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
的值:使用 asyncio reactor 时,您只能 await
asyncio.Future
对象。不使用 asyncio reactor 时,您只能 await
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
)的代码中使用这些函数,您可以将这些函数的实现复制到您自己的代码中。
可以使用以下辅助函数将协程和 futures 封装到 Deferreds 中(例如,当 Scrapy API 要求传递一个 Deferred 时):
强制要求使用 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。