Claude Media
Claude CodeでScrapyのスクレイパーを作る — robots.txt遵守とcontracts検証

Claude CodeでScrapyのスクレイパーを作る — robots.txt遵守とcontracts検証

ROBOTSTXT_OBEYとDOWNLOAD_DELAYをCLAUDE.mdの固定ルールにし、spider contractsとscrapy checkでspiderの出力を検証させる手順を、設定例つきで示します。

Claude CodeにScrapyのspiderを書かせるときは、アクセスの作法を決める設定(ROBOTSTXT_OBEYとDOWNLOAD_DELAY)をCLAUDE.mdに固定し、出来上がったspiderはspider contractsと scrapy check で検証させる、という二段構えが扱いやすい構成です。前者は「やってはいけないこと」を、後者は「spiderが返すべきもの」を決めます。

前提と進め方

この記事は、scrapy startproject で作ったプロジェクトを想定しています。Scrapyは2.19.0以上を前提にします。@method などのcontractとDOWNLOAD_DELAY_JITTERが2.19.0で加わった機能で、scrapy check がITEM_PIPELINESとFEEDSを無視する挙動は2.18.0からです。手元のバージョンは scrapy version で確かめられます。

Claude Codeの権限設定は、プロジェクト直下の .claude/settings.json に置きます。CLAUDE.mdもプロジェクト直下です。

手順

Scrapyのspiderを任せる流れ

  1. 1

    固定ルールを置く

    CLAUDE.mdに作法の方針を書く。

  2. 2

    spiderとcontractを同時に書かせる

    コールバックごとにdocstringへcontractを入れさせる。

  3. 3

    検査を実行させる

    pytestと scrapy check の結果から直させる。

  4. 4

    本番実行は人が判断する

    scrapy crawl は権限で確認制にしておく。

作法の設定は2つだけ先に決める

スクレイパーで事故になりやすいのは、抽出ロジックの誤りより、相手サイトへの負荷とrobots.txtの無視です。Claude Codeに任せる前に、次の2つを固定しておきます。

作法

CLAUDE.mdに固定する設定

  • ROBOTSTXT_OBEY

    Trueのまま変えない。spiderごとの上書きもしない。

  • DOWNLOAD_DELAY

    同じドメインへの連続リクエストの最小間隔。下げる提案が出たら理由を書かせる。

ROBOTSTXT_OBEYの既定値は2通りある

Scrapyの設定リファレンスでは、ROBOTSTXT_OBEYの既定値は True(フォールバックは False)と書かれています。補足として、既定値が False なのは歴史的な理由で、scrapy startproject が生成するsettings.pyでは有効になっている、とあります。

つまり、startproject で作ったプロジェクトなら最初から有効です。一方、設定ファイルを自前で組んだ小さな構成や、scrapy runspider で単体ファイルを動かす構成では、意図せず無効のままになる余地があります。Claude Codeが雛形から作り直すときも同じです。値を明示させておけば、この差を気にせずに済みます。

robots.txtの判定は、RobotsTxtMiddlewareが担います。有効にするには、このミドルウェアが有効で、かつROBOTSTXT_OBEYも有効である必要があります。照合に使うユーザーエージェントはROBOTSTXT_USER_AGENTで指定でき、None のときはリクエストのUser-Agentヘッダー、次にUSER_AGENTの順で使われます。

DOWNLOAD_DELAYは「最小の待ち時間」

DOWNLOAD_DELAYは、同じドメインへの連続する2リクエストの間に待つ最小秒数です。小数も指定できます。たとえば2.5を指定すると、10秒に最大4リクエストになります。

挙動で気をつける点が3つあります。

  • 2.19.0で追加されたDOWNLOAD_DELAY_JITTER(既定0.5)により、待ち時間は既定で±50%ばらつく
  • 相手の応答時間がDOWNLOAD_DELAYより短いと、そのドメインの実効並列数は1になる
  • スロットごとに変えたいときはDOWNLOAD_SLOTSを使う

Scrapyの「Avoiding getting banned」には、リクエストを2秒以上あけるためにDOWNLOAD_DELAYを使う案と、USER_AGENTに連絡先がわかる値を入れる案が並んでいます。後者は、相手が遮断する前に調整を求められるようにする狙いです。

CLAUDE.mdに書く固定ルール

CLAUDE.mdは強制される設定ではなく、Claudeが読むコンテキストです。公式は、検証できるくらい具体的に書くよう勧めています(「Test your changes」ではなく「Run npm test before committing」の形)。Scrapy向けに言い換えると、次のようになります。

## Scrapy の固定ルール
- settings.py の ROBOTSTXT_OBEY は True のまま変えない。
  spider 内の custom_settings でも上書きしない
- Request.meta に dont_obey_robotstxt を付けない
- DOWNLOAD_DELAY は 2 以上。下げる変更は理由をコメントに書く
- USER_AGENT は連絡先つきの値にする(ブラウザの偽装はしない)
- 新しい callback を足したら、docstring に @url / @returns / @scrapes を書く
- 変更後は `scrapy check` を実行し、結果を報告する
- 本番サイトへの `scrapy crawl` は、指示があるまで実行しない

dont_obey_robotstxt を名指しするのは、Request.metaにこのキーをTrueで入れたリクエストはRobotsTxtMiddlewareに無視される、と公式に書かれているからです。ROBOTSTXT_OBEYを守っていても、リクエスト単位の抜け道が残ります。

CLAUDE.mdの運用全般(置き場所や200行の目安など)はpytestの規約を教える記事と同じ考え方で整えられます。

contractsでspiderの出力を検証する

Scrapyは、spiderのコールバックをテストする仕組みとしてspider contractsを持ちます。コールバックのdocstringに @ で始まる行を書き、サンプルURLに対する結果を検査します。

def parse(self, response):
    """
    This function parses a sample response. Some contracts are mingled
    with this docstring.
 
    @url http://www.example.com/s?field-keywords=selfish+gene
    @returns items 1 16
    @returns requests 0 0
    @scrapes Title Author Year Price
    """

この例では、@url がサンプルURL、@returns がitemsとrequestsの下限・上限、@scrapes がitemが持つべきフィールドを指します。

組み込みcontractの使い分け

contract役割補足
@url役割サンプルURLを指定補足必須。無いコールバックは検査対象から外れる
@returns役割itemsまたはrequestsの件数の範囲補足上限は省略可。同じ値を2つ書くと厳密な件数になる
@scrapes役割全itemが持つべきフィールド補足欠けると失敗する
@cb_kwargs / @meta役割サンプルリクエストの引数補足値は有効なJSON辞書
@method / @body / @header / @cookie役割サンプルリクエストの組み立て補足いずれも2.19.0で追加

@url を書き忘れたコールバックは、エラーにならず「検査されない」点が落とし穴です。Claude Codeが新しいコールバックを足したのに、scrapy check が緑のまま、という状況が起こり得ます。CLAUDE.mdの「docstringに @urlを書く」は、この対策です。

scrapy checkの読み方

検査は scrapy check で実行します。-l で検査対象のコールバック一覧を出せます。contractsのページには、失敗時の出力例として次の形が載っています。

FAIL: [first_spider] parse (@returns post-hook)
...
scrapy.exceptions.ContractFail: Returned 92 requests, expected 0..4

「何件返って、何件を期待したか」が文面に出るので、Claude Codeに渡すと修正の方向を決めやすい出力です。実行はClaude Codeに任せ、失敗したら出力を読んで直させるループが回せます。

scrapy check -l
scrapy check myspider

2.18.0以降、scrapy check はITEM_PIPELINESとFEEDSを無視します。contractsはコールバックの出力を見るもので、itemの後処理や書き出しには回さないためです。パイプラインまで通して確かめたいときは、-s オプションで設定を戻します。

contractsでは守れないことを分ける

contractsが見るのは、サンプルURLに対するコールバックの戻り値です。ROBOTSTXT_OBEYやDOWNLOAD_DELAYといった設定の値そのものは検査しません。したがって、固定ルールの遵守は別の仕組みで担保します。

設定値は、pytestで読むのが素直です。以下は考え方を示す例で、プロジェクト名は仮のものです。

# tests/test_polite_settings.py
from scrapy.utils.project import get_project_settings
 
 
def test_robots_and_delay():
    s = get_project_settings()
    assert s.getbool("ROBOTSTXT_OBEY") is True
    assert s.getfloat("DOWNLOAD_DELAY") >= 2
    ua = s.get("USER_AGENT", "")
    # Scrapy の既定 UA にも http が含まれるため、自分の連絡先を直接検査する
    assert "scrapy.org" not in ua
    assert "contact@example.com" in ua  # 実際の連絡先に置き換える

これで「設定は守る」はテストで、「spiderの形は守る」はcontractsで、と役割が分かれます。CLAUDE.mdに「変更後は pytest と scrapy check を両方実行」と書けば、Claude Codeは2つの結果を見て作業を終えられます。

Claude Codeの権限で実行を絞る

CLAUDE.mdの指示は強制ではないので、本番サイトに触れる実行は権限の側でも絞れます。Claude Codeの権限ルールはdeny、ask、allowの順に評価され、最初に一致したものが結果になります。次の例は scrapy check を許可し、scrapy crawl は毎回確認を求める構成です。

{
  "permissions": {
    "allow": ["Bash(scrapy check *)", "Bash(pytest *)"],
    "ask": ["Bash(scrapy crawl *)", "Bash(scrapy runspider *)"]
  }
}

Bash(scrapy check *) のように、* はサブコマンドの後ろに置きます。権限設計の型は、Denoのdeno taskと--allowの記事も参考になります。

独自のcontractで「作法」まで検査する

組み込みで足りなければ、SPIDER_CONTRACTS設定に自作のcontractを登録できます。Contractを継承し、adjust_request_args、pre_process、post_process を上書きします。失敗はContractFailを送出して知らせます。

公式のデモは、レスポンスに特定のヘッダーがあるかを調べる @has_header です。この形を応用すれば、たとえば「サンプルレスポンスに想定のセレクタが無ければ失敗」といった検査も足せます。ただし、contractが増えるほど scrapy check が相手サイトへ送るリクエストも増える点には注意してください。

検査時だけ挙動を変えたい場合は、scrapy check の実行中に環境変数 SCRAPY_CHECK が文字列 true で設定される仕様を使えます。os.environ.get("SCRAPY_CHECK") で分岐します。

つまずきやすい点

  • scrapy check は実際のサンプルURLにリクエストを送る。相手サイトの規約や負荷を考えて、サンプルURLは最小限にする
  • ブラウザ描画が必要なサイトは、Scrapy単体では扱いにくい。その場合はFirecrawlのMCPやPlaywrightのE2Eテストを検討する

robots.txtは、相手サイトが自分のクローラーに出す指示です。自分のサイトからClaudeのクローラーを締め出す側の書き方は、ClaudeBotをブロックするrobots.txtにまとめています。

まとめ

人が介入する場所は、サンプルURLを決める段階と、scrapy crawl を初めて走らせる段階の2か所に絞れます。サンプルURLは、@returns の範囲を確かめられる最小の数にとどめるのが相手サイトへの負荷との釣り合いです。件数を増やすほど検査は厳密になりますが、scrapy check のたびに相手へ届くリクエストも増えます。

この記事を共有:XはてブLinkedIn