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导入移动到使用它们的方法或函数定义中来解决问题。例如,如果你有类似以下的内容:

from twisted.internet import reactor


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

切换到类似的内容:

def my_function():
    from twisted.internet import reactor

    reactor.callLater(...)

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

等待延迟对象

当没有安装asyncio反应器时,你可以直接在协程中等待Deferreds。当它被安装后,由于Scrapy协程集成的特殊性(协程被包装成asyncio.Future对象,而不是直接包装成Deferred),这就不再可能了,你需要将它们包装成Futures。Scrapy为此提供了两个辅助工具:

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

新版本2.6.0中新增。

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

使用asyncio反应器时,你不能等待 来自Deferred对象的定义为协程的Scrapy可调用对象,你只能等待 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 一起工作。

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

注意

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

使用自定义的asyncio循环

你也可以使用自定义的asyncio事件循环与asyncio反应器。设置ASYNCIO_EVENT_LOOP设置为所需事件循环类的导入路径,以使用它代替默认的asyncio事件循环。