スパイダー規約(contract)

スパイダーのテストは特にウンコで、単体テストを書くのは楽チンだけど、テスト作業はマンドクサ。Scrapyは規約(contract)によってスパイダーを統一的にテストする方法を提供します。

これにより、サンプルURLをハード・コーディングしてスパイダーの各コールバックをテストし、コールバックがレスポンスを処理する方法のさまざまな制約を確認できます。各規約はdocstringに含め、各規約の先頭には @ が付けられています。次の例をご覧ください:

def parse(self, response):
    """ This function parses a sample response. Some contracts are mingled
    with this docstring.

    @url http://www.amazon.com/s?field-keywords=selfish+gene
    @returns items 1 16
    @returns requests 0 0
    @scrapes Title Author Year Price
    """

このコールバックは、3つの組み込み規約を使用してテストされます:

class scrapy.contracts.default.UrlContract[ソース]

この規約(@url)は、このスパイダーの他の規約条件をチェックするときに使用されるサンプルURLを設定します。 この規約は必須です。 チェックを実行する場合、この規約がないコールバックはすべて無視されます:

@url url
class scrapy.contracts.default.CallbackKeywordArgumentsContract[ソース]

この規約(@cb_kwargs)は、サンプル・リクエストの cb_kwargs 属性を設定します。 有効なJSON辞書である必要があります:

@cb_kwargs {"arg1": "value1", "arg2": "value2", ...}
class scrapy.contracts.default.ReturnsContract[ソース]

この規約(@returns)は、スパイダーによって返されるアイテムとリクエストの下限と上限を設定します。 上限はオプションです:

@returns item(s)|request(s) [min [max]]
class scrapy.contracts.default.ScrapesContract[ソース]

この規約(@scrapes)は、コールバックによって返されたすべてのアイテムに指定されたフィールドがあることを確認します:

@scrapes field_1 field_2 ...

check コマンドを使用して、規約チェックを実行します。

カスタム規約

うぬはチカラが欲しくないか? 組み込みScrapy規約よりも多くのチカラを。その場合は、 SPIDER_CONTRACTS 設定を使用して、プロジェクトに独自の規約を作成してロードできます:

SPIDER_CONTRACTS = {
    'myproject.contracts.ResponseCheck': 10,
    'myproject.contracts.ItemValidate': 10,
}

各規約は Contract から継承する必要があり、3つのメソッドをオーバーライドできます:

class scrapy.contracts.Contract(method, *args)[ソース]
パラメータ
  • method (collections.abc.Callable) -- 規約が関連付けられているコールバック関数

  • args (list) -- docstringに渡される引数のリスト(空白区切り)

adjust_request_args(args)[ソース]

これは、リクエスト・オブジェクトのデフォルト引数を含む引数として dict を受け取ります。 Request はデフォルトで使用されますが、これは request_cls 属性で変更できます。 チェーン内の複数の規約にこの属性が定義されている場合、最後の規約が使用されます。

同じまたは変更されたバージョンを返す必要があります。

pre_process(response)

これにより、コールバックに渡される前に、サンプル・リクエストから受信したレスポンスのさまざまなチェックをフックすることを許します。

post_process(output)

これにより、コールバックの出力を処理できます。 反復可能オブジェクトは、このフックに渡される前に変換されてリスト化されます。

期待どおりで無い場合、 pre_process または post_process から ContractFail 例外が送出されます。

class scrapy.exceptions.ContractFail[ソース]

規約が失敗した場合に発生するエラー

以下に、受信したレスポンスのカスタム・ヘッダーの存在を確認するデモ規約を示します:

from scrapy.contracts import Contract
from scrapy.exceptions import ContractFail

class HasHeaderContract(Contract):
    """ Demo contract which checks the presence of a custom header
        @has_header X-CustomHeader
    """

    name = 'has_header'

    def pre_process(self, response):
        for header in self.args:
            if header not in response.headers:
                raise ContractFail('X-CustomHeader not present')

scrapy check 実行の検出

scrapy check が実行されているとき、 SCRAPY_CHECK 環境変数を true 文字列に設定します。 scrapy check が使用されている場合、 os.environ を使用して、スパイダーや設定に変更を加えることができます:

import os
import scrapy

class ExampleSpider(scrapy.Spider):
    name = 'example'

    def __init__(self):
        if os.environ.get('SCRAPY_CHECK'):
            pass  # Do some scraper adjustments when a check is running