これはもう機能していません 、scrapyのAPIが変更されました。
ドキュメントには、「 スクリプトからScrapyを実行 」する方法が含まれていますが、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()
ツイストリアクターは再起動できません。この問題の回避策は、次の投稿で提案されているように、実行する各クロールのセロリタスクに新しい子プロセスをフォークさせることです。
これは、multiprocessing
パッケージを利用することで、「リアクターを再起動できない」問題を回避します。しかし、これの問題は、代わりにデーモンプロセスがサブプロセスを生成できないという別の問題が発生するため、最新のセロリバージョンでは回避策が廃止されていることです。したがって、回避策が機能するためには、セロリバージョンでダウンする必要があります。
はい、そしてscrapy
APIが変更されました。しかし、小さな変更(import Crawler
の代わりにCrawlerProcess
)。セロリバージョンでダウンして回避策を動作させることができます。
セロリの問題はここにあります: セロリの問題#1709
これが私の動作する更新されたクロールスクリプトで、billiard
の代わりにmultiprocessing
を使用することにより、新しいセロリバージョンを使用します。
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つのスパイダーが実行を終了し、crawler
が暗黙的にリアクターを停止すると、そのワーカーは役に立たなくなります。
他の質問への回答に記載されているように、あなたがする必要があるのはあなたのスパイダーを走らせた労働者を殺し、それを新しいものと交換することだけです。これを行うには、次のように設定します。
CELERYD_MAX_TASKS_PER_CHILD = 1
1つのリアクターが単一のプロセスで一度に複数のスパイダーを実行できるため、欠点は実際にはsingツイステッドリアクターを最大限に活用してリソースを浪費し、複数のリアクターを実行していることです。より良いアプローチは、ワーカーごとに1つのリアクター(またはグローバルに1つのリアクター)を実行し、crawler
に触れさせないことです。
私は非常に類似したプロジェクトでこれに取り組んでいるので、何か進歩があったらこの投稿を更新します。
Celery Tasks QueueでScrapyを実行するときにReactorNotRestartableエラーを回避するために、スレッドを使用しました。 1つのアプリで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
私にとってはうまくいきます。
これはプロセスの実行をブロックするものではありませんが、とにかく、コールバックでデータを処理するのが非常に厄介です。このようにしてください:
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()