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()
答案 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线程开销! 我建议不要在这里使用芹菜。而是直接访问经纪人!