在Scrapy爬虫中使用线程

3

在Scrapy爬虫中是否可以使用多线程?比如说我已经创建了一个爬虫,它会爬取博客主题并保存其中所有的信息。我想让每个主题都与一个线程从池子里绑定,这个线程将爬取所有需要的信息。这样每个线程就可以爬取不同的主题了。

2个回答

12

这个标记的答案并不是100%正确。

Scrapy运行在Twisted上,支持从pipeline的 process_item方法中返回deferreds。

这意味着您可以在管道中创建一个deferred,例如使用threads.deferToThread。 这将在反应器线程池中运行您的CPU绑定代码。请注意,在适当的位置使用callFromThread。 我使用信号量来避免耗尽线程池中的所有线程,但为下面提到的设置设置良好的值也可能起作用。

http://twistedmatrix.com/documents/13.2.0/core/howto/threading.html

这里是我其中一个项目管道的方法:

def process_item(self, item, spider):
    def handle_error(item):
        raise DropItem("error processing %s", item)

    d = self.sem.run(threads.deferToThread, self.do_cpu_intense_work, item)
    d.addCallback(lambda _: item)
    d.addErrback(lambda _: handle_error(item))
    return d

你可能希望关注 REACTOR_THREADPOOL_MAXSIZE,如此处所述: http://doc.scrapy.org/en/latest/topics/settings.html#reactor-threadpool-maxsize

以及 CONCURRENT_ITEMS ,如此处所述: http://doc.scrapy.org/en/latest/topics/settings.html#concurrent-items

然而,你仍会面临Python GIL的问题,这意味着CPU密集型任务实际上不会在多个CPU上并行运行,它们只是假装在那样做。GIL仅针对IO释放。但你可以使用此方法在项目管道中使用IO阻塞的第三方库(例如webservice调用),而不会阻塞反应器线程。


2
仍然是一个很好的答案!谢谢。 - Mike
@GregorMelhorn:为什么需要使用DeferredSemaphore来“限制”Twisted线程池中的工作量?难道不是Scrapy的CONCURRENT_ITEMS设置来处理它吗? - abhinavkulkarni
@abhinavkulkarni,正如我在帖子中所写的那样:这些设置可能也适用,我使用信号量来确保我不会耗尽线程。无论如何,您需要使用deferToThread。 - Gregor Melhorn
@GregorMelhorn:在使用Twisted的DeferredSemaphore时,您如何跟踪Scrapy中使用的总资源?您会保留一个全局的吗?每个项目管道一个?还是每个爬虫一个? - abhinavkulkarni
我不记得了,这是6年前的事情 :) 但关于信号量VS并发项:假设信号量是必要的,因为如果您的线程比scrapy项花费更长的时间,那么运行的线程可能比管道中同时存在的项还要多得多。我不确定scrapy如何计算这个 - 最好再检查一下。 - Gregor Melhorn

6
Scrapy本身是单线程的,因此你不能在一个爬虫中使用多个线程。但是,你可以同时使用多个爬虫(CONCURRENT_REQUESTS),这可能会帮助你(查看常见实践)。
Scrapy不使用多线程,因为它是建立在Twisted上的异步http框架。

如果我在爬虫代码中创建了一个Python线程池,为什么它不能支持线程? - Eran
2
@Eran 因为爬虫代码的底层框架是异步的, - miradulo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接