ブロックされずにスクレイピングする: コード付きの実践ガイド
スクレイピング中にブロックされるのは、通常次のいずれかが原因です。リクエストを送りすぎた、速すぎた、クライアントが通常のブラウザや既知のボットとまったく違って見えた、またはあなたの IP アドレスがサイトに信頼されていないリストに載っている。いくつかの習慣で、この三つすべてを減らせます。ブロックを早く察知すること、サイトが抵抗したら速度を落とすこと、一貫性のある正直な ID を保つこと、そしてプロキシは適切な場合だけ使うことです。このガイドでは、それぞれを動作するコードとともに説明します。アクセスを許可されているサイトに使ってください。
ブロックを見分ける
ブロックは、必ずしもエラーページのようには見えません。次に注意してください。
- 403 Forbidden。 サイトがあなたのクライアントを拒否していることを意味することが多いです。再試行する前に止まって、そこにいてよいかどうかを確認してください。
- 429 Too Many Requests。 送りすぎていることを意味します。
Retry-Afterヘッダーが付くことが多いです。 - 503 Service Unavailable。 サイトが過負荷になっている可能性があります。後退してください。
- ほとんど内容のない 200 応答、ログインやチャレンジページへのリダイレクト、または期待したデータが欠けているページ。
失敗のたびに、ステータスコードと本文の短いサンプルを記録してください。ログに見えないブロックは、直すこともできません。
リトライで後退する
サイトが「速度を落として」と言ったら、コードは少し減速するのではなく、大きく減速すべきです。試行回数に上限を設けた指数バックオフが標準的な方法です。サイトが Retry-After を送ってくる場合は、それも尊重します。
import time
import requests
session = requests.Session()
session.headers.update({
"User-Agent": "ExampleBot/1.0 (+https://example.com/bot)",
"Accept-Language": "en-US,en;q=0.9",
})
def polite_get(url: str, max_attempts: int = 4) -> requests.Response:
delay = 2.0
resp = None
for _ in range(max_attempts):
resp = session.get(url, timeout=30)
if resp.status_code not in (429, 503):
return resp
retry_after = resp.headers.get("Retry-After", "")
wait = float(retry_after) if retry_after.isdigit() else delay
time.sleep(wait)
delay *= 2
return resp # まだブロックされている: 最後の応答を呼び出し元で処理させる
resp = polite_get("https://example.com/products?page=1")
print(resp.status_code, len(resp.text))
この関数が何をしないかにも注目してください。403 はリトライしませんし、一つのブロックを越えるために ID を次々に切り替えることもしません。403 は解く対象のパズルではなく、止まるという判断です。
ブロックを段階的に診断する
実行が失敗し始めたら、何かを変更する前に次の確認を行ってください。
- 最初に失敗した URL とそのステータスコードを見る。ブロックは、ランダムではなく特定の地点から始まることが多いです。
- 最初の失敗の数分前のリクエスト頻度を比べる。バーストが最も多い原因です。
- 失敗が一つのパス、一つのセクション、一日のうちの特定の時間帯に限られていないか確認する。
- 同じ URL を通常のブラウザで一度手で開く。ブラウザで読み込めるなら、違いはあなたのリクエストパターンにあります。ブラウザでもブロックされるなら、サイトは今のところそのページをあなたに提供していません。
- その後で初めてコードを変更する。まず速度を落とし、次にバックオフを加え、最後に ID を見直してください。
一度に多くを変えると、どれが効いたのかが分からなくなります。
一貫性のある正直な ID を保つ
ブロックされるスクレイパーは、リクエストごとに ID が変わっていたり、残りのトラフィックと一致していなかったりします。一つのセッションと、明確な一つの User-Agent が、最も単純で安定した ID です。ボットに名前を付け、連絡先のリンクを加え、通常のクライアントが送るヘッダーだけを送ってください。多くの異なる訪問者に見せるために User-Agent を変えるのは回避の一形態であり、ブロックをより起こしやすくする傾向があります。
サイトがブラウザと通常の HTTP クライアントに異なる内容を提供する場合は、それらのページに本物のブラウザを使い、同じレート制限を守ってください。それは正当な経路であり、リクエストのパターンも妥当に保たれます。
プロキシは慎重に使う
プロキシは、リクエストが発信されるアドレスを変えます。正当な必要のためのツールです。たとえば、テストを許可されたサイトに対して特定の地域からテストする場合や、自分のインフラ全体に負荷を分散する場合です。サイトが自分に設けた制限から逃れる手段ではありません。プロキシの形式は、プロキシのタスク種別で使われるものと同じです。
proxies = {
"http": "http://user:pass@proxy.example.net:8000",
"https": "http://user:pass@proxy.example.net:8000",
}
resp = session.get("https://example.com/products", proxies=proxies, timeout=30)
プロキシの種類を選ぶことは別の問題であり、住宅用とデータセンタープロキシの比較とスクレイピング用のローテーティングプロキシで扱っています。
ブロックがキャプチャになるとき
一部のサイトは、レートの問題に対して、強制的なブロックではなくキャプチャで応えます。妥当な順序は、止まる、速度を落とす、ルールをもう一度確認する、それから初めて解決を検討する、です。PopularCaptcha のウィジェットでは、Scarnox のライブ API が、フォームの h-captcha-response フィールドに送るトークンを返します。他のキャプチャ種別は、ライブの一覧を確認してください。reCAPTCHA、Cloudflare Turnstile、GeeTest、AWS WAF、Arkose のサポートは近日公開予定です。詳しくはウェブスクレイピングのキャプチャを参照してください。
サイトでブロックが出始めたら、原因はほぼ常にリクエストのパターンです。スクレイピング時のキャプチャを避けるでは、それを防ぐ習慣を扱っています。
責任を持ったスクレイピング
- 開始する前に
robots.txtとサイトの利用規約を確認し、求められている経路と遅延に従ってください。 - 403 や「スクレイピング禁止」という明確なメッセージを受けたら止めてください。試し続けないでください。
- 通常の訪問者が気づかない程度までリクエスト頻度を低く保ってください。
- 理由のない個人データは収集しないでください。収集したデータは、適用される privacy ルールに従って扱ってください。
- サイトが処理できる量を大きく超えるデータが必要なら、所有者にフィードや API を依頼してください。