scrapy-extra について
scrapy-extraは、スパイダー、ページオブジェクト、アイテム、パイプライン、設定を実装・変更する開発者向けのScrapyスキルです。作業に応じた実装・テスト手順を示し、新規プロジェクトでは適切な場合にweb-poetのページオブジェクトを推奨します。
主な機能
- 01Scrapyの各コンポーネントの作成とデバッグ
- 02適切な新規スパイダーでweb-poetページオブジェクトを利用
- 03作業に応じたScrapyリファレンスを参照
- 04フィクスチャによるページオブジェクトのテスト
- 05件数を制限したクロールとアイテム確認
活用シーン
- 01Scrapyプロジェクトやスパイダーの作成
- 02既存クローラーの抽出処理の変更
- 03保存済みページのフィクスチャでページオブジェクトをテスト
- 04クロール出力の欠損フィールドや誤値の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zytedata/ skills --skill scrapy-extraインストール後、Claude Code で/scrapy-extraを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/project-template/cookiecutter.jsonassets/project-template/{{cookiecutter.project_name}}/pyproject.tomlscrapy.cfgassets/project-template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__init__.pyitems.pysettings.pyassets/project-template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/pages/__init__.pyassets/project-template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/spiders/__init__.pyassets/project-template/{{cookiecutter.project_name}}/fixtures/.gitkeepreferences/scrapy-poet.mdscrapy-zyte-api.mdscrapy.mdweb-poet.md詳細
このファイルは172行あります。すべて読んでください。
以下のSKILL_DIRは、このファイルを含むディレクトリの絶対パスを表します。
Scrapyのコーディングを実際に行います。スパイダー、ページオブジェクト、アイテム、パイプライン、設定、またはプロジェクト全体を作成・変更します。
新しいスパイダーでは、web-poetのページオブジェクトを優先してください。抽出処理を分離すると、スパイダーをクロールに集中させられ、保守、テスト、再利用も容易になります。どこまで導入するかはプロジェクトによって異なります。
- 新規プロジェクト、またはすでにscrapy-poetを設定済みのプロジェクト:特に指示がなければ、ページオブジェクトを使用します。
- スパイダーはあるものの、scrapy-poetを使用していない既存プロジェクト:ユーザーが導入を望まないと明言していない限り、scrapy-poetのサポート追加を提案し、勝手に導入することは避けます。代替策として、辞書やアイテムを返す通常のスパイダーを使用します。
依頼されていない限り、既存のスパイダーをページオブジェクトに移行したり、使用していないプロジェクトにweb-poetを追加したりしないでください。
Scrapyがすでに行う処理を再実装しないでください。重複リクエストは重複フィルターが自動的に除外するため、スパイダーに既処理URLの集合は不要です。
決まったワークフローも、必須の入力や出力もありません。タスクを読み、以下の関連リファレンスを確認して、慣用的なコードを書いてください。
リファレンス
SKILL_DIR/references/に含まれています。これから書く内容に応じて必要なものだけを読みます。多くのタスクで必要なのは1つです。
- スパイダー、アイテム、パイプライン、セレクター、フィードエクスポート、または設定の作成・デバッグ →
scrapy.md。 - ページオブジェクトの作成・デバッグ、またはフィクスチャの保存・実行 →
web-poet.md。 - ページオブジェクトまたはアイテムをパラメーターとして受け取るスパイダーコールバック(
handle_urls、DummyResponse、SCRAPY_POET_DISCOVER)の作成 →scrapy-poet.md。どちらを注入するかによってコールバックの形が決まります。 - サイトがリクエストをブロックする場合やJavaScriptが必要な場合 →
scrapy-zyte-api.md。
これ以外については、推測せず公式ドキュメントを取得してください。docs.scrapy.org、web-poet.readthedocs.io、scrapy-poet.readthedocs.io、scrapy-zyte-api.readthedocs.io、docs.zyte.comを参照してください。これらはLLM向けに配慮されています。URLの.htmlを.mdに置き換えるとMarkdownを取得でき、目次は<host>/llms.txtで確認できます。
環境
Pythonの実行にはすべてuvを使用してください。pythonやpipを直接実行してはいけません。uvがなければインストールしてください。
cd PROJECT_DIR && uv run scrapy crawl <name>
cd PROJECT_DIR && uv run --with pytest python -m pytest fixtures/
pytestコマンドを使うのは、プロジェクトにfixtures/ディレクトリがある場合です。ない場合は実行するものがありません。
.venv/、uv.lock、*.egg-info、build/は一時ファイルではなく、想定されているプロジェクトの状態です。そのまま残してください。実行時に生成される.pytest_cache/、__pycache__/、テストクロールのitems.jsonlも同様です。これらを探して削除しようとしないでください。
プロジェクトの作成
新規プロジェクトが必要なタスクの場合に限ります。同梱のテンプレートを使用してください。テンプレートにはscrapy-poetとZyte APIアドオンを設定済みのsettings.pyに加え、pages/、spiders/、fixtures/ディレクトリが含まれています。
uvx cookiecutter --no-input SKILL_DIR/assets/project-template \
-o PARENT_DIR project_name=PROJECT_NAME
cd PARENT_DIR/PROJECT_NAME && uv sync
既存のプロジェクトでは、そのプロジェクト内で作業してください。必要な依存関係がpyproject.tomlにない場合は、不足しているものをユーザーに伝え、追加する前に確認してください。黙ってプロジェクトを書き換えてはいけません。
抽出コードの記述
スパイダーのコールバックでも、ページオブジェクトの @field でも、既知の他のプロジェクトガイドラインと矛盾しない限り、デフォルトで次のガイドラインに従ってください。
- シンプルで、特定のドメインに依存しない方法にしてください。確認した特定のページに過剰適合させないでください(タイトルのハードコードや、ページごとの
if/elseは避けてください)。 - サンプルページが複数ある場合は、最初のページだけでなくすべてのページで抽出結果を確認してください。たまたま開いたページでしか機能しないセレクターは過剰適合です。ページ間で結果が異なる場合は、より汎用的なセレクターを優先するか、フォールバックを使ってください(例:まず JSON-LD を使い、次に CSS を使う)。
- データがない場合は
""やFalseではなく、Noneを返してください。 - 属性にアクセスする前にガードし、
Noneかどうかを確認してください。 - 裸の
Exceptionを捕捉しないでください。予期される特定の例外だけを捕捉してください。例外は、リスト項目を反復処理する際の項目ごとの抽出です。この場合は、1 つの項目のエラーで残りの処理が止まらないよう、各項目の処理をExceptionで囲んで捕捉し、exc_info=Trueを指定してログに記録してください(web-poet.md→ Nested item extraction を参照)。 - 決定論的な出力を優先してください(セットは避け、必要に応じてリストの重複を除去してください)。
- 構造化データがある場合は、それを優先してください。JSON-LD/microdata/OpenGraph には
extruct、JSON クエリにはjmespath、価格にはprice_parserを使ってください。 - データに JavaScript が必要な場合に限り、ブラウザーでのレンダリングを要求してください。ページオブジェクトを使う場合は、ページオブジェクトの入力としてその要件を宣言してください。つまり、
BrowserPageをサブクラス化してBrowserResponseに依存させてください。automap のパラメーターやcustom_settingsを通じてbrowserHtmlを有効にしないでください。ページオブジェクトを使わない場合は、必要なリクエストに限り、リクエストごとにmeta={"zyte_api_automap": {"browserHtml": True}}を指定してください(scrapy-zyte-api.mdを参照)。
抽出処理は抽出を行い、フィルタリングは行いません。 値による結果の除外や制限をユーザーから求められた場合でも、そのロジックをページオブジェクトや抽出メソッドに入れないでください。フィルタリング、重複除去、検証はスパイダーまたはアイテムパイプラインで行います。プロジェクトにまだどちらもない場合は、フィルターをページオブジェクトに移すのではなく、フィルター自体を追加しないでください。リクエストがこれに反する内容を示唆していた場合は、概要でその旨を伝えてください。
テスト
-
ページオブジェクト:セレクターを個別にテストするのではなく、
to_item()を実行して期待値と比較し、検証します。@fieldメソッド、Returns、出力処理によって、生のセレクター値が最終的なフィールドに変換されるためです。pytest で検証する web-poet のフィクスチャとして作成してください。実際のページオブジェクトを実行し、HttpResponseとBrowserResponseのどちらのページでも同じように動作し、回帰テストとして残ります。手元にある入力を使ってフィクスチャを作成します。- ライブ URL →
uv run scrapy savefixture <page.object.ClassPath> '<url>'。 - 保存済みのサンプル HTML のみ(URL なし)→ 各ファイルから入力を作成し、
web_poet.testing.Fixture.saveでフィクスチャを保存します(コード例はweb-poet.md→ Testing を参照してください。BrowserResponseについても説明しています)。
各
output.jsonに、手動で確認した期待値を設定します(savefixtureによる記録を修正するか、Fixture.saveにitem=として渡します)。その後、uv run --with pytest python -m pytest fixtures/を実行すると、保存済みのすべてのページでto_item()が再実行され、フィールドにずれがあれば失敗します。全体の手順についてはweb-poet.md→ Testing を参照してください。 - ライブ URL →
-
スパイダー:範囲を限定したテストクロールを実行し、アイテムを確認してください。終了コードが正常でも、それだけを信用しないでください。必ず
CLOSESPIDER_ITEMCOUNTまたはCLOSESPIDER_PAGECOUNTで範囲を限定します。抽出が機能しているか確認するには数件のアイテムで十分であり、スパイダーの検証でサイト全体をクロールする必要はありません。cd PROJECT_DIR && uv run scrapy crawl <name> -s CLOSESPIDER_ITEMCOUNT=5 -o items.jsonlitems.jsonlを確認します。フィールドが一貫してnullになっていたり、値がおかしかったりする場合、通常はセレクターが原因です。生の HTML が JavaScript のシェルだとわかった場合は、ブラウザーでレンダリングした入力(BrowserPage)に切り替え、フィクスチャを保存し直します。保存される入力は、記録時にページオブジェクトが依存していたものと一致します。根本原因が、依頼されたスパイダーではなくページオブジェクトにある場合でも、対応範囲内であれば修正してください。修正後はクロールを再実行し、items.jsonlをもう一度確認して、フィールドに値が設定されていることを確認します。終了コードが正常であることや終了時の統計情報ブロックは、修正が機能した証拠にはなりません。また、今回の実行結果で確認していないフィールド値を報告してはいけません。
あるフィールドが一貫して null のままなのに、その理由を説明せずスパイダーを完成とみなしてはいけません。修正するか、最終回答で修正しなかった理由(対応範囲外である、または実際にそのページにデータがない)を説明してください。どちらの場合も、該当するフィールド名を明記します。