certainlyscraped について
certainlyscraped は、エージェントや開発者向けのハイブリッド型ウェブスクレイピングツールです。軽量な取得から始め、失敗やブロック時に段階的に方式を切り替えます。編集可能な YAML 設定を生成し、フィールドの充足状況や各ページの結果、停止理由を報告します。
主な機能
- 01取得方式の段階的な切り替え
- 02YAML スクレイピング設定の生成
- 03一覧ページからの行データ抽出
- 04ページごとの結果と診断
- 05プロキシプールと並列ページ処理
活用シーン
- 01ページ分割された一覧を CSV 化
- 02ブロックや未完了ページの原因調査
- 03設定済みプロキシグループ経由の取得
MCP 対応
MCP サーバーは probe、init、try_selector、scrape の4つのツールを提供します。応答には抽出行、データセットの保存先、ページごとの結果が含まれます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
4掲載時のツール一覧 · 1 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| url | any | いいえ |
詳細
certainlyscraped
段階的に試行を重ねるハイブリッドスクレーパーです。
まず成功する中で最も低コストな取得方法を試し、ページがブロックされた場合や取得に失敗し続ける場合にのみ段階を上げ、取得できた内容と停止した理由を正確に報告します。 ページを読み取り、設定ファイルを自動作成し、すべてのフィールドに値が入ったかを確認して、一覧を期待値ではなく行データに変換します。
クイックスタート:20秒
pip install "certainlyscraped[mcp]"
cscrape init https://books.toscrape.com/catalogue/page-1.html --name books
cscrape run books.yaml
init はサイトを調査し、ページ内で繰り返されるブロックを見つけ、フィールドと次のページへのリンクを推測して、各フィールドに値が入ったアイテム数を表示します。
run はページを取得して out.csv に書き込み、--min-rows(デフォルトは 1)より少ない行しか取得できなかった場合は、成功と報告するのではなく明確に失敗します。
エージェントから利用するには、MCP サーバーをクライアントに追加します。uv を使う場合、事前のインストールは不要です。
{
"mcpServers": {
"certainlyscraped": {
"command": "uvx",
"args": ["--from", "certainlyscraped[mcp]", "certainlyscraped", "mcp"]
}
}
}
uv を使わない場合は、pip install "certainlyscraped[mcp]" を実行し、"command": "certainlyscraped", "args": ["mcp"] を使用します。
これにより、エージェントは probe、init、try_selector、scrape を利用できます。完全な仕様については、MCP サーバー(エージェント向け)を参照してください。

毎日のライブチェックでの実際の出力:
[probe] https://books.toscrape.com/catalogue/page-1.html -> tier 1 (static page)
[init] wrote books.yaml: 20 items on this page (20 repeats, median text 51 chars, each has a link, each has an image)
field coverage on this page:
title 20/20 (100%)
url 20/20 (100%)
image 20/20 (100%)
price 20/20 (100%)
availability 20/20 (100%)
設定は、読み取りや編集が可能なプレーンな YAML です。
name: books
url: "https://books.toscrape.com/catalogue/page-1.html"
tier: 1
max_pages: 5
rate_limit_sec: 2.0
paginate:
selector: "li.next a"
items:
selector: "article.product_pod"
fields:
title: { selector: "h3 a", attr: title }
url: { selector: "h3 a", attr: href, absolute: true }
image: { selector: "img.thumbnail", attr: src, absolute: true }
price: { selector: "p.price_color", attr: text, type: float }
availability: { selector: "p.instock.availability", attr: text }
output: csv
エスカレーションの段階
各ページは、調査で判定した段階(または設定の tier)から開始し、その段階で失敗した場合にのみ次の段階へ進みます。
| 返された内容 | 次の動作 |
|---|---|
| レート制限(429、またはチャレンジを伴わない 419) | 別のプロキシグループ経由でページを試します。利用できるグループがない場合は、1 回待機(Retry-After、なければ cooldown_sec)して再試行します。[ladder] <host>: rate limited, waiting <n>s と表示されます |
| チャレンジページ(Cloudflare、PerimeterX、DataDome のマーカー) | プロキシを維持し、max_tier まで段階を 1 つ上げます |
| 通常の 403 | まず別のプロキシグループを試し、残りがなければ段階を上げます |
| 5xx | エラーページの場合はプロキシを維持し、同じ段階で 1 回再試行してから段階を上げます。短い 503(本文が 20 kB 未満)はエラーではなくブロックとして分類されるため、同じ段階での再試行は行いません。別のプロキシグループがあればそちらを試し、なければ段階を上げます |
| 応答なし(接続拒否、DNS、TLS、リセット) | プールがある場合は、別のプロキシグループを 1 回試します。その後はプールの有無にかかわらず、同じ段階で 1 回再試行してから段階を上げます |
グループを切り替えるたびに [ladder] <host>: <reason>, trying another proxy group も表示されるため、切り替えの様子を確認できます。
チャレンジまたは 403 によりサイトで段階が上がると、そのサイトの以降のページは新しい段階から開始します。エラーによる段階上昇のみでは、その段階は記憶されません。ページレポートの steps には、各段階と割り当てられたグループが表示されます。たとえば ["t1@dc-a", "t1@dc-b", "t2@dc-b"] の場合、あるグループでは 403、別のグループではチャレンジが発生し、段階 2 で実際の HTML を取得したことを示します。
段階
| 段階 | 内容 | コスト |
|---|---|---|
| 1 | httpx、通常の HTTP | ページあたり数ミリ秒、インストールはほぼ不要 |
| 2 | curl_cffi、TLS の偽装 | Python の依存関係が必要です。Python の TLS フィンガープリントを拒否するサイトでも、実際の HTML を返す場合があります |
| 3 | ブラウザスタック(scrapling、cloakbrowser、または playwright) | オプションですが、追加の負荷が大きくなります。max_tier: 3 の場合にのみ使用され、特定のサイトで使えるとは保証されません |
tier: auto は最初に調査を行い、実際のページを返す最も低い段階を選択します。
プロキシプールが設定されている場合、auto は調査をスキップして段階 1 から開始します。調査は直接リクエストであり、プールがある場合はオリジン IP からリクエストを送信できないためです。そこから段階を上げていきます。段階は成功を保証するものではなく、エスカレーションの開始位置です。
結果と終了コード
ページごとに1件のレコードがあり、--report report.jsonl および MCP
レスポンスの pages フィールドで利用できます。
{"url": "https://httpbin.org/status/403", "outcome": "blocked", "status": 403, "rows": 0, "attempts": 2, "steps": ["t1", "t2"], "proxy_group": null, "elapsed_ms": 1358, "note": "max_tier 2 reached"}
outcome は ok、empty、blocked、rate_limited、not_found、
error のいずれかです。実行時には、次の要約行も出力されます。
[done] 2 pages: 1 ok, 1 blocked (HTTP 403 on /status/403)
レスポンスがないページには引用できる HTTP ステータスがないため、
[done] 1 page: 1 error (no response on /list) と表示されます。
| 終了コード | 意味 |
|---|---|
| 0 | 完了:--min-rows 以上の行があり、不完全なページがありません |
| 1 | 設定、I/O、またはフェッチのエラーです。--min-rows 0 の場合は、すべてのページでエラーが発生した実行です |
| 2 | --min-rows の行数に達していません(4より2が優先されます)。デフォルトの --min-rows 1 で全ページがエラーになった場合、行がないため2になります |
| 3 | probe --fail-blocked:対象がブロックされています |
| 4 | 不完全:--min-rows 以上の行がありますが、すべての再試行後に1つ以上のページが blocked、rate_limited、または error で終了しました |
ページ単位のエラーによる終了コード1になるのは、--min-rows 0 で
行を要求しない場合に限られます。デフォルトの --min-rows 1 では、
すべてのページでエラーが発生した実行は、先に行数のチェックに失敗して
終了コード2になります。
終了コード4の対象は意図的に限定されています。範囲の末尾を超えた
not_found または empty のページは対象外で、再試行で成功したページも
対象外です。ゼロのみをチェックするスクリプトでは、--accept-partial により
4を0に変換できます。ページを1つ取得できなかった場合でも、取得できた行は
書き込まれ、レポートには欠落したページが示されます。
MCP サーバー(エージェント向け)
pip install "certainlyscraped[mcp]"
certainlyscraped mcp
クライアント設定:
{
"mcpServers": {
"certainlyscraped": {
"command": "certainlyscraped",
"args": ["mcp"]
}
}
}
同じ API を呼び出す薄いラッパーとして、4つのツールを提供します。
| ツール | 戻り値 |
|---|---|
probe(url) | ティア判定、ブロックマーカー、見つかった埋め込み JSON |
init(url) | 設定 YAML とフィールドのカバレッジ |
try_selector(url, selector, within?) | マッチ数、サンプル値、ヒント |
scrape(config, min_rows?, max_rows?) | status、行(max_rows まで。デフォルトは50)、実際の行数、データセットのパス、ページごとの結果 |
config はインラインの YAML または JSON テキストで、パスは指定できません。
設定キーの proxy、proxies、cookies、since、snapshot、fetcher は拒否され、
json や headers などの不明なキーも拒否されます(埋め込み JSON は
items.json の下に指定します)。status は実行結果を示します。
complete(min_rows 以上の行があり、不完全なページがない)、
incomplete(十分な行はありますが、ページが blocked、rate-limited、または
エラーで終了した)、too_few_rows(min_rows 未満の行)、
blocked(行が0件で、すべてのページがブロックまたはレート制限された)、
error(行が0件で、すべてのページでエラーが発生した)のいずれかです。
これらは CLI の終了コードではありません。ブロックのみの run は終了コード2で、
終了コード3になるのは probe --fail-blocked のみです。終了コード1は設定、I/O、
フェッチの例外、または --min-rows 0 の場合にすべてのページでエラーが発生した
実行を表します。不正なリクエストもステータス辞書として返され、ツールが
JSON-RPC チャネルに例外を送出することはありません。
デフォルトではプライベートアドレスへのアクセスを拒否します。対象はループバック、
RFC 1918、リンクローカル(169.254.169.254 を含む)、およびユニークローカル IPv6
で、DNS 解決後とリダイレクトの各段階でチェックされます。ローカル開発では
--allow-private を指定すると、この制限を無効にできます。CLI はプライベート
アドレスを拒否しません。拒否するのは MCP サーバーのみです。拒否されたページは
そこで処理を終了し、試行は1回のみで、再試行もティアの引き上げも行わず、
結果は error となり、メモに拒否の理由が記録されます。
プロキシはサーバー自身の --proxies proxies.yaml からのみ取得され、ツールの
引数からは取得されません。また、サーバーは CERTAINLYSCRAPED_PROXIES を
読み取りません。probe、init、try_selector は、呼び出しごとにプールから
プロキシを1つ借り受けます。プールに空きがない場合は
no proxy available for <host> を含む blocked を返し、リクエストは行いません。
--proxies を指定しない場合、サーバーにはプロキシプールがなく、直接フェッチします。
ファイルの読み取りとデータセットの保存先は、作業ディレクトリ内に限定されます。
プロキシ
1 つのプロキシは引き続き使え、プロキシ数 1 のプールになります。
proxy: "http://10.1.2.3:8000"
プールにはグループ名を付けます。403 または 429 が発生したとき、より上位のプランに移行する前に、別のネットワークを試せます。
proxies:
list: proxies.txt # one URL per line, optional "# group" label
gateway: # one generic provider gateway
template: "http://{user}-country-{country}-session-{session}:{password}@gw.example:7000"
user: myuser
password_env: GATEWAY_PASSWORD
country: se
session: rotate # or sticky
per_proxy_rate_sec: 1.0
cooldown_sec: 300
max_retries: 3
proxies.txt:
http://user:[email protected]:8000 # dc-a
http://10.1.9.9:8000 # dc-b
http://10.1.1.7:8000
グループ名には # の後のラベルを使います。ラベルがない場合は、最初に登場した順に net-1、net-2、... という中立的なラベルを、IPv4 の /16、IPv6 の /48、またはホスト名ごとに割り当てます。ラベルにプロキシアドレスやホスト名が含まれることはありません。
あるホストに対してブロックされたプロキシは、cooldown_sec の間、そのホスト向けには使用を見合わせますが、プールからは削除されません。session: rotate はリースごとに新しいトークンを生成します。つまり、ページごとに 1 つ、また 403 または 429 によってグループのリースを切り替えるたびに新しいトークンを生成します。sticky はホストとワーカーごとに 1 つのトークンを維持します。
パスワードをメイン設定に記載してはいけません。--proxies で指定する専用のプロキシファイルに記載するか、password_env を使って環境変数から渡してください。コミット済みの YAML 内にあるインラインの proxy:/proxies: URL にパスワードが含まれている場合、2 つの代替方法を示すメッセージとともに拒否されます。ページの処理結果と MCP の応答には、プロキシ URL やパスワードではなくグループラベルが含まれます。停止したプロキシで失敗したページには、たとえば ConnectionError: Failed to perform, curl: (7) Failed to connect to books.toscrape.com:443 over proxy *** ... のように、トランスポートエラーが注記として残ります(接続先はそのまま表示され、プロキシアドレスは *** になります)。名前を解決できないホストは、DNSError: ... Could not resolve host: no-such-host.invalid ... と表示されます。ログにプロキシアドレスが出力されることはないため、原因の特定にはグループラベルとエラーテキストを使います。MCP ガードによって拒否されたプライベートアドレスは、再試行も段階的な切り替えもなく、1 回で終了します。
run の優先順位は、--no-proxy > --proxies FILE > CERTAINLYSCRAPED_PROXIES > 設定ファイルです。--no-proxy は設定済みのプロキシをすべて無視し、直接取得します。MCP にこのフラグはなく、CERTAINLYSCRAPED_PROXIES も読み込みません。--proxies を指定しない場合、プロキシプールはありません。
速度を上げるには、スレッド数だけでなく、多数のホストまたはプロキシプールが必要です。処理は per_proxy_rate_sec で(ホスト、プロキシ)ごとに間隔を空けます(rate_limit_sec にフォールバックし、デフォルトは 1.0 です)。
プールがない場合、concurrency の値にかかわらず、1 つのホストへのリクエストは各間隔につき最大 1 件です。そのため、単一ホストの pages: ジョブでは、サイトの速度制限に合わせて処理されます。プールがある場合、1 つのホストに対してプロキシが N 個あれば、N 個の枠を利用できます。1 つのホストに対する 1.0 秒間隔の 8 ページの取得では、プロキシ 1 つの場合は 7.1 秒、4 つの場合は 1.1 秒でした。net-1 という自動生成のグループ 1 つでも、ラベル付きのグループ 4 つでも同じ結果です。
複数ページの並列処理
pages:
template: "https://news.ycombinator.com/news?p={n}"
from: 1
to: 10
concurrency: 4
または、明示的なリストを指定します。
urls:
- "https://example.com/a"
- "https://example.com/b"
応答が届いた順序にかかわらず、行はページの順序で出力されます。
pages: の範囲は、連続する 2 ページが not_found または empty になった時点で停止します。これは範囲の末尾に限りません。途中でページが欠けると、その時点で処理が早期終了します。これらのページは不完全とは見なされないため、実行結果の終了コードは 0 になる場合があります。
その他のコマンド
cscrape try <url> "p.price_color" --in "article.product_pod" # test a field
cscrape fetch <url> --out page.html # save once, iterate offline
cscrape probe <url> # tier verdict
cscrape verify data.csv --min-rows 10 # check a dataset after the fact
try は、JavaScript で描画されたページにフラグを付け、埋め込み JSON を示し、壊れやすい DevTools の「Copy selector」パスについて警告します。詳しいガイドは docs/selectors.md をご覧ください。
このツールで行わないこと
- ログインすること、ログイン制限を回避すること、または自分のものではない認証情報を使うこと。
- ある日に動作したサイトが、別の日にも動作することを保証すること。
- プロキシを使ってフィンガープリントや JavaScript のチャレンジ(Cloudflare managed challenge、PerimeterX、DataDome)を突破すること。これらのページは
blockedと報告されます。持ち込んだプロキシで回避できるのは IP 禁止、IP 範囲の禁止、レート制限であり、チャレンジではありません。 - 取得したデータを同梱すること。同梱されるのはツールです。
対応範囲
COVERAGE.md は、人気のデータ取得先40件をまとめた表です。毎晩調査し、日付を付けて公開しています。行データが返されたサイト、JavaScript のシェルが返されたサイト、データではない同じブロック(トラッキング用 JSON、Cookie ダイアログ、ショップの価格のないカルーセル)が繰り返し返されたサイト、アクセスが遮断されたサイトを確認できます。各行には見つかったフィールドが記載されているため、判定を自分で確認できます。この表は targets/popular.yaml から生成され、自動更新されます。そのため、アクセスを拒否し始めたサイトは、このファイルに記載された主張のままではなく、表に反映されます。ログインが必要なサイトは、ここでは定義上アクセスが遮断されています。ティア1〜3ではパスワードを持っておらず、パスワードを求めることもありません。
数値を信頼できる理由
実行のたびにページごとの結果が記録されます。また、毎日のライブチェックでは、終了コード0を表明する同じサンプルサイトをスクレイピングするため、静かなリグレッションも、親切な空の CSV ではなく、失敗チェックとして表示されます。
旧名称
0.5.0 までは scrapeforge コマンドも引き続き動作し、stderr に非推奨の通知を1回出力します。import scrapeforge も引き続き動作し、DeprecationWarning を発生させます。この警告は、Python が __main__ でインポートを実行するとき(通常のスクリプトの場合)には表示され、インポートされたモジュール内では非表示になります。新しいコードでは certainlyscraped または cscrape を使用してください。旧名称は 0.5.0 で削除されます。
開発
git clone https://github.com/vhsgreed/certainlyscraped && cd certainlyscraped
pip install -e ".[dev]"
python -m pytest -q
テストは tests/fixtures/ に保存されたページを使ってオフラインで実行されます。CI では Python 3.11〜3.14 でテストを実行します。ライブチェックでは、毎日、実際のサイトに対してサンプルを実行します。MIT ライセンスです。vhsgreed ツールセットの一部です。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。