WebスクレイピングのAnti-Bot対策|WAF・Cloudflare・フィンガープリント・CAPTCHAの技術解説
Anti-Botはレート制限 → WAF/CDN → フィンガープリント → CAPTCHAの多層防御として機能する。スクレイパー側は「回避テクニック」より先に429/403の意味を読み、取得頻度を下げ、公式APIを探すのが現実的。技術的に突破できても許可されていない行為は、別記事で述べる法的・倫理リスクを伴う。
なぜサイトはAnti-Bot防御を入れるのか
Webサイト運営者がボット対策を導入する動機は、セキュリティとコストの両面にある。検索エンジンのクローラー(Googlebot等)は歓迎される一方、価格監視ボット、在庫スナイパー、クレデンシャルスタッフィング、コンテンツ盗用クローラーは帯域・DB・在庫APIに直接負荷をかける。
Anti-Bot(ボット管理)は、リクエストごとに「このクライアントは信頼できるか」をスコアリングし、人間らしくないものをチャレンジ(JS/CAPTCHA)・レート制限・遮断する。CDN(Cloudflare、Akamai、Fastly)、WAF、専用ボット管理(DataDome、PerimeterX、Kasada等)がエッジからアプリケーション手前まで多層で判定する。
スクレイパー側が理解すべきは、防御が単一のフィルタではなく相関分析の積み重ねだという点だ。User-AgentをChromeに偽装しても、TLSフィンガープリントがPython requests のままなら、WAF層で先に落とされる。逆に、公開データを低頻度で取得し、識別可能なボットUAを付けた礼儀正しいクローラーは、Verified Botとして許可されることもある。
レート制限:最初に当たる壁
レート制限(Rate Limiting) はAnti-Botの第一関門であり、最も実装コスト対効果が高い。IPアドレス、APIキー、セッションCookie、ユーザーID、ASN(自律システム番号)単位で、一定時間あたりのリクエスト数を上限内に抑える。
サーバー側の典型的な実装
制限アルゴリズムは Fixed Window(1分100回でリセット)、Sliding Window(直近60秒を常時カウント)、Token Bucket(平均レートは守りつつ短時間バーストを許容)の3種が主流だ。Redisの INCR + EXPIRE、Nginx limit_req、Cloudflare Rate Limiting Rules、AWS WAF Rate-based rule などが代表例。
超過時は 429 Too Many Requests を返し、Retry-After ヘッダーで待機秒数を伝える。設計の良いAPIは X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset も付与する(詳細は APIのレート制限実装ガイド を参照)。
ボット検知に使われるリクエストパターン
レート制限だけでなく、リクエストの「形」 もスコアに加算される。
- 同一IPから短時間に数百〜数千リクエスト
- 一覧ページを経由せず、連番IDの詳細URLだけを機械的に叩く
- 人間には不可能な一定間隔(正確に100ms間隔など)
- 深夜帯に通常の100倍のトラフィック
- 同一セッションで地理的に矛盾するIP遷移(プロキシローテーション)
スクレイパー側の実装例
礼儀正しい取得では、ランダムな待機、指数バックオフ、キャッシュ、並列数の抑制を組み合わせる。
import time
import random
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
SESSION = requests.Session()
SESSION.headers.update({
"User-Agent": "MyResearchBot/1.0 (+https://example.com/bot; [email protected])"
})
retry = Retry(total=3, status_forcelist=[429, 503], backoff_factor=1)
SESSION.mount("https://", HTTPAdapter(max_retries=retry))
def fetch_with_backoff(url: str, max_retries: int = 5):
wait = 1.0
for attempt in range(max_retries):
time.sleep(random.uniform(0.8, 2.5)) # 人間的な間隔
response = SESSION.get(url, timeout=30)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", wait))
time.sleep(retry_after + random.uniform(0, 2))
wait = min(wait * 2, 120)
continue
response.raise_for_status()
return response
raise RuntimeError(f"Rate limit exceeded: {url}")
プロキシでIPを分散する手法も存在するが、規約で禁止されているケースが多い。技術的対応の前に、対象サイトが自動取得を許容しているかを確認する(法的・倫理の詳細は後述の境界線セクションおよび 法的・倫理ガイド を参照)。
WAFルールとボット検知の仕組み
WAF(Web Application Firewall) は、HTTPリクエストのヘッダー・パス・クエリ・ボディをルールベースまたは機械学習で評価し、疑わしいものを遮断する。Anti-Bot文脈では「既知のスクレイパー署名」「異常なヘッダー組み合わせ」「地理的異常」を検知する層として機能する。
AWS WAF のルール例
AWS WAFでは Managed Rule Groups(AWSManagedRulesBotControlRuleSet 等)に加え、カスタムルールを定義できる。
{
"Name": "BlockKnownScraperUAs",
"Priority": 10,
"Statement": {
"OrStatement": {
"Statements": [
{
"ByteMatchStatement": {
"FieldToMatch": { "SingleHeader": { "Name": "user-agent" } },
"SearchString": "python-requests",
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "CONTAINS"
}
},
{
"ByteMatchStatement": {
"FieldToMatch": { "SingleHeader": { "Name": "user-agent" } },
"SearchString": "scrapy",
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "CONTAINS"
}
}
]
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "BlockKnownScraperUAs"
}
}
Rate-based rule(5分間に同一IPから2000リクエスト超でブロック)も定番だ。
{
"Name": "RateLimitPerIP",
"Priority": 20,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"Action": { "Block": {} }
}
ModSecurity / OWASP CRS の例
自前Nginx + ModSecurity 環境では、CRS(Core Rule Set)の Bot Detection やカスタム SecRule が使われる。
# 空または異常に短い User-Agent を拒否
SecRule REQUEST_HEADERS:User-Agent "^$" \
"id:1001,phase:1,deny,status:403,msg:'Empty User-Agent'"
# 既知の自動化ツール(例)
SecRule REQUEST_HEADERS:User-Agent "@rx (?i)(curl|wget|python-requests|go-http-client)" \
"id:1002,phase:1,pass,nolog,setvar:tx.anomaly_score=+5"
# 異常スコアで遮断
SecRule TX:ANOMALY_SCORE "@ge 10" \
"id:1003,phase:1,deny,status:403,msg:'Anomaly score exceeded'"
WAFは誤検知(False Positive) も起きうる。正当なAPIクライアントがブロックされた場合、運営者はルール調整やAllowlist(特定APIキー・IPレンジ)を行う。スクレイパー側から見ると「403 Forbidden + 特定のWAFヘッダー(cf-ray、x-amzn-requestid 等)」が返るパターンが多い。
Cloudflare Bot Fight Mode と Super Bot Fight Mode
Cloudflareは世界最大級のCDN/WAFであり、ボット管理機能が段階的に提供される。Bot Fight Mode、Super Bot Fight Mode、Bot Management(Enterprise) の3 tierが実務で語られる。
Bot Fight Mode(無料プラン)
ダッシュボード → Security → Bots → Bot Fight Mode: ON で有効化できる基本機能。明らかな自動化トラフィック(既知の悪意ボット、簡易ヘッドレス検知)に対し、JavaScript Challenge や Block を自動適用する。
特徴:
- 設定が簡単(ON/OFFのみ)
- Verified Bot(Googlebot、Bingbot等)は自動許可
curlや素のrequestsは JS チャレンジを解けず 403 / 503 / Challenge ページ になりやすい- 細かい閾値やカスタムルールは不可
Super Bot Fight Mode(Pro / Business / Enterprise)
有料プランで利用可能。Bot Score(1=bot確度高、99=human確度高)に基づく細かいアクション設定ができる。
| 設定項目 | 内容 |
|---|---|
| Definitely automated | スコアが低いトラフィック → Block / Managed Challenge |
| Likely automated | 中間スコア → JS Challenge / CAPTCHA |
| Verified bots | 検索エンジン等 → Allow |
| Static resource protection | JS/CSS/画像への直接ボットアクセスを制限 |
カスタムWAFルールと組み合わせる例:
(http.request.uri.path contains "/api/prices") and (cf.bot_management.score lt 30)
→ Managed Challenge
(cf.bot_management.verified_bot) or (ip.src in $trusted_scraper_ips)
→ Skip(ボット検知をバイパス)
スクレイパーがCloudflareに当たったときの挙動
- Managed Challenge — ブラウザでJSを実行し、Cookie
_cf_bm等をセットしないと通過できない - Interactive Challenge / CAPTCHA — 人間確認。HTTPクライアント単体では突破不可
- Block — 403。IPレピュテーション悪化で長期ブロック
Playwright / Puppeteer で page.goto() し、チャレンジ通過後のCookieを requests セッションに移植する手法は存在するが、チャレンジ通過のたびに再取得が必要で、サイト側のルール変更に脆弱だ。Cloudflareに当たった時点で、公式API・データ提供の問い合わせを第一選択肢に据えるべきだ。
ブラウザフィンガープリント:見えない識別レイヤー
User-Agent偽装だけでは通用しない理由が、ブラウザフィンガープリント(Device Fingerprinting) だ。複数のブラウザAPI・環境属性を組み合わせ、「おそらく同一クライアント」を確率的に識別する。
収集される信号の例
| 信号カテゴリ | 具体内容と検知への使われ方 |
|---|---|
| Canvas / WebGL | 描画結果の微妙な差異(GPU・ドライバ依存)。headlessと通常ブラウザで差が出やすい |
| AudioContext | 音声処理パイプラインの処理結果。自動化環境で異常値になりうる |
| Fonts / Plugins | インストールフォント一覧。Docker上のheadlessは最小セットになりがち |
| Screen / Timezone / Language | 解像度・色深度・Intl設定。UAと矛盾するとスコア低下 |
| navigator.webdriver | Selenium/Playwright等で true になりやすい。stealth系対策とのいたちごっこ |
| TLS / HTTP2 フィンガープリント | JA3/JA4、ALPN、cipher suite。Python requests と Chrome で別物 |
| Behavioral biometrics | マウス軌跡・スクロール・キー入力間隔。完全自動ループは機械的パターン |
フィンガープリントが効く場面
- SPA(React/Vue)で初回ロード時に fingerprint SDK(FingerprintJS、PerimeterX等)が走るサイト
- ログイン後のセッション維持(同一「端末」として追跡)
- 決済・チケット販売など在庫争奪が起きるページ
スクレイパー側の現実
requests + Chrome UA では、TLS層で既に別クライアントとして扱われる。JavaScriptレンダリングが必要なページでは Playwright / Puppeteer が選択肢になるが、以下の限界がある。
navigator.webdriver === true(Playwrightは--disable-blink-features=AutomationControlled等で緩和可能だが完全ではない)- headless検知(
HeadlessChromeUA痕跡、WebGL vendor/renderer) - 行動パターン(ページ到着後0.1秒でクリック、スクロールなしで全ページ巡回)
stealth プラグインや指紋偽装ツールは、検知側も継続的にアップデートするため持続性が低い。フィンガープリントに当たったら、「より巧妙に偽装する」より取得頻度を下げる・対象を減らす・APIを探す方がメンテコストとリスクの両面で合理的だ。
CAPTCHAの導入判断とトレードオフ
CAPTCHA(reCAPTCHA v2/v3、hCaptcha、Cloudflare Turnstile、Friendly Captcha等)は、「人間かボットか」を最終関門で判定する。JavaScript実行、Cookie、場合によってはGoogleアカウント履歴(reCAPTCHA v3スコア)が前提。
サイト運営者視点のトレードオフ
| メリット | デメリット |
|---|---|
| ボット流入を大幅削減 | UX摩擦(離脱率上昇、特にモバイル) |
| WAFだけでは止まらない高度ボットにも有効 | アクセシビリティ問題(視覚障害者等) |
| v3ならユーザー操作なしでスコア判定可能 | v3は誤判定(人間なのに低スコア)のクレーム |
| ログイン・決済等の高リスク操作を保護 | 第三者(Google等)へのデータ送信・プライバシー論点 |
reCAPTCHA v3 はページロード時に 0.0〜1.0 のスコアを返し、サーバー側で閾値(例: 0.5未満は追加認証)を設ける。ユーザーには見えないが、低スコアの自動トラフィックはサイレントに拒否される。スクレイパーから見ると「200 OKだが空データ」「403」「CAPTCHAリダイレクト」のいずれかになる。
Cloudflare Turnstile は比較的軽量な代替として採用が増えている。いずれも HTTPクライアント単体ではトークン取得不可 だ。
スクレイパー側でCAPTCHAに遭遇したとき
- 即座にクロールを停止 — 続行はIP・ASNレピュテーション悪化
- CAPTCHA突破サービス(2Captcha等)の利用は推奨しない — 対象サイトToS違反、CAPTCHA提供者ToS違反、法的リスク(別記事参照)
- 公式API・データライセンス・Bulk Export を調査
- 研究目的なら サイト運営者への事前連絡(許可・レート上限の交渉)
CAPTCHAが表示されること自体が、「自動取得を望んでいない」シグナル として解釈すべきだ。
スクレイパー側が取れる技術的対応(限定的な範囲)
規約上問題なく、公開データの収集が許容される場合に検討できる手段を整理する。いずれも防御の回避ではなく、防御に抵触しない取得が目的だ。
識別可能な User-Agent を設定する(ボット名・連絡先URL・メール)
robots.txt をパースし、Disallow パスへアクセスしない
リクエスト間隔をランダム化し、並列数は1〜2程度に抑える
取得結果をキャッシュし、同一URLの再取得を避ける
JavaScriptレンダリングが必須の場合のみ Playwright 等を使い、操作間隔に待機を入れる
429・403・CAPTCHA を検知したら自動停止し、アラートを上げる
User-Agent の明示 — 偽装より、MyBot/1.0 (+https://example.com/bot) のように正直にボットであることを宣言した方が、Verified Bot登録や運営者との交渉で信頼されやすい。
ヘッドレスブラウザ — page.goto(url, wait_until="networkidle") 後、主要要素の出現まで待機。連続 page.goto ではなく、人間的なスクロール・待機を挟む。
from playwright.sync_api import sync_playwright
import random
import time
def fetch_rendered_html(url: str) -> str:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(
user_agent="MyBot/1.0 (+https://example.com/bot)",
locale="ja-JP",
timezone_id="Asia/Tokyo",
)
page = context.new_page()
page.goto(url, wait_until="domcontentloaded", timeout=60000)
time.sleep(random.uniform(1.5, 3.0))
page.mouse.wheel(0, random.randint(200, 600))
time.sleep(random.uniform(0.5, 1.5))
html = page.content()
browser.close()
return html
セッション・Cookie — ログインが必要なデータは OAuth / 公式API を優先。Cookie の無断複製はアカウント規約違反になりうる。
Anti-Botとのいたちごっこ(指紋偽装の常態化、CAPTCHA突破サービス、禁止サイトへのプロキシローテーション)は、技術的成功と引き換えにIP永久ブロック・アカウント停止・法的措置を招く。技術的にできる ≠ 許されている。
429とボット遮断に遭遇したときの実務フロー
Anti-Botに触れたときの対応順序を固定化しておくと、偶発的なIP burnを防げる。
- 即座にリクエストを止める — 429/403/CAPTCHA検知でサーキットブレーカーを開く
Retry-Afterを読む — ヘッダーがあればその秒数以上待つ(無視するとブロック期間延長)- 並列数・頻度を半減 — それでもダメなら停止
- レスポンスヘッダーを記録 —
cf-ray、server、x-ratelimit-*でどの層で落とされたか切り分け - 公式API・問い合わせ — スクレイピング継続を見直す
- 別IPで「リトライ」しない — 禁止サイトでは迂回とみなされる
レスポンス別の切り分け
| ステータス / 現象 | 想定される原因と対応 |
|---|---|
| 429 + Retry-After | レート制限。待機後に頻度を下げて再試行。改善しなければ停止 |
| 403 + cf-ray | Cloudflare WAF / Bot Management。JSチャレンジ前提。HTTPクライアントでは困難 |
| 503 + Set-Cookie | チャレンジ前段。Cookie未取得の可能性 |
| 200 だが空 / 異常HTML | reCAPTCHA v3低スコア、ソフトブロック。内容検証必須 |
| CAPTCHA ページ | 自動取得拒否。API・問い合わせへ |
倫理的な境界線:法的ガイド記事との役割分担
本記事は Anti-Botの技術的メカニズム に焦点を当てる。法的・倫理の詳細(robots.txtの扱い、著作権法、個人情報保護法、CFAA、不正アクセス禁止法、ToS違反の民事リスク等)は、別記事 Webスクレイピングの実務ガイド|法規制・robots.txt・ToS・個人情報の境界線 で体系的に解説している。
記事間の読み分け
| 本記事(Anti-Bot) | 法的・倫理ガイド |
|---|---|
| WAF / Cloudflare / フィンガープリントの仕組み | robots.txt / ToS / 著作権の解釈 |
| 429・403の技術的対応 | 規約違反時の法的リスク |
| レート制限・CAPTCHAの実装と挙動 | 個人情報・再配布のコンプライアンス |
| ボット検知を避けない取得の作法 | 迷ったときの法務相談フロー |
技術記事として押さえる最低限の倫理原則
法的詳細は別記事に委ねるが、Anti-Bot対策を学ぶスクレイパーが技術以前に守るべき線引きは次のとおりだ。
- ToSで自動取得禁止なら、WAF突破を試みない — 技術的成功は契約違反・訴訟リスクを意味しうる
- robots.txt の Disallow を尊重 — 法的拘束力の議論は別記事参照。実務では最低限の礼儀
- 429・CAPTCHAは「止まれ」の信号 — 回避より停止
- 個人情報・ログイン必須・有料コンテンツは対象外
- 負荷をかけない — 取得頻度・時間帯・キャッシュでサーバー負担を最小化
Anti-Botの「回避テクニック」を学ぶ目的は、防御を理解し、許可された範囲で持続可能なデータ取得を設計することにある。防御を破るための手順書ではない。
関連記事
あわせて読むと理解が深まる関連記事をまとめました。
| トピック | 記事 |
|---|---|
| Webスクレイピングの実務ガイド | Webスクレイピングの実務ガイド|法規制・robots.txt・ToS・個人情報の境界線 |
| Web Vitals INP改善ガイド | Web Vitals INP改善ガイド|scheduler.yield・Long Task・Input Delay対策(2026) |
| AIクローラー対応 robots.txt・llms.txt 設定ガイド 2026 | AIクローラー対応 robots.txt・llms.txt 設定ガイド 2026|GPTBot・ClaudeBot・Google-Extended・GEO戦略 |
| Image Compress WebP | Image Compress WebP|画質とサイズのバランス |
| Passkeys・WebAuthn実装の基礎 | Passkeys・WebAuthn実装の基礎|FIDO2・Resident Key・@simplewebauthn/server |
| robots.txtの書き方と役割 | robots.txtの書き方と役割|クローラーを正しく制御する方法 |
| Robots.txt 生成 | Robots.txt 生成|クローラ許可・拒否の書き方 |
| SSE vs WebSocket 使い分け | SSE vs WebSocket 使い分け|EventSource 実装と再接続設計 |
| WebHook設計のベストプラクティス | WebHook設計のベストプラクティス|署名・再試行・べき等性で信頼性を担保する |
| WebP変換で画質が落ちる原因と最適な設定 | WebP変換で画質が落ちる原因と最適な設定 |
| Web APIのエラーハンドリング設計 | Web APIのエラーハンドリング設計|RFC 9457・error_code・400/422/409の使い分け |
| Web APIのべき等性(Idempotency)設計 | Web APIのべき等性(Idempotency)設計|二重決済を防ぐ実装ガイド |
まとめ
Anti-Botは レート制限 → WAF/CDN(Cloudflare Bot Fight Mode等) → ブラウザフィンガープリント → CAPTCHA の多層防御として機能する。各層は相関分析でスコアリングされ、User-Agent偽装だけでは突破できない。
スクレイパー側の実務指針:
- 429 には指数バックオフと
Retry-After尊重 - Cloudflare / WAF には JSチャレンジ前提を理解し、HTTPクライアント単体の限界を認識
- フィンガープリント には headless も検知されることを前提に、頻度と規模を抑える
- CAPTCHA は停止信号。突破サービスは推奨しない
- 倫理・法 は 法的・倫理ガイド と併読
最優先は公式APIの有無と規約確認。技術的回避は最後の手段であり、長期運用するスクレイピングほど「防御に抵触しない設計」が成功率を決める。