在Celery任务中运行Scrapy爬虫。

8 浏览
0 Comments

在Celery任务中运行Scrapy爬虫。

我有一个Django网站,当用户请求时会进行爬取操作,我的代码会在新的进程中启动一个独立的Scrapy脚本。当用户增加时,这种方法自然而然地无法正常运行。

类似这样:

class StandAloneSpider(Spider):
    #一个普通的爬虫
settings.overrides['LOG_ENABLED'] = True
#其他设置可以更改...
crawler = CrawlerProcess( settings )
crawler.install()
crawler.configure()
spider = StandAloneSpider()
crawler.crawl( spider )
crawler.start()

我决定使用Celery并使用worker来排队爬取请求。

然而,我遇到了Tornado反应器无法重新启动的问题。第一次和第二次爬虫运行成功,但后续的爬虫会抛出ReactorNotRestartable错误。

有谁能分享在Celery框架中运行爬虫的任何提示吗?

0
0 Comments

问题的原因是在Scrapy框架中,当使用Celery任务运行爬虫时出现了一些问题。具体问题是在每次爬虫运行后,worker守护进程会启动一个新的进程,这导致了反应器的问题。

解决方法是在设置文件中设置CELERYD_MAX_TASKS_PER_CHILD为1。这样做可以解决问题,并且每次爬虫运行后,worker守护进程都会重新启动一个新的进程来处理反应器。

这是一个聪明的方法,不确定使用Celery重新启动任务是否会带来多少开销。但是从逻辑上讲,这种方法是可行的。对于未来的用户,可以尝试这种解决方法。

最近的Celery版本已经更改了设置的名称。还可以使用命令行的方式应用这个设置,使用--max-tasks-per-child 1标志。

当前Celery版本4.3.0的设置名称是WORKER_MAX_TASKS_PER_CHILD。可以参考文档:docs.celeryproject.org/en/latest/userguide/configuration.htmldocs.celeryproject.org/en/latest/userguide/…

0
0 Comments

文章标题:在Celery任务中运行Scrapy爬虫的原因和解决方法

最近我在我的Django项目中使用Celery来对爬取任务进行排队,并成功地将Scrapy与之配合使用。这个解决方法主要来自于joehillen的代码,代码地址在这里http://snippets.scrapy.org/snippets/13/

首先是tasks.py文件:

from celery import task
def crawl_domain(domain_pk):
    from crawl import domain_crawl
    return domain_crawl(domain_pk)

然后是crawl.py文件:

from multiprocessing import Process
from scrapy.crawler import CrawlerProcess
from scrapy.conf import settings
from spider import DomainSpider
from models import Domain
class DomainCrawlerScript():
    def __init__(self):
        self.crawler = CrawlerProcess(settings)
        self.crawler.install()
        self.crawler.configure()
    
    def _crawl(self, domain_pk):
        domain = Domain.objects.get(
            pk=domain_pk,
        )
        urls = []
        for page in domain.pages.all():
            urls.append(page.url())
        self.crawler.crawl(DomainSpider(urls))
        self.crawler.start()
        self.crawler.stop()
    
    def crawl(self, domain_pk):
        p = Process(target=self._crawl, args=[domain_pk])
        p.start()
        p.join()
        
crawler = DomainCrawlerScript()
def domain_crawl(domain_pk):
    crawler.crawl(domain_pk)

这里的关键是“from multiprocessing import Process”,它解决了Twisted框架中的“ReactorNotRestartable”问题。因此,Celery任务调用“domain_crawl”函数,该函数一次又一次地重用“DomainCrawlerScript”对象与Scrapy爬虫进行交互。(我知道我的示例有点多余,但在我使用多个版本的Python进行设置时,我确实有这样的原因[我的Django Web服务器实际上使用的是Python2.4,我的工作服务器使用的是Python2.7])

在我这个示例中,“DomainSpider”只是一个修改过的Scrapy Spider,它接受一个url列表,并将它们设置为“start_urls”。

希望这对你有所帮助!

你使用Postgresql吗?我从Celery那里得到了最奇怪的"InterfaceError: connection already closed"错误。

是的,我使用Postgresql,如果你能提供错误信息,我可以尝试帮助你找出问题所在,我确实遇到了类似的错误,但我不记得具体是什么错误或者我是怎么解决的。(我目前正在将11,077,910个项目加载到我的队列中,我有5台工作机器从中获取数据,所以这个设置确实有效)

我找到了问题,与在postgresql中存储结果有关。我认为这是以下问题:github.com/celery/django-celery/issues/121,我正在使用的解决方法是设置CELERY_RESULT_BACKEND。你有遇到过这个问题吗?

不,我没有遇到这个问题。我目前的环境是服务器Python 2.6.6和工作机器Python 2.7,Celery 3.0.3,django-celery 3.0.1。

假设进程在完成后会停止,不需要手动停止吗?真的很优雅的方法,以后可能也会实现这个。因为我在Heroku上运行,可以使用任务队列。

伙计,我按照你的示例做了一遍,但是却从进程中得到了一个坏的文件描述符异常。你知道这是从哪里来的吗?

需要更多细节,我的系统已经很久没有碰过了,它已经安静地运行了近一年。我不记得以前是否遇到过类似的错误,也不记得我是怎么解决的。

我的工作机器运行在Python 2.7上,使用0.16.2的Scrapy,12.2.0的Twisted在一个OSX机器上。我已经按照你上面的内容尝试了一模一样,但是出现了以下异常:

[2013-04-04 23:35:38,838: WARNING/PoolWorker-1] Traceback (most recent call last): [2013-04-04 23:35:38,838: WARNING/PoolWorker-1] File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 249, in _bootstrap [2013-04-04 23:35:38,838: WARNING/PoolWorker-1] sys.stdin.close() [2013-04-04 23:35:38,838: WARNING/PoolWorker-1] IOError: [Errno 9] Bad file descriptor

是的,抱歉,我想你需要问一个比我更了解Twisted的人。我现在有点记得类似的错误,但不知道我是如何解决的。

没关系,我不知道这更像是一个Twisted的问题还是多进程的问题。

你能告诉我你是如何访问Scrapy打印到stdout的信息的吗?还有你是如何组织日志的?谢谢。

实际上,我不使用Scrapy打印到stdout的任何内容,相反我编写了自己的“pipelines”来收集和记录爬取数据。至于日志,我只是使用“django-sentry”来记录错误,这对于运行多个线程/进程的分布式爬虫非常方便。

这个答案已经过时,因为最新的Celery版本。你会遇到另一个问题,即无法在Celery工作进程中生成子进程。

是的,你可能是正确的,最新版本的Celery非常棒,所以当我重写这个项目以使用最新版本时,我会告诉你如何解决这个问题。

你能帮帮我吗?stackoverflow.com/questions/25353650/…

我也遇到了同样的问题。这个解决方案在Scrapy 1.0中还有效吗?你贴出的链接现在已经失效了。

0