ジョブ制御: クロールの一時停止と再開

大きなサイトでは、クロールを一時停止して後で再開できることが望ましい場合があります。

Scrapyは、以下の機構を提供することにより、この機能をいち早くサポートしています:

  • スケジュールされたリクエストをディスクに保持するスケジューラー

  • 訪問したリクエストをディスク上に保持する重複フィルター

  • バッチ間でいくつかのスパイダーの状態(キー/値のペア)を保持する拡張機能

Jobディレクトリ

永続性サポートを有効にするには、 JOBDIR 設定で jobディレクトリ を定義するだけです。このディレクトリは、単一のジョブ(スパイダー実行など)の状態を維持するために必要なすべてのデータを保存するためのものです。このディレクトリは、単一ジョブ の状態を保存するために使用されるため、異なるスパイダー、または同じスパイダーの異なる job/run でさえ共有してはならないことに注意することが重要です。

使い方

永続性サポートを有効にしてスパイダーを起動するには、次のように実行します:

scrapy crawl somespider -s JOBDIR=crawls/somespider-1

その後、いつでも(Ctrl-Cを押すかシグナルを送信することにより)スパイダーを安全に停止し、同じコマンドを発行して後で再開できます:

scrapy crawl somespider -s JOBDIR=crawls/somespider-1

バッチ間で永続的な状態を維持する

時々、一時停止/再開バッチ間で永続的なスパイダー状態を維持したい場合があります。 それには spider.state 属性を使用できます。これは辞書である必要があります。スパイダーが開始および停止するときに、ジョブ・ディレクトリからの属性のシリアル化、保存、および読み込みを処理する組み込みの拡張機能があります。

スパイダー状態を使用するコールバックの例を次に示します(簡潔にするため、他のスパイダー・コードは省略されています):

def parse_item(self, response):
    # parse item here
    self.state['items_count'] = self.state.get('items_count', 0) + 1

永続性サポートあるある

Scrapy永続性サポートを使用できるようにしたい場合、留意すべきことがいくつかあります:

クッキーの有効期限

Cookieの有効期限が切れる場合があります。 そのため、スパイダーをすぐに再開しないと、スケジュールされたリクエストが機能しなくなる可能性があります。 クモがクッキーに依存していない場合、これは問題になりません。

リクエストのシリアル化

永続性が機能するためには、リクエストは pickle モジュールによってシリアル化可能でなければならないため、リクエストがシリアル化可能であることを確認する必要があります。

ここで最も一般的な問題は、永続化できないリクエスト・コールバックで lambda 関数を使用することです。

たとえば、これは機能しません:

def some_callback(self, response):
    somearg = 'test'
    return scrapy.Request('http://www.example.com',
                          callback=lambda r: self.other_callback(r, somearg))

def other_callback(self, response, somearg):
    print("the argument passed is: %s" % somearg)

しかし、これは動作します:

def some_callback(self, response):
    somearg = 'test'
    return scrapy.Request('http://www.example.com',
                          callback=self.other_callback, cb_kwargs={'somearg': somearg})

def other_callback(self, response, somearg):
    print("the argument passed is: %s" % somearg)

シリアル化できなかったリクエストをログに記録したい場合、プロジェクトの設定ページで SCHEDULER_DEBUG 設定を True に設定できます。デフォルトでは False です。