Pythonでレビューサイトスクレイピング:CAPTCHA回避より設計を見直す
Pythonでレビューサイトをスクレイピングしていると、ある時点から急にリクエストが通らなくなる。CAPTCHA画面にリダイレクトされる、あるいは何も言わずに403が返ってくる。こういった状況に直面したとき、多くの開発者が最初にやってしまうのが「回避テクニックの検索」だ。しかし、その方向性は長期的に成功率を上げない。
この記事では、ブロックを"突破"するのではなく、そもそもブロックされにくい取得設計に組み直すための考え方を整理する。
ブロックの"種類"から診断する
まず確認すべきは、何が起きているかを正確に把握することだ。HTTPステータスコードは最初の手がかりになる。
- 429 Too Many Requests: レート制限に引っかかっている。同一IPや同一セッションからのリクエスト頻度が閾値を超えた状態。Cloudflare Developer Docsでは、ブラウザ実行において短時間にリクエストが集中すると429が返り得ることを明記している。
- 403 Forbidden: WAFやアクセス制御ポリシーによって、そのリクエスト自体を拒否している。ヘッダの構成、TLS指紋、あるいはIPの評判に基づくこともある。
- 5xx系: サーバー側の問題か、意図的な応答制限かを区別する必要がある。
CAPTCHAページへのリダイレクトが起きるなら、それはサーバーが「自動化トラフィックの疑い」と判定したシグナルだ。単なるリクエスト拒否とは別レイヤーの問題になる。ログには最低限、URL、ステータスコード、発生時刻、並列数、リトライ回数を記録しておく。これがないと原因の切り分けができない。
現代のアンチボットはヘッダ変更では解けない
2018〜2021年頃の情報には「User-Agentを変えれば通る」「Cookieを渡せばいい」といった対処法が多い。ただ現実として、Cloudflare TurnstileやBot Managementはそれより多くのシグナルを見ている。
CloudflareのBot Managementは、機械学習と行動分析を組み合わせて悪性トラフィックを検知・緩和すると公式に説明されている。具体的には、TLS指紋、HTTPヘッダの順序と構成、JavaScriptエンジンの実行結果、セッション内の行動パターンなど、複数のシグナルを組み合わせる。Turnstileは従来の画像認識CAPTCHAを置き換える形で、ユーザーへの見えない形でのチャレンジを完結させる設計だ(Cloudflare Turnstile公式ドキュメント)。
また、Google CloudのreCAPTCHAはスクレイピングやクレデンシャルスタッフィングを防ぐボット対策として位置づけられており、攻撃パターンを特定するための分析ドキュメントも公開している。
「ヘッダやUAを少し変える」だけでは対応できないケースが増えているのは、こうした多層的な検知の結果だ。
robots.txtと利用規約を先に読む
技術的な対処の前に、まず/robots.txtを確認する。Cloudflareの公式記事でも触れられているように、robots.txtは通常https://example.com/robots.txtで閲覧できる。これはサイト側が「どのクローラーにどこまでアクセスしてほしいか」を示す意図の表明だ。
ただし、robots.txtは技術的に強制されるものではない。問題は法的・契約的なリスクだ。レビューサイトの利用規約(ToS)が自動アクセスを明示的に禁じている場合、それに反するアクセスは契約違反に該当し得る。CAPTCHAを突破しようとする行為は、相手のセキュリティ措置を無効化する試みとして解釈される可能性もある。この点は技術設計の前に整理しておくべき問題だ。
Pythonでの正しいレート制御設計
429が返ったとき、正しい応答は「待つ」ことだ。指数バックオフ+ジッターの組み合わせが標準的な設計になる。
import time
import random
import requests
def fetch_with_backoff(url, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, timeout=10)
if response.status_code == 200:
return response
if response.status_code == 429:
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
continue
if response.status_code in (403, 404):
break # リトライしても意味がないケース
return None
httpxの非同期版でも同じ考え方が使える。接続エラーとHTTPエラーは分けてハンドリングするのが原則だ。
並列数についても明確な上限を設ける。並列リクエスト数を上げれば上げるほど検知される確率が上がる。参考として、SECのEDGARは自動化アクセスに対して「1秒あたり10リクエスト」という上限を公式ガイダンスとして示している(SEC.gov、2021年7月27日発表)。公開サービスがこのような明示的な上限を設ける理由は、公平なアクセスを維持するためだ。同じ考え方を自前のスクレイピングに適用することが、サーバー負荷の観点からも理にかなっている。
ブロックされにくい取得設計の実務パターン
取得設計そのものを見直す視点も重要だ。
取得範囲の最小化: 必要なページと項目だけに絞る。全ページを定期的にクロールするのではなく、差分取得で対応する。HTTPのETagやIf-Modified-Sinceヘッダが使えるなら活用すると、サーバー側との無駄なやりとりを減らせる。
停止条件の設計: 一定件数以上で429または403が連続した場合、自動的に処理を止める仕組みを最初から入れておく。「いつまで頑張るか」を事前に決める。無限リトライは運用上も検知上も問題になる。
ブラウザ自動化の使い方: Playwrightなどのブラウザ自動化は再現性のある取得に使えるが、負荷上限と停止条件をセットで設計する。自動化イコール高速大量取得という発想が問題を起こす。
代替手段を先に検討する
レビューサイトが公式APIやデータフィードを提供していれば、それが最優先だ。スクレイピングより安定していて、利用規約のリスクもない。データライセンス契約が提供されているケースもある。
構造的にスクレイピングが困難な領域については、データ取得基盤を運用として検討する選択肢がある。Bright Dataのようなウェブデータプラットフォームは、プロキシネットワーク、ブラウザ自動化、Web Unlocker APIなどを統合した形で提供しており、IPブロックやJSレンダリングへの対応、データ品質検証まで含めた設計になっている。コンプライアンス対応(KYC、GDPR/CCPAへの言及、濫用防止体制)を前面に出している点も、業務利用として評価できる基準になる。
正しい順序で設計する
整理すると、レビューサイトのスクレイピングで問題に直面したときの対応順序はこうなる。
- 利用規約とrobots.txtを確認する
- HTTPステータスとレスポンス内容からブロックの種類を特定する
- 指数バックオフ+ジッターと停止条件をPythonコードに組み込む
- 並列数と取得頻度を下げ、差分取得を導入する
- 公式APIやデータライセンスの代替手段を検討する
チャレンジページが連続して出る場合、突破を試みるより取得戦略を見直すほうが長期的なコンプライアンスと成功率の両方を高める。Cloudflare TurnstileやBot Managementのような現代的なアンチボット設計は、単一テクニックでの恒久的な回避を前提として外すよう設計されている。その事実を受け入れた上で設計するのが、実務的に正しいアプローチだ。