scrape-explore-site について
Webサイトを探索し、開始ページ、一覧ページ、詳細ページを保存するClaudeスキルです。リンク分類、ページのダウンロード、ナビゲーション値の生成を案内し、スクレイピングのワークフローを支援します。
主な機能
- 01範囲を指定したサイト探索
- 02一覧・詳細ページ数の設定
- 03リンクグループの分類
- 04参照元付きページ保存
- 05ナビゲーション値の生成
活用シーン
- 01サイトのカテゴリーページと詳細ページの収集
- 02カテゴリー配下に限定した探索
- 03スクレイピング仕様向けページの準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zytedata/ skills --skill scrape-explore-siteインストール後、Claude Code で/scrape-explore-siteを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
scripts/apply_classification.pydownload.pyextract_links.pyextract_nav_values.pyextract_values.py詳細
ウェブサイトを探索し、多様なページを見つけて保存します。開始 URL を指定すると、対象範囲内の開始ページ、一覧/カテゴリページ、詳細ページを見つけます。
${CLAUDE_SKILL_DIR}/../scrape/references/python-environments.md を読みます。
入力
生の引数文字列は $ARGUMENTS です。空白区切りの位置引数を最大 4 つに分割します。
- url:開始 URL。通常はホームページです
- project_path:出力を保存するパス
- DETAIL_COUNT:見つける詳細ページの数(デフォルト:3)
- LIST_COUNT:保存する一覧/カテゴリページの数(デフォルト:2)
開始ページは常に保存されます。
対象範囲
開始 URL を対象範囲のルートとして扱います。
- サイトのルート:カテゴリを広く探索します。
- カテゴリ/一覧ページ:その配下にとどまります。対象範囲内でページネーションや下位カテゴリをたどりますが、兄弟カテゴリや親カテゴリには範囲を広げません。
例:
/scrape-explore-site https://example.com .scrape/myspec
→ DETAIL_COUNT=3, LIST_COUNT=2
/scrape-explore-site https://example.com dataset/site-slug 8 4
→ DETAIL_COUNT=8, LIST_COUNT=4
ツール
download.py — ページを取得(HTTP + Playwright + スクリーンショット)
uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[...]
EOF
標準入力からダウンロードタスクの JSON 配列を読み込みます。
[
{"url": "...", "output_dir": "...", "page_type": "...",
"discovered_from": {"page": "...", "url": "..."}}
]
各タスクについて OUTPUT_DIR/{raw.html, rendered.html, screenshot.png, meta.json} を作成します。複数のタスクは並列でダウンロードされます。ログファイルは実行ごとに追記されます。
ページのダウンロードには WebFetch を絶対に使用しないでください。HTML 全体がコンテキストに読み込まれます。
screenshot.png ファイルは絶対に読み込まないでください。これらは後で使用するために保存されるものであり、探索には使用しません。分類にはリンクグループと HTML 構造を使用してください。
extract_links.py — リンクを抽出
uv run ${CLAUDE_SKILL_DIR}/scripts/extract_links.py PAGE.html [PAGE2.html ...] --base-url-from-meta --group --link-limit N
番号付きの JSON グループを出力します。
0: {"path": "article.product_pod > h3 > a", "count": 20, "links": [...]}
1: {"path": "ul.nav > li > a", "count": 50, "links": [...], "file": "list-1"}
出力サイズを制限するため、--link-limit には DETAIL_COUNT と LIST_COUNT のうち大きい方の値を渡す必要があります。
分類用に出力をファイルへ保存します。複数の入力ファイルを指定すると、グループには全体を通じた番号が付けられ、それぞれに "file" キーが含まれます。
このスクリプトの stderr は絶対に保存せず、stdout だけを保存してください。
apply_classification.py — 分類して links.json を書き込む
どのグループがどの種類に対応するかを決めたら、分類を適用します。
uv run ${CLAUDE_SKILL_DIR}/scripts/apply_classification.py GROUPS_FILE --classify "items=0 subCategories=1 nextPage=4" --pages-dir {pages_dir}
保存したグループファイルを読み込み、各ソースページのディレクトリに links.json を書き込みます。Write ツールを使う必要はありません。スクリプトが直接ファイルを書き込みます。
リンク抽出に使う HTML の選択
rendered.html を優先します(JavaScript によって描画されるサイトに対応しています)。rendered.html が存在しない場合は、raw.html を使用します。
リンクの分類
手順 1(グループの抽出)の後、番号付きの各グループを次のいずれかに分類します。
- items:詳細ページへのリンク。URL に固有の ID またはスラッグがあり、同じパスパターンのリンクが多数あります。
- subCategories:現在のページのトピックを絞り込む一覧/カテゴリページへのリンク。グループには 1 つのラベルを付けるため、すべての URL が該当する必要があります。このラベルを適用する前に、推論でグループ内の最後の URL に言及してください。グループ内に現在のページのトピックから外れる URL が 1 つでもある場合は、グループ全体を skip にします。
- nextPage:ページネーションリンク。URL またはテキストにページ番号が含まれているか、「next」/「previous」のテキストが使われています。
- skip:ナビゲーション、フッター、アカウント、ヘッダーのリンク、対象外のカテゴリリンクです(
--classifyには含めません)。
次の 3 つの手掛かりをすべて使用します。path(構造上の位置)、href(URL パターン)、text(アンカーテキスト)。
次に、apply_classification.py を --classify "items=0,3 subCategories=1 nextPage=2" とともに実行して、JSON ファイルを書き込みます。skip グループは classify 文字列から除外します。
バックリンク
保存する各ページ(開始ページを除く)では、タスクに discovered_from を含めてください。ダウンロード時に meta.json へバックリンクが直接書き込まれるため、別途読み書きする必要はありません。
出力構成
出力はすべて {pages_dir}({project_path}/pages/)の下に保存されます。
start-1/ — start page
raw.html rendered.html screenshot.png meta.json links.json
list-1/ — category/listing page (from start page)
list-2/ — sub-category page (from list-1, deeper navigation)
detail-1/ — item/article page
unknown-1/ — explored page that didn't fit list/detail (still kept)
...
各ページのディレクトリには raw.html、rendered.html、screenshot.png、meta.json、および(そのページのリンクを分類した場合は)links.json が含まれます。
ブロックされたサイトへの対応
raw.html(HTTP)とrendered.html(Playwright)の両方を確認してください。動作が異なる場合があります。両方でボット検出コンテンツ(CAPTCHA、「人間であることを確認してください」、403、Cloudflareのチャレンジ)が返された場合に限り、サイトが完全にブロックされていると判断してください。rawがブロックされていてもrenderedが機能する場合は、renderedを使って続行してください。問題ありません。
開始ページが完全にブロックされている場合(rawとrenderedの両方)、ただちに停止してください。ブロックされたことを報告し、概要に"blocked": trueを含めてください。回避策、再試行、ヘッダーの変更、代替URLの使用は試みないでください。代替案の提示は呼び出し元が行います。
開始ページは成功したものの、後続のページがブロックされた場合は、取得できたページの処理を続行してください。ブロックされたURLを再試行したり、ブロックの回避を試みたりしないでください。
いずれかのページでブロックが検出された場合、そのページのmeta.jsonに"blocked": true(HTTPのみがブロックされている場合は"blocked": "raw")を追加してください。
手順
1. 開始ページをダウンロードして分類する
ダウンロードします:
uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[{"url": "URL", "output_dir": "{pages_dir}/start-1", "page_type": "start"}]
EOF
リンクを抽出し、グループをファイルに保存します:
uv run ${CLAUDE_SKILL_DIR}/scripts/extract_links.py {pages_dir}/start-1/rendered.html --base-url-from-meta --group > {pages_dir}/start-1/groups.txt
グループを読み取り、分類を決めてから適用します:
uv run ${CLAUDE_SKILL_DIR}/scripts/apply_classification.py {pages_dir}/start-1/groups.txt --classify "items=N subCategories=N nextPage=N" --pages-dir {pages_dir}/start-1
2. リストページをダウンロードして分類する
開始ページのsubCategoriesリンクから、LIST_COUNT個のURLを選びます:
- 開始ページがサイトのルートの場合は、異なるカテゴリを優先します。
- すでにカテゴリ/リストページの場合は、そのサブツリー内にとどまります。子孫ページを優先し、兄弟カテゴリや親カテゴリには移動しないでください。
候補をすべて1回の呼び出しでダウンロードします:
uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[
{"url": "LIST_URL_1", "output_dir": "{pages_dir}/list-1", "page_type": "list", "discovered_from": {"page": "start-1", "url": "START_URL"}},
{"url": "LIST_URL_2", "output_dir": "{pages_dir}/list-2", "page_type": "list", "discovered_from": {"page": "start-1", "url": "START_URL"}}
]
EOF
すべてのリストページからリンクを一括で抽出し、グループを保存します:
uv run ${CLAUDE_SKILL_DIR}/scripts/extract_links.py {pages_dir}/list-*/rendered.html --group --base-url-from-meta > {pages_dir}/list-groups.txt
番号付きグループ(各グループには"file"キーがあり、値は"list-1"のようになります)を読み取り、分類してから適用します:
uv run ${CLAUDE_SKILL_DIR}/scripts/apply_classification.py {pages_dir}/list-groups.txt --classify "items=N,N subCategories=N,N nextPage=N" --pages-dir {pages_dir}
分類後、各ページのlinks.jsonを確認してください。リストページにitemsがない場合は、そのページのmeta.jsonに"warning": "no_items"を追加してください。リンクの分類に誤りがある可能性があります。
3. サブサブカテゴリをたどる(より深い階層への移動)
現在の対象範囲内で、さらに深い階層を指すsubCategoriesリンクがあるリストページを確認します。該当するリンクがあり、LIST_COUNTに達するためにさらにリストページが必要な場合は、次のようにします:
- 異なるサブサブカテゴリのURLを1~2個選びます
- ダウンロードして分類します
- これらは追加のリストページとなり、
discovered_fromに親リストページを参照する値が入ります
これにより、クロール範囲を広げずに階層を深掘りできます。LIST_COUNTをすでに満たしていても、対象範囲内で利用可能なサブカテゴリがあれば、1つはさらに深い階層までたどってください。
4. 詳細ページを選んでダウンロードする
開始ページとすべてのリストページから、itemsリンクをすべて収集します。候補の中から、異なるものをDETAIL_COUNT個選びます:
- URLのIDまたはスラッグが異なるもの
- 可能であれば、異なるカテゴリ/リストページから選ぶ
- 同じアイテムのバリエーション(例:色やサイズが異なるもの)は選ばない
各候補の取得元ページを記録します(バックリンク用)。
候補をすべて1回の呼び出しでダウンロードします:
uv run ${CLAUDE_SKILL_DIR}/scripts/download.py --log-file {pages_dir}/download.log <<'EOF'
[
{"url": "ITEM_URL_1", "output_dir": "{pages_dir}/detail-1", "page_type": "detail", "discovered_from": {"page": "list-1", "url": "LIST_URL_1"}},
{"url": "ITEM_URL_2", "output_dir": "{pages_dir}/detail-2", "page_type": "detail", "discovered_from": {"page": "list-2", "url": "LIST_URL_2"}}
]
EOF
5. 後処理
ダウンロードに失敗して空になったディレクトリを削除します。
残りのページに連番を付け直し、番号に欠番がないようにします:detail-1、detail-2など。
6. ナビゲーション値を生成する
リンクの分類後、links.jsonファイルからナビゲーション値を生成します:
uv run ${CLAUDE_SKILL_DIR}/scripts/extract_nav_values.py {project_path}
これは{pages_dir}/にある各ページのlinks.jsonとmeta.jsonを読み取り、{project_path}/values/にナビゲーション値を書き込みます。分類済みリンクがあるページのみ、valuesファイルが作成されます。
7. 概要を返す
Explored {domain}:
start: 1 page
list: N pages (target: LIST_COUNT)
detail: N pages (target: DETAIL_COUNT)
Pages saved to {pages_dir}/
目標数に達していない場合は、その理由を説明してください。
開始ページのリンク分類結果を返してください:アイテム数、サブカテゴリ数、URLの例をいくつか含めます。これは呼び出し元にとって役立ちます。