web-dev-qa-db-ja.com

セロリタスクでスクレイピースパイダーを実行する

これはもう機能していません 、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()
31
Juan Riaza

ツイストリアクターは再起動できません。この問題の回避策は、次の投稿で提案されているように、実行する各クロールのセロリタスクに新しい子プロセスをフォークさせることです。

これは、multiprocessingパッケージを利用することで、「リアクターを再起動できない」問題を回避します。しかし、これの問題は、代わりにデーモンプロセスがサブプロセスを生成できないという別の問題が発生するため、最新のセロリバージョンでは回避策が廃止されていることです。したがって、回避策が機能するためには、セロリバージョンでダウンする必要があります。

はい、そしてscrapy AP​​Iが変更されました。しかし、小さな変更(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 を使用すると、私のスクリプトは最新のセロリビルドで動作します!更新されたスクリプトを参照してください。

36
Bj Blazkowicz

ツイステッドリアクターは再起動できないため、1つのスパイダーが実行を終了し、crawlerが暗黙的にリアクターを停止すると、そのワーカーは役に立たなくなります。

他の質問への回答に記載されているように、あなたがする必要があるのはあなたのスパイダーを走らせた労働者を殺し、それを新しいものと交換することだけです。これを行うには、次のように設定します。

CELERYD_MAX_TASKS_PER_CHILD = 1

1つのリアクターが単一のプロセスで一度に複数のスパイダーを実行できるため、欠点は実際にはsingツイステッドリアクターを最大限に活用してリソースを浪費し、複数のリアクターを実行していることです。より良いアプローチは、ワーカーごとに1つのリアクター(またはグローバルに1つのリアクター)を実行し、crawlerに触れさせないことです。

私は非常に類似したプロジェクトでこれに取り組んでいるので、何か進歩があったらこの投稿を更新します。

11
Blender

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()
2