mcpField
W
website-scraping

website-scraping

github.com/moonlight-lupinデータ・データベース113139 回インストール1 日前に更新
リポジトリ

website-scraping について

ウェブページを構造化された JSONL レコードに変換するワークフロースキルです。適切な抽出方法の選択、小規模で堅牢なスクレイパーの作成、既知の例との照合による検証を支援します。

主な機能

  1. 01軽量な抽出方法の選択
  2. 02レコード項目と出力形式の計画
  3. 03項目単位で処理する堅牢な抽出器
  4. 04状況に応じたボット対策
  5. 05生データの保存と出力検証

活用シーン

  1. 01URLから商品や掲載情報を収集
  2. 02ウェブページの表を構造化データに変換
  3. 03再実行可能なウェブデータ収集スクリプトの作成

MCP 対応

単発かつ低件数の作業では、利用可能なスクレイピング MCP サーバーが最も軽量な方法になる場合があると説明しています。

インストール

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

$npx skills add https://github.com/moonlight-lupin/agent-skills --skill website-scraping

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

同梱ファイル

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

.gitignore
README.md
evals/
evals.json
routing-fixtures.json
examples/agentic-browsing-quotes-demo/
explore.py
plan.md
README.md
examples/agentic-browsing-quotes-demo/final_runs/run_1/
final_script_log.txt
final_script.py
records.jsonl
references/
agentic-browsing.md
anti-bot.md
extraction.md
named-div-extraction.md
playwright.md
recon.md
scrapling.md
scripts/
recon.py

詳細

ウェブサイトのスクレイピング

ウェブサイトを構造化データに変換するワークフローです。動作する最も軽量なツールを優先します。静的 HTML → ページ内の JSON → リバースエンジニアリングした API → ブラウザー自動化の順です。調査を省略して、いきなり Playwright に頼るのは、何時間も無駄にする最もよくある原因です。

使用する場面

このスキルは、「URL または URL のリストから構造化レコードを作成する」という形式のあらゆるタスクに適用できます。例:

  • 競合他社の価格モニタリング(ホテル、航空券、小売、レンタル、学生向け住宅、SaaS)
  • E コマースの商品カタログ抽出
  • 不動産や求人情報の収集
  • レビュー、評価、空き状況カレンダー
  • 単発の「この表をスプレッドシートに取り込む」作業

対象外:データベースへの永続化、スケジューリング、ダッシュボード、固定スキーマに照らした下流での正規化。このスキルが作成するのは JSONL です。その後の処理は呼び出し側の責任です。

ワークフロー

次の手順に従ってください。ほとんどの失敗は手順 1 を省略することが原因です。

1. コードに触れる前に目標を明確にする

エディターを開く前に、答えを文書にまとめてください。

  • どのレコードが必要ですか? 何ごとに 1 行ですか? 商品ごとですか?(商品 × バリエーション × 日付)ごとですか?(ホテル × 客室タイプ × チェックイン日)ごとですか? 明確にしてください。多くのサイトでは、「リスティングカードごとに 1 行」か「価格バリエーションごとに 1 行」かを選ぶ必要があり、この選択はスクレイパー全体に影響します。
  • レコードごとにどのフィールドが必要ですか? 必須項目と、あると望ましい項目を分けてください。ソースがすべてのレコードで公開していない可能性のある項目には印を付けてください(欠落することがある項目は通常のことで、バグではありません)。
  • 入力形式:単一の URL ですか? URL のリストですか? 先に実行する必要がある検索クエリですか? クロール元となるシードのリスティングページですか?
  • 出力形式:このスキルでは、呼び出し側がキーを定義する JSONL(1 行に 1 つの JSON オブジェクト)と、付随するマニフェストファイルを推奨します。下記の「出力規約」を参照してください。
  • 規模と頻度:レコードはいくつで、どのくらいの頻度で処理しますか? この答えひとつで、その後のあらゆるコスト判断が決まります。100 ページを一度だけ収集するプロジェクトと、SKU 10,000 件を毎日更新するプロジェクトでは事情が異なります。前者には適切な time.sleep があれば十分ですが、後者ではプロキシやマネージドサービスのコストに見合うかを検討する必要があります(手順 4 の Tier 5 以降を参照)。一度きりのスクレイパーを作って対応しきれなくなってからではなく、今のうちに量と反復頻度の両方を明確にしてください。
  • 「正解」は何ですか? ブラウザーで最初から最後まで目視確認し、スクレイパーの結果が一致することを検証する具体的な項目を 1 つ選んでください。

これらのいずれかがユーザーから明確に示されていない場合は、質問してください。30 秒の確認で、形式の違うデータを 1 時間かけて抽出する事態を防げます。

2. サイトを調査する — 最も軽量なツールを選ぶ

以下の判断ツリーを作成する前に、簡単な確認を2つ行います。どちらかに該当すれば、スクレイパー自体が不要になる可能性があります。

2.0a — データはすでにツール呼び出し1回で取得できますか? Hermes の web_search と web_extract ツール、組み込みの検索機能、またはスクレイピング用 MCP サーバー(Bright Data、Firecrawl など)のように、フェッチ/スクレイピング/SERP 機能をすでに提供しているエージェント実行環境内で動作している場合があります。単発で少量の作業なら、すでに存在するツールを呼び出すのが最も軽量な方法です。コードを書くよりも手軽です。エディターを開く前に、利用可能なものを確認してください。(繰り返し実行する作業、バージョン管理が必要な作業、大量データを扱う作業では、引き続きスクリプトを作成してください。再実行、差分確認、引き継ぎができるものが必要です。)

2.0b — 対象は対策の厳重な巨大プラットフォームですか? Amazon、LinkedIn、Instagram、TikTok、Facebook、YouTube、Zillow、Google Maps、Crunchbase など一部のサイトは、自作スクレイピングを阻止するために多大な投資をしており、内部構造も頻繁に変更します。こうしたサイトでは、スクレイパーを自作しても費用対効果が低いことがよくあります。1週間は動いても、その後壊れてしまいます。ここで最も軽量なツールは、urllib とは限りません。プラットフォームごとのデータ抽出をすでに維持している、商用の構造化データ製品(Bright Data のデータセット/Web-Scraper API、Apify のアクターなど)かもしれません。このトレードオフをユーザーに伝えてください(「自作もできますが、$SITE については、保守されたデータ API のほうが信頼性が高く、保守にかかる費用もおそらく安くなります。どちらにするかはお任せします」)。脆弱な自作スクレイパーを黙々と作り続けないでください。それでも自作を希望する場合は、先に進めてかまいません。ただし、ステップ4のボット対策の段階を想定しておいてください。

どちらの近道も当てはまらない場合は、通常どおり調査します。DevTools を開いた実際のブラウザーで対象 URL を開きます。この判断ツリーを上から順に確認し、最初に該当した時点で止めてください。早く止めるほど、スクレイパーはシンプルで堅牢になります。

Is the data you need visible in "View Source" (right-click → View Page Source)?
├─ YES → Static HTML. Use urllib / requests + an HTML parser. Done.
│
└─ NO → Is there a <script type="application/ld+json"> block with what you need?
    │
    ├─ YES → JSON-LD. Universal schema (schema.org), present on most modern
    │        sites for SEO. Often has product, price, address, rating, etc.
    │        Parse the JSON block directly. No JS execution needed.
    │
    └─ NO → Is there a JSON blob in a <script id="__NEXT_DATA__">, __NUXT__,
            drupal-settings-json, window.__INITIAL_STATE__, or similar?
        │
        ├─ YES → Framework JSON-in-page. Pull the blob, json.loads it, navigate
        │        to the fields you want. Still no browser needed.
        │
        └─ NO → Open DevTools Network tab. Reload. Filter to XHR/Fetch.
                Does a clear JSON endpoint return the data?
            │
            ├─ YES → Reverse the API. Copy as cURL, port to Python.
            │        Often the cleanest path — same data the site uses, no scraping
            │        of HTML at all. Watch for auth tokens / CSRF / rate limits.
            │
            └─ NO → It's client-rendered. Browser automation required.
                    Use Playwright. See references/playwright.md.

厳守事項:Playwright を使うのは、それより前の方法が実際に機能しない場合に限ります。Playwright は urllib と比べて10〜50倍遅く、メモリ消費量は10倍、壊れやすさも10倍です。サイトの HTML 構造は数か月ごとに変更されますが、JSON-LD やバックエンドの API はそれほど頻繁には変更されません。

URL にアクセスして、どの方法が使えそうかを報告する自動調査ヘルパーについては、scripts/recon.py を参照してください。新しいサイトでは、最初にこれを実行してください。

3. 可能な限り最小の抽出処理を作る

カタログ全体ではなく、まずは1件から始めます。既知の対象を1つ使ってフィールド抽出を正確に行い、それからリスト全体に一般化します。理由は2つあります。

  1. フィールド抽出のバグは、200件の中に埋もれているより、1件だけで確認するほうが見つけやすくなります。
  2. パーサーが正しく動くまでに5~10回ほど試行錯誤することになります。サイト全体を対象にこれを繰り返すと、レート制限の予算を消費し、サイトのWAFにあなたのフィンガープリントを学習させてしまいます。

内側のループのコード例:

def extract_one(html_or_json) -> dict | None:
    # one item -> one dict; None if this item is unparseable.
    ...

def extract_many(items_iter) -> list[dict]:
    out, seen = [], set()
    for item in items_iter:
        try:
            rec = extract_one(item)
        except Exception as e:
            log.warning("extract failed for %s: %s", _label(item), e)
            continue
        if rec is None:
            continue
        key = rec.get("id") or rec.get("url") or _hash(rec)
        if key in seen:
            continue
        seen.add(key)
        out.append(rec)
    return out

この形によって守られる3つの点と、それぞれが重要な理由は次のとおりです。

  • バッチ全体ではなく、アイテムごとにtry/exceptを使います。 1件の不正なリスティングによって、残りの199件まで失わないようにします。後から対象を特定できるだけの情報(URLや安定したID)とともに失敗をログに記録し、処理を続けます。例外を黙って握りつぶすのはさらに悪く、失敗率が見えなくなります。
  • レコードの同一性ではなく、安定した正規IDで重複を排除します。 サイトが数値ID、SKU、URLスラッグを公開している場合は、それを使います。hash(record_dict)で重複を排除しないでください。実際のレコードには、空白の変更や価格の変動といった細かな違いがあり、重複を見逃すおそれがあります。
  • Noneは「このアイテムは意図的にスキップする」という意味で、例外は「試したが失敗した」という意味です。 これらは異なるシグナルなので、ログも別々に記録します。

並行処理。 上記のループは逐次処理で、これが適切なデフォルトです。サイトへの負荷が最も少なく、デバッグも最も簡単です。逐次処理が本当に遅すぎる場合に限り、並列処理を使い、上限を設けてください。

  • URLが約20件まで:リクエストの間に短いtime.sleepを挟み、逐次処理を続けます。複雑にする価値はありません。
  • URLが約20~200件:並行処理に上限を設けます。asyncio.Semaphore(N)(またはN個のスレッドプール)を使い、Nは小さく(4~8)します。すべてのリクエストを一度に送信してはいけません。200件に対する上限のないasyncio.gatherは、自ら引き起こすDoSであり、IPアドレスがブロックされます。
  • 数百件以上:この段階では、すでに明確にボット対策の領域です(ステップ4)。セマフォのサイズはCPUではなく、サイトが許容する値によって決まります。小さい値から始め、成功率が維持されている間だけ引き上げます。

並行処理の上限を決めるのはハードウェアではなく、サイトのレート制限(ステップ4、Tier 4)です。迷った場合は、速く処理してブロックされるより、遅くても最後まで完了するほうがよいでしょう。

4. 対応は段階的に行い、ボット対策には必要な範囲で対処します

最近のサイトは、スクレイピングに対して段階的に防御を強化します。懸念ではなく、実際に確認した状況に応じて対処してください。

確認できる状況考えられる原因対処法
urllib.request でデフォルトの UA を使うと 403 / 429 が返るボット UA フィルターリクエストヘッダーに実際のブラウザー UA を設定します。それだけで解決することもよくあります。
本文が空ですが、ブラウザーではコンテンツ全体が表示されるクライアント側でのレンダリングブラウザーが必要です。Playwright をデフォルト設定で使用します。
200 と実際のものに見える HTML が返るものの、必要なフィールドがない(または実行ごとに値が変わる)スタブレンダリング — キャプチャ前に DOM が確定していませんでしたキャプチャの前に特定のセレクターを待機します。その後、マーカーが存在するまで再取得する validate 述語を使って再試行します。references/anti-bot.md を参照してください。
HTML 本文のタイトルに「Just a moment...」または「Performing security verification」と表示されるCloudflare チャレンジplaywright-stealth を追加します。それでも失敗する場合は、ページごとに新しい browser.new_context() を開いてください。Cloudflare は同じコンテキスト内での再訪を検知します。references/anti-bot.md を参照してください。
一度は成功するものの、5 回目のリクエストで失敗するIP ごとのレート制限リクエストの間に time.sleep(1-3) を挟みます。それでも失敗する場合は、サイトでプロキシのローテーションが必要です。スクレイピングの量が妥当か、ユーザーに確認してください。
実際のブラウザーからアクセスしても、毎回 CAPTCHA(画面に表示されるもの)が出る積極的な WAFステルス機能と新しいコンテキストを試します。それでも失敗する場合は、「このサイトをスクレイピングできるかどうか」の限界に達しています。選択肢として、マネージド型のブロック解除 API(下記参照)、住宅 IP プロキシ、undetected-playwright、ヘッド付きブラウザーでの手動 CAPTCHA 解決、または対象範囲を縮小して(たとえば、認証なしで見られるランディングページの内容のみを取得して)対応する方法があります。制限を記録し、次に進んでください。
ログインが必要認証で保護されたコンテンツこのスキルの対象外です。認証にはサイトごとの対応が必要で、ユーザーとの別途合意(認証情報、利用規約、2FA)が必要です。ブロッカーとして明示してください。

初日から住宅プロキシを使わないでください。 実費がかかるうえ、サイトが積極的に防ごうとしている規模または強度で運用していることを示します。そのユースケースに見合うかを確認してください。

マネージド型ブロック解除サービスへの切り替え。「自分で住宅プロキシを設定する」と「諦める」の間には、知っておく価値のあるマネージド型サービスがあります。Bright Data の Web Unlocker、Zyte API、ScraperAPI などのサービスは URL を受け取り、プロキシのローテーション、TLS フィンガープリンティング、CAPTCHA の解決をすべてサーバー側で処理して、きれいな HTML を返します。費用をかける価値があるサイトで DIY が行き詰まった場合、通常はフェッチャーだけを入れ替え、ほかはそのままにするのが適切です。extract_one、重複排除、JSONL ライター、マニフェストはバイト単位で一切変更せず、HTML の受け取り方だけを変更します。利用者が出所を把握できるよう、マニフェストに切り替えを記録します("tool": "managed-unblocker" または具体的なベンダー名)。こうすることで、このスキルは特定のベンダーに依存しません。マネージドサービスは同じフェッチ境界の背後にある実装の詳細であり、全面的な書き換えではありません。エスカレーションの段階における位置づけは references/anti-bot.md を参照してください。この層に特有の次の 2 つの対応も記載されています。ブロックページの判定を通過したものの、対象フィールドを含まない不完全なレンダリングを、検証して再取得するフックで再試行すること、およびアンブロッカーの出口国によってアクセスだけでなく返されるデータも変わるため、通貨とロケールを確認することです。

参考:references/anti-bot.md に、Cloudflare / Turnstile を回避するためのエスカレーション手順の全体と具体的なパターンが記載されています。

5. 解析済みの出力だけでなく、生のペイロードを保存する

スクレイピングするすべての項目について、解析済みのレコードと一緒に生の HTML/JSON レスポンスを保存してください。これは、堅牢性を高めるうえで最も価値の高い対策です。理由は次のとおりです。

  • 再解析:パーサーにバグがあった場合(実際に起こります)、再取得せずに保存済みの生のペイロードから解析し直せます。気づいたときには、サイトが停止していたり、アクセスを制限されていたり、変更されていたりする可能性があります。
  • 原因調査:サイトが変更されてパーサーが動かなくなった場合、正常に動作していたときの生のペイロードと、動作しなくなったときの生のペイロードを比較すれば、何が変わったのかがわかります。
  • 低コスト:HTML はよく圧縮できます。1MB のページは gzip 圧縮すると約 100KB です。

慣例として、生のペイロードは出力 JSONL の隣にある raw/<id_or_hash>.html または .json に書き込んでください。後で見つけられるよう、解析済みレコードからファイル名を参照します。

6. 正解データと照合する

完了と判断する前に、次の確認を行ってください。

  1. ブロックページのチェック(最後だけでなく、取得時に行ってください)。 200 OK が返ってきても、実際のコンテンツを取得できたとは限りません。解析の前に、ペイロードが空でないこと、かつブロックページの特徴を含まないことを確認してください。特徴には "Just a moment"、"Performing security verification"、"Access denied"、"unusual traffic"、Cloudflare/Turnstile の iframe、ページの内容に対して不自然に短い本文などがあります。チャレンジページのレコードを status: "ok"、200 行として気づかずに書き込むスクレイパーは、成功したように見えても、利用可能なデータがまったく含まれていません。エラーとして明確に扱い、マニフェストでエラー数に加算し、レコードとして扱わないでください。これは、気づかないままデータが破損する最もよくある原因です。

0a. LLM で使う場合は、コンテンツをサニタイズしてください(スクレイピングしたコンテンツを LLM に入力する場合)。 非表示の要素(<script>、<style>、<template>、非表示の div)には、スクレイピングしたテキストが LLM プロンプトに入ったときに実行されるプロンプトインジェクションのペイロードが含まれていることがあります。保存またはモデルに渡す前に、これらを取り除いてください。Scrapling がインストールされている場合は、scrapling extract get <url> output.md --ai-targeted を使用してください。自動的に処理されます。それ以外の場合は、references/scrapling.md(コンセプト 1)の手動サニタイズ関数を適用してください。スクレイピングしたコンテンツが LLM のコンテキストウィンドウに入る場合は、必ず実施してください。

0b. 取得時に通貨/ロケールの規則を確認してください(データがローカライズされている場合)。 価格が設定されるもの(旅行、小売、マーケットプレイスなど)では、外向きの通信に使う国(プロキシやブロック解除サービスの出口、またはコードを実行している場所)によって、ページの通貨、言語、税の扱いが変わることがあります。そのため、£1,200 が気づかないうちに $1,200 や ¥1,200 として返される可能性があります。ページに実際に表示された通貨を読み取り、指定した通貨と比較してください。不一致があれば、換算された数値を記録せず、処理を拒否します。気づかないうちに換算された価格は、正しく見える誤った数値であり、データが欠けているよりも厄介です。スタブのような値(ほぼ 0 で、再試行すればよい値)とは異なり、これは判定可能です。再取得せず、その項目をスキップしてフラグを立ててください。詳細は references/anti-bot.md を参照してください。

  1. スモークテスト:ステップ 1 の正しいことがわかっている項目を 1 件使ってスクレイパーを実行します。生成されたレコードのすべてのフィールドを、ブラウザーに表示される内容と目視で照合してください。
  2. N 件のサンプル確認:全件実行からランダムに 5~10 件のレコードを抽出し、それぞれブラウザーで確認します。列ずれのバグ(すべてのフィールドが 1 つずれている)、単位の取り違え(£ と $、週単位と月単位)、日付形式の誤り(MM/DD と DD/MM)、値が気づかないうちに空になっているフィールドを確認してください。
  3. 件数の確認:サイトが合計件数(「この都市には 234 件の物件があります」など)を報告している場合、出力にほぼ同数のレコードがあるか確認してください(サイトに掲載されていても除外した項目がある場合は考慮してください)。
  4. 再実行時の決定性:数分以内に同じ URL を 2 回スクレイピングします。レコードやフィールド値がランダムに変わる場合は、ブラウザーを使った抽出に競合状態があります。references/extraction.md の二重レンダリングに関する注意点を参照してください。
7. JSONL + マニフェストに出力する

このスキルでは、スキーマの変更に最も強いシンプルな形式として、次の出力規約を推奨しています。

output/
├── records.jsonl          # one JSON object per line, caller-defined keys
├── manifest.json          # run metadata: timestamp, source URLs, status, counts
└── raw/                   # raw payloads, named by stable id
    ├── abc123.html
    └── def456.json

JSONL:各行は json.dumps(record) + "\n" です。ヘッダーはありません。古いコンシューマーとの互換性を損なうことなく、実行のたびに自由にフィールドを追加できます。ストリームで読み取れます。

マニフェストの構造(部分的な失敗があった実行でも必ず書き出してください):

{
  "scrape_started_at": "2026-05-24T10:34:00Z",
  "scrape_finished_at": "2026-05-24T10:36:12Z",
  "source_urls": ["https://example.com/listings"],
  "status": "ok",                  // "ok" | "partial" | "failed"
  "records_written": 187,
  "items_attempted": 200,
  "items_skipped": 13,
  "errors": [
    {"url": "...", "reason": "..."}
  ],
  "user_agent": "Mozilla/5.0 ...",
  "tool": "urllib" | "playwright" | "playwright+stealth"
}

マニフェストがあれば、呼び出し元(そして3か月後のあなた自身)がデータを信頼できるかどうかを判断できます。status: "partial" かつ items_skipped: 13 の実行と、status: "ok" の実行では、データの利用方法が異なります。

参考資料

詳しい情報は references/ にあります。必要に応じて読み込み、最初からすべて読む必要はありません。

  • references/recon.md — 偵察の判断ツリー全体と、実際のサイトで各パターンを見分ける具体例(JSON-LD の形状、Next.js/Nuxt/Drupal のデータ格納場所、よくあるフレームワークの特徴、隠れた REST API)。また、偵察前に使える2つの近道、既存のランタイム取得/スクレイピングツールの再利用と、独自実装よりも保守された商用データ製品が適している、対処が難しい巨大プラットフォームの見分け方も扱います。
  • references/playwright.md — ブラウザー自動化のパターン:使うべき場面、domcontentloaded と networkidle の違い(後者が通常不適切な理由)、セレクターの待機、ステルス、ヘッドレスとヘッドありの違い、SPA 用のページ側 JS スクレイパーの注入。
  • references/anti-bot.md — Cloudflare、Turnstile、レート制限、IP ベースのブロックへの段階的な対処。具体的なパターン(リクエストごとに新しいコンテキストを使う、ステルスライブラリの設定、マネージドブロック解除サービスへの切り替えと取得処理だけを置き換える方法、諦めるタイミング)。
  • references/extraction.md — JSON-LD の取得、Next.js/Nuxt/Drupal などのページ内 JSON データの検索、二重にレンダリングされた DOM への対処、クリック後の値の変化を待つ方法、堅牢な重複排除、一般的なフィールド形式(サイズ範囲、価格帯、日付、郵便番号、住所)の解析。
  • references/agentic-browsing.md — 問題が解析ではなく複数ステップのナビゲーションにある場合の限定的なケース(ログインフロー、フィルターウィザード、カレンダー選択)。Microsoft の Webwright エージェント型ブラウザーフレームワークについて、導入する価値がある場合と過剰な場合、インストール/CLI、「一度使ってスクリプトを堅牢化し、その後はスクリプトを決定論的に実行する」パターンを扱います。一括抽出には使いません。Webwright 自身のドキュメントにも、そう記されています。
  • references/named-div-extraction.md — <div id="..."> コンテナーからサーバー側でレンダリングされたテキストを抽出する方法(ASP.NET やレガシーサイトでよく見られます)。正規表現、BeautifulSoup、ブラウザーフォールバックの比較、入れ子の div に関する落とし穴、多言語版への対応、一括処理で urllib を使い、必要に応じてブラウザーへフォールバックするハイブリッド方式を扱います。
  • references/scrapling.md — サイトの構造が変わって CSS セレクターが機能しなくなった場合(要素の適応型再配置)、スクレイピングしたコンテンツを LLM に渡す場合(プロンプトインジェクションのサニタイズ)、または本格的なスクレイパーを書く前に CLI で手早くテストしたい場合に読み込みます。

ヘルパースクリプト

  • scripts/recon.py — python recon.py <url> は、まず通常の HTTP でページを取得し、必要に応じて Playwright でも取得して、実行可能性のある抽出戦略(静的 HTML の一致、JSON-LD ブロック、フレームワークのデータ格納、アンチボットの特徴)を報告します。新しいサイトでは、何かを書く前に必ず実行してください。

避けるべきアンチパターン

よさそうに見えても、得られるものよりコストのほうが大きい方法です。

  • 「念のため」と最初から Playwright を使うこと。 まずは調査してください。urllib で動くなら、Playwright のデバッグに費やす時間はすべて無駄になります。
  • 何でも捕捉するグローバルな try/except。 自分のバグまで捕捉し、気付かないままデータを壊してしまいます。適切な粒度は、項目ごとに処理し、失敗を記録することです。
  • テナントによって異なる値をハードコードすること。 URL 内の内部 ID(例:academicYear=82、region_id=14)、CSRF トークン、署名付きクエリパラメーターなどは、ハードコードするのではなくページから読み取ってください。サイトではこれらが定期的に変更されるため、原因がはっきりしないまま「スクレイパーが 0 件を返す」事態に直面することになります。
  • 生成されたように見える CSS クラス名に依存すること(例:.css-1a2b3c4)。これらはビルドハッシュの生成物であり、デプロイのたびに変わります。意味的なセレクター([data-product-id]、[itemprop=price]、<h1>、構造化データ、ARIA ロール)を優先してください。
  • セレクターの待機の代わりに time.sleep(N) を使うこと。 固定時間のスリープは、不安定さの最大の原因です。必ず特定の DOM 条件を待機してください。
  • 生のペイロードを破棄すること。 パースのバグをデバッグするために再取得すると、時間がかかり、レート制限を消費し、そもそも不可能なこともよくあります(サイトが変更されている可能性があります)。生のペイロードは低コストで保存できます。
  • すべての項目のすべてのフィールドにこだわること。 項目によってデータが欠けているのは、サイトによっては当然のことです。手順 1 ではフィールドをオプションとして扱い、欠けていてもクラッシュしないようにしてください。

倫理と利用規約について

このスキルが支援するのはスクレイピングの手法であり、スクレイピングを行うべきかどうかという問題ではありません。意味のある規模でスクレイパーを実行する前に、呼び出し側は次の点に責任を持ってください。

  • サイトの robots.txt と利用規約を確認する。
  • 同じ目的を果たす公開 API が利用可能かどうかを検討する。
  • トラフィックを節度あるレベル(DDoS ではなく、1 人のユーザーが閲覧する程度)に抑える。
  • 合法的な根拠なしに個人データをスクレイピングしない。
  • プログラムからアクセスする明示的な許可を得ていないログイン必須のコンテンツをスクレイピングしない。

ユーザーの依頼範囲が一線を越える場合は、これらのいずれかを懸念事項として指摘してください。「実行できますが、まず利用規約を確認してください」は妥当な回答です。

評価

2 つの評価セットが evals/ にあります。

  • evals.json — 安定した練習用サイト(静的 HTML および JS でレンダリングされるサイト)を対象とする、実行可能なエンドツーエンドのスクレイピングタスクと、期待される抽出方法。
  • routing-fixtures.json — 軽量な契約フィクスチャです。サンプルリクエストから期待されるルーティング(リクエストが entity-research または people-enrichment に該当する場合を含む)、必須の出力フィールド、禁止パターンを定義します。

ルーティングフィクスチャは仕様であり、実際のモデルに対して実行するものではありません。 tests/test_routing_fixtures.py は、フィクスチャの形式が正しく保たれていることを検証します。

コメント