如何在Scrapy / Twisted中使用线程,即如何在响应回调中异步调用阻塞代码?

Sak*_*Jun 3 python multithreading twisted scrapy

我需要在Scrapy中运行一些多线程\多处理工作(因为我有一些使用阻塞调用的库),并在完成后将Request放回Scrapy引擎。

我需要这样的东西:

def blocking_call(self, html):
    # ....
    # do some work in blocking call
    return Request(url)

def parse(self, response):
    return self.blocking_call(response.body)
Run Code Online (Sandbox Code Playgroud)

我该怎么做?我认为我应该使用扭曲反应堆和Deferred对象。但是Scrapy parse回调必须仅返回Noneor RequestBaseItemobject。

Rob*_*ujo 5

根据@ Jean-Paul Calderone的回答,我进行了一些调查和测试,这是我所发现的。

内部抓取使用Twisted框架来管理请求/响应同步和异步调用。

Scrapy 以异步方式生成请求(爬网),但是处理响应(我们的自定义解析回调函数)是同步完成的。因此,如果您在回调中阻止了调用,它将阻止整个引擎

希望可以更改。处理Deferred响应回调结果时,如果Deferred对象返回其他Deferred对象,则Twisted处理大小写(twisted.internet.defer.Deferred源)。在这种情况下,Twisted会产生新的异步调用。

基本上,如果我们从响应回调中返回Deferred对象,这会将响应回调调用的性质从sync更改为async。为此,我们可以使用deferToThread方法(内部调用 deferToThreadPool(reactor, reactor.getThreadPool()... -@ Jean-Paul Calderone代码示例中使用的方法)。

工作代码示例为:

from twisted.internet.threads import deferToThread
from twisted.internet import reactor

class SpiderWithBlocking(...):
    ...
    def parse(self, response):
        return deferToThread(reactor, self.blocking_call, response.body)

    def blocking_call(self, html):
        # ....
        # do some work in blocking call
        return Request(url)
Run Code Online (Sandbox Code Playgroud)

此外,只有回调可以返回Deferred对象,但start_requests不能(草率逻辑)。