在芹菜任务中运行Scrapy蜘蛛

时间:2014-03-01 15:46:43

标签: scrapy twisted celery

This is not working anymore,scrapy的API发生了变化。

现在,文档功能可以“Run Scrapy from a script”,但我收到ReactorNotRestartable错误。

我的任务:

from celery import Task

from twisted.internet import reactor

from scrapy.crawler import Crawler
from scrapy import log, signals
from scrapy.utils.project import get_project_settings

from .spiders import MySpider



class MyTask(Task):
    def run(self, *args, **kwargs):
        spider = MySpider
        settings = get_project_settings()
        crawler = Crawler(settings)
        crawler.signals.connect(reactor.stop, signal=signals.spider_closed)
        crawler.configure()
        crawler.crawl(spider)
        crawler.start()

        log.start()
        reactor.run()

4 个答案:

答案 0 :(得分:34)

扭曲的反应堆无法重新启动。解决这个问题的方法是让celery任务分叉为你想要执行的每个抓取创建一个新的子进程,如下面的帖子中所建议的那样:

Running Scrapy spiders in a Celery task

利用多处理包解决了“反应堆无法重启问题”的问题。但问题是现在使用最新的celery版本已经过时了解决方法,因为您将遇到另一个问题,即守护进程无法生成子进程。因此,为了使解决方法正常工作,您需要了解芹菜版本。

是的,scrapy API已经改变了。但稍作修改(导入Crawler而不是CrawlerProcess)。您可以通过芹菜版本来获得解决方法。

  

芹菜问题可以在这里找到:   Celery Issue #1709

通过使用台球而非多处理,这是我的更新的爬网脚本与更新的芹菜版本:

from scrapy.crawler import Crawler
from scrapy.conf import settings
from myspider import MySpider
from scrapy import log, project
from twisted.internet import reactor
from billiard import Process
from scrapy.utils.project import get_project_settings

class UrlCrawlerScript(Process):
    def __init__(self, spider):
        Process.__init__(self)
        settings = get_project_settings()
        self.crawler = Crawler(settings)
        self.crawler.configure()
        self.crawler.signals.connect(reactor.stop, signal=signals.spider_closed)
        self.spider = spider

    def run(self):
        self.crawler.crawl(self.spider)
        self.crawler.start()
        reactor.run()

def run_spider(url):
    spider = MySpider(url)
    crawler = UrlCrawlerScript(spider)
    crawler.start()
    crawler.join()
  

编辑:通过阅读芹菜问题#1709,他们建议使用台球而不是多处理,以便解除子流程限制。换句话说,我们应该尝试billiard并查看它是否有效!

     

编辑2:是的,使用billiard,我的脚本可以使用最新的芹菜构建!请参阅我更新的脚本。

答案 1 :(得分:10)

Twisted反应器无法重新启动,因此一旦一个蜘蛛完成运行并且crawler隐式停止反应堆,该工作人员就没用了。

正如在其他问题的答案中所述,您需要做的就是杀死运行蜘蛛的工人并用新的蜘蛛替换它,这可以防止反应堆被启动和停止多次。为此,只需设置:

CELERYD_MAX_TASKS_PER_CHILD = 1

缺点是你并没有真正使用 Twisted反应器充分发挥潜力并浪费运行多个反应堆的资源,因为一个反应堆可以在一个过程中同时运行多个蜘蛛。更好的方法是为每个工人(或者甚至是全球的一个反应堆)运行一个反应堆,并且不要让crawler触摸它。

我正在为一个非常类似的项目工作,所以如果我取得任何进展,我会更新这篇文章。

答案 2 :(得分:2)

为了避免在Celery任务队列中运行Scrapy时出现ReactorNotRestartable错误,我使用了线程。用于在一个应用程序中多次运行Twisted reactor的方法相同。 Scrapy也使用Twisted,所以我们也可以这样做。

以下是代码:

from threading import Thread
from scrapy.crawler import CrawlerProcess
import scrapy

class MySpider(scrapy.Spider):
    name = 'my_spider'


class MyCrawler:

    spider_settings = {}

    def run_crawler(self):

        process = CrawlerProcess(self.spider_settings)
        process.crawl(MySpider)
        Thread(target=process.start).start()

别忘了为芹菜增加CELERYD_CONCURRENCY。

CELERYD_CONCURRENCY = 10

对我来说很好。

这不是阻止进程运行,但无论如何scrapy最佳实践是在回调中处理数据。就这样做:

for crawler in process.crawlers:
    crawler.spider.save_result_callback = some_callback
    crawler.spider.save_result_callback_params = some_callback_params

Thread(target=process.start).start()

答案 3 :(得分:-2)

我想说如果要处理很多任务,这种方法效率非常低。 因为Celery是线程化的 - 在自己的线程中运行每个任务。 让我们以RabbitMQ作为经纪人说你可以通过> 10K q / s。 使用Celery,这可能会导致10K线程开销! 我建议不要在这里使用芹菜。而是直接访问经纪人!