mcpField
S
scrape-explore-site

scrape-explore-site

github.com/zytedataデータ・データベース11175 回インストール2 日前に更新
リポジトリ

scrape-explore-site について

Webサイトを探索し、開始ページ、一覧ページ、詳細ページを保存するClaudeスキルです。リンク分類、ページのダウンロード、ナビゲーション値の生成を案内し、スクレイピングのワークフローを支援します。

主な機能

  1. 01範囲を指定したサイト探索
  2. 02一覧・詳細ページ数の設定
  3. 03リンクグループの分類
  4. 04参照元付きページ保存
  5. 05ナビゲーション値の生成

活用シーン

  1. 01サイトのカテゴリーページと詳細ページの収集
  2. 02カテゴリー配下に限定した探索
  3. 03スクレイピング仕様向けページの準備

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/zytedata/skills --skill scrape-explore-site

インストール後、Claude Code で/scrape-explore-siteを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

scripts/
apply_classification.py
download.py
extract_links.py
extract_nav_values.py
extract_values.py

詳細

ウェブサイトを探索し、多様なページを見つけて保存します。開始 URL を指定すると、対象範囲内の開始ページ、一覧/カテゴリページ、詳細ページを見つけます。

${CLAUDE_SKILL_DIR}/../scrape/references/python-environments.md を読みます。

入力

生の引数文字列は $ARGUMENTS です。空白区切りの位置引数を最大 4 つに分割します。

  1. url:開始 URL。通常はホームページです
  2. project_path:出力を保存するパス
  3. DETAIL_COUNT:見つける詳細ページの数(デフォルト:3)
  4. 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の例をいくつか含めます。これは呼び出し元にとって役立ちます。

コメント