クイックアンサー
スクレイパーは403、429、CAPTCHAの応答をどう扱うべき?
これらを障害ではなく信号として扱いましょう。429の場合はRetry-After待機し、そのホストでのリクエスト頻度を下げます。403の場合は一度だけ休止後に再試行し、それでも続くなら停止しサイトのルールを確認します。CAPTCHAの場合はそのホストを止めて自動解決はしないでください。CrawlRelayのレジデンシャルプロキシは$0.50/GBから提供、負荷分散や位置設定に使えますが、サイトの判定を回避する手段ではありません。KYC不要。暗号通貨で支払い可能。
3つの信号を一目で理解
| 429 Too Many Requests(リクエストが多すぎます) | あなたのアクセスが速すぎます。Retry-Afterを尊重し、バックオフし、ホスト単位の同時接続数を下げましょう |
|---|---|
| 403 Forbidden | リクエストが拒否されました。一度だけ休止後に再試行し、その後は停止してサイトのルールを確認します |
| CAPTCHAまたはチャレンジページ | サイトは人間かどうかを確認しています。そのホストを停止しペースを落としましょう |
| クロールルールの標準 | robots.txt、RFC 9309;Crawl-delayはよく使われる拡張機能です |
| サポートしていないこと | CAPTCHA自動解決、ステルスプラグイン、フィンガープリント偽装 |
それぞれの信号はクローラーに何を伝えている?
- 429 は最も丁寧な信号です:コンテンツは存在していますがあなたのペースではありません。RFC 6585で定義され、よく
Retry-Afterヘッダーで再アクセス可能時間が示されます。 - 403 はそのリクエストを拒否するものです。修正可能な場合(誤った国からのアクセス、不完全なヘッダー、速すぎる)もあれば、最終的にコンテンツへの自動アクセスを許可しないケースもあります。
- CAPTCHAやチャレンジページは、しばしば403や503ステータスで返され、サイトのボット管理があなたのトラフィックを疑っていることを意味します。これは無視された429の次の段階の警告です。
これら3つは段階的に強まる信号です。429を無視するサイトは403、チャレンジへとエスカレートし、最終的にIPレンジ単位での長時間ブロックを行います。これはそのレンジを使う全ユーザーに影響します。
クローラーは各信号にどう対処すべき?
| 信号 | 最初の対応 | 継続する場合 | 停止の条件 |
|---|---|---|---|
| 429 | Retry-Afterを待つかジッター付き指数バックオフをする | そのホストの同時接続数を半分にする | Retry-Afterが長くなるか、429が単リクエストでも継続 |
| 403 | 一度だけ休止後に再試行、同じ国からのアクセス | 同国ブラウザと比較し、robots.txtと利用規約を確認 | 同じURLパターンでの2回目403 |
| CAPTCHA / チャレンジ | 数分間そのホストを休止 | 非常に低い頻度で再開 | 低頻度でもチャレンジが返る場合は自動アクセスが認められていない |
| 503とRetry-After | 指示通り待つ | そのホストを休止 | 数分以上ほとんどのリクエストが失敗 |
正しく応答するクローラーをどう作る?
ルールは各スパイダーではなくフェッチャーに組み込む。例として1ホスト1リクエストずつ送り、robots.txtとそのCrawl-delayを尊重し、Retry-Afterを待機、連続3回拒否されたらそのホストへの要求を止める。この例では4xxのrobots.txtは許可、5xxは完全拒否として扱います(RFC 9309準拠)。
import time
import urllib.robotparser
from urllib.parse import urlsplit
import requests
PROXY = "http://USERNAME-country-us:[email protected]:7777"
USER_AGENT = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
class PoliteFetcher:
"""One request at a time per host; robots.txt, Crawl-delay and Retry-After honoured."""
def __init__(self, min_interval=1.0, max_refusals=3):
self.min_interval, self.max_refusals = min_interval, max_refusals
self.http = requests.Session()
self.http.proxies = {"http": PROXY, "https": PROXY}
self.http.headers["User-Agent"] = USER_AGENT
self.robots, self.next_time, self.refusals = {}, {}, {}
def _robots(self, root):
if root not in self.robots:
rp = urllib.robotparser.RobotFileParser(root + "/robots.txt")
try:
r = self.http.get(root + "/robots.txt", timeout=(10, 30))
if r.status_code >= 500:
rp.parse(["User-agent: *", "Disallow: /"]) # unreachable: assume disallow
else:
rp.parse(r.text.splitlines() if r.status_code == 200 else []) # 4xx: allowed
except requests.RequestException:
rp.parse(["User-agent: *", "Disallow: /"])
self.robots[root] = rp
return self.robots[root]
def get(self, url):
scheme, host = urlsplit(url)[:2]
if self.refusals.get(host, 0) >= self.max_refusals:
return None # this host said no: stop asking
rp = self._robots("%s://%s" % (scheme, host))
if not rp.can_fetch(USER_AGENT, url):
return None
delay = max(self.min_interval, rp.crawl_delay(USER_AGENT) or 0)
wait = self.next_time.get(host, 0) - time.monotonic()
if wait > 0:
time.sleep(wait)
r = self.http.get(url, timeout=(10, 30))
pause = delay
if r.status_code in (429, 503):
retry_after = r.headers.get("Retry-After", "")
pause = max(delay, int(retry_after) if retry_after.isdigit() else 60)
refused = r.status_code in (403, 429) or "captcha" in r.text[:5000].lower()
self.refusals[host] = self.refusals.get(host, 0) + 1 if refused else 0
self.next_time[host] = time.monotonic() + pause
return r
fetcher = PoliteFetcher()
resp = fetcher.get("https://example.com/")
print(resp.status_code if resp is not None else "skipped")正当な403解消法は?
いくつかの403原因はあなた側にあり、適切に対応すべきです:
- 誤った国からのアクセス。 多くのサイトは地域限定公開です。
-country-<code>でターゲット国を指定しましょう。 - 不十分なヘッダー。
Accept、Accept-Language、連絡用URL付きの正直なユーザーエージェントを送信してください。ブラウザ風のユーザーエージェントをHTTPクライアントで送るのは矛盾が生じやすく拒否されます。 - あなたのアクセスペース。 Nリクエスト後に発生する403はペースの問題です。遅くしましょう。
無効な対応は、ユーザーエージェント回転で多人数に見せかけたり、ブラウザフィンガープリント偽装、IPを巡回させて故意のブロックを回避することです。もしサイトが自動アクセス排除のためにホスティングレンジをフィルターしている場合、レジデンシャルIPだからアクセスが許されるということはありません。
なぜCAPTCHAを解くだけで済ませないのですか?
CAPTCHAはサイトからの質問であり、誤った回答をさせるためのものです。自動で解くことは、礼儀正しいクローラーをアクセス制御を突破するためのツールに変えてしまい、それによって法的リスクが高まります。2026年の米国の判例(Google対SerpApi、Reddit対SerpApiなど)では、被告がDMCAの回避禁止ルールに基づきボット検知システムを回避したと主張されています。裁判所は結論を出していませんが、リスクは現実的です。
CrawlRelayはCAPTCHAの解決やボット検出回避をサポートしておらず、その利用規約ではゲートウェイを通じてこれらを禁止しています。チャレンジの裏にあるデータが必要な場合は、サイトにアクセス許可を求めるかAPIを使用するか、データのライセンスを取得してください。
いつサイトに連絡すべきですか?
サイトのデフォルト設定以上のアクセスが必要な場合です。多くのサイトはAPIやデータフィード、パートナープログラムを提供しているか、自己申告と目的説明のあるクローラーには単に高い制限を設けています。ユーザーエージェントに連絡用URLを入れ、クローラーの内容と拒否方法を説明するページを用意し、通報には迅速に対応しましょう。連絡可能なクローラーはブロック前に警告を受けます。
403、429とCAPTCHAに関する質問
レジデンシャルプロキシはCAPTCHAを防げますか?
期待しないでください。レジデンシャルプロキシの出口IPが変わるだけで、クローラーの行動は変わりません。ペースや行動に起因するCAPTCHAは残りますし、それが目的です。少ないリクエスト数でもチャレンジが続く場合、そのコンテンツへの自動アクセスはサイトが望んでいません。
Retry-Afterが付いていない429の後、どれくらい待つべき?
最初は数秒間待ち、429が続く度に待ち時間を倍にします。待ち時間にランダムな余裕を持たせ、約1分を上限にしてください。リクエストが成功し始めたら、429が発生したレートよりも低い速度で再開し、エラー率が安定するまで徐々に上げます。
IPをローテーションしてレート制限を回避するのは許されますか?
ローテーションは出口IP間で負荷を分散し、場所を選べるので一般的です。しかし、サイトが拒否したレートのまま、1IPあたりの制限を下回るためだけに使うのは回避行為です。ほとんどの利用規約で禁止されており、当社の利用規約でも禁止しています。
クローラーはどのユーザーエージェントを送るべきですか?
ご自身のものを使ってください:名前、バージョン、クローラーの説明と連絡方法URL(例:ExampleCrawler/1.0 (+https://example.com/crawler-info))を含めます。GooglebotやGPTBotなど他のクローラー名は使用しないでください。
情報源 (4)
- RFC 9309: Robots Exclusion Protocol — IETF (2022-09)
- RFC 6585: 追加HTTPステータスコード(429) — IETF (2012-04)
- Reddit対SerpApi LLC — Loeb & Loeb (2026-08)
- Google対SerpApi: 当社の棄却申し立てが認められました — SerpApi (2026-07-21)
礼儀正しいクローラーのためのレジデンシャルプロキシ
$1.50は1 GB、$0.50/GBは50 GBから。