スケールする礼儀正しいスクレイピング:レート制限のプレイブック

サイトを叩き続けると禁止され、一度に1リクエストずつクロールするとどこにも行き着かない。スケーラブルな中間は、クリーンなIPに分散されたドメインごとの適応ペーシングです。サイトに対して礼儀正しく、あなたにとっては速い。

インターネットトラフィックのほぼ半分が自動化されており、ウェブサイトはそれを知っています。レート制限は彼らの最初で最も基本的な防御策であり、どれだけ取れるか、どれだけ速く取れるかを決定する速度制限です。それを力ずくで突破する障害物と見なす誘惑がありますが、それでは禁止されます。反対の間違いである、一度に慎重に1リクエストずつクロールすることは、スケールではどこにも行き着きません。スケーラブルな答えは礼儀正しいスクレイピングです:重くても合法的なユーザーのように各ターゲットのペースを調整し、クリーンなIPに負荷を分散し、サイト自身の応答で速度を調整します。これが、誰もがブロックされるトラフィックにならずに速くなる方法です。

どの制限に達しているかを知る

サーバーは、通常はあなたのIP、時にはAPIキーやアカウントに対して、一定の時間枠内でリクエストをカウントし、閾値を超えるとアクションを起こします。3つの一般的なモデルは異なる動作をします:固定ウィンドウはカレンダーミニッツごとにリクエストをカウントします;トークンバケットは固定数のトークン(例えば1分間に100)を渡し、リクエストごとに1つを消費し、バケットが空になると待機を強制します;スライディングウィンドウは任意の瞬間に過去60秒間でカウントします。実際の結果は、バーストが安定したストリームよりも危険であるということです。1秒間に10リクエストは、1分間に100を分散させた場合には引っかからない制限を引っかける可能性があります。

制限にはさまざまな種類があります。ソフトリミットは穏やかです:サーバーはあなたを遅くし、または429 Too Many Requestsを返し、Retry-Afterヘッダーで正確にどれくらい待つべきかを教えてくれます(Cloudflareのエラー1015がこれです)。一部のサイトは小さなバーストを許容します—1分間に100を許可し、120でのみスロットルします—他のサイトは早期にスロットルし、例えば1分間に15で、30でのみハードブロックします。ハードリミットは厳しい上限です:1時間に1,000リクエストを許可するAPIは、それを超えると完全にロックアウトされます。ソフトリミットを繰り返し押すとエスカレートします:429が403になり、数分から数時間の一時的な禁止が増加し、最終的にはあなたのIPまたはサブネット全体の永久的なブラックリストになります。

応答を読み、推測しない

スクレイパーの最大のアップグレードは、固定レートで爆撃するのではなく、サーバーが教えてくれることに反応することです。語彙を学びましょう:429は減速しRetry-Afterを尊重することを意味します;403はこのIDがフラグされていることを意味し、再試行するのではなくローテーションすることを意味します;503はしばしば挑戦または一時的な拒否です;200がCAPTCHAページを返す場合はソフトブロックであり、成功ではありません。それぞれを異なる方法で扱います。間違った動き—403で同じ焼けたIPを再試行する、またはRetry-Afterを無視して429を突破する—は、ソフトな警告を永久的な禁止に変えるものです。429を修正するに関する深いダイブでは、応答処理を詳細にカバーしています。

import time, requests

def polite_get(session, url, max_tries=4):
    for attempt in range(max_tries):
        r = session.get(url, timeout=20)
        if r.status_code == 200 and "captcha" not in r.text.lower():
            return r
        if r.status_code == 429:                       # obey the server
            wait = int(r.headers.get("Retry-After", 2 ** attempt))
            time.sleep(wait)
            continue
        if r.status_code in (403, 503):                # this exit is burned
            rotate_ip(session)                         # fresh IP, then retry
            time.sleep(2 ** attempt)                    # exponential backoff
            continue
        return r
    return None
リクエストを送信し、ステータスコードを読み、IPをローテーションし、バックオフし、レートを調整する適応ペーシングループの図
サイトの応答コードで速度を調整します:200で加速し、429でRetry-Afterを遵守し、403でローテーションします。

ドメインごとにリクエストを予算化する

多くのサイトに触れるクローラーは、決してすべてに1つのグローバルレートを適用すべきではありません。小さなブログと堅牢なマーケットプレイスは非常に異なる負荷を許容するため、各ドメインに独自の予算を与えます。ホストごとのトークンバケットはクリーンなパターンです:ドメインごとに保守的なレートを割り当て、時間をかけて補充し、異なるホストへのリクエストを並行して実行し、同じホストへのリクエストはその制限内に留まります。新しいターゲットではゆっくりと開始し、応答コードが速度を上げるべきかどうかを教えてくれます。

import time
from collections import defaultdict

class DomainLimiter:
    def __init__(self, per_min=30):
        self.gap = 60.0 / per_min          # min seconds between hits per host
        self.last = defaultdict(float)
    def wait(self, host):
        now = time.time()
        delay = self.gap - (now - self.last[host])
        if delay > 0:
            time.sleep(delay)
        self.last[host] = time.time()

# 30 req/min to any single host; different hosts proceed independently
limiter = DomainLimiter(per_min=30)
limiter.wait("example.com")

負荷を分散して各IPが礼儀正しくなるようにする

ここに「礼儀正しさ」と「スケール」を調和させる動きがあります:礼儀正しさはIPごとに測定されますが、あなたの総スループットはIP全体の合計です。ターゲットがアドレスごとに1分間に30リクエストを許容する場合、1つのIPは30で制限されますが、10個のクリーンなIPがそれぞれ30を行うと、1分間に300を提供し、各個別の出口が礼儀正しさを保ちます。ローテーティング住宅ゲートウェイはこれを自動的に行い、90M+のアドレスにわたってリクエストごとに新しいIPを提供するため、単一の出口が攻撃的に見えることはありません。これはより強く叩くためのトリックではなく、誰もが疑わしいスパイクを負担しないように本物の負荷を分散することです。IPローテーションの基本はIPローテーションとは何か、そしてなぜそれが重要かにあります。

クリーンなローテーティングIPに負荷を分散する

少なく取り、キャッシュを増やし、時間を選ぶ

最も礼儀正しいリクエストは、送信しないリクエストです。3つの習慣がデータを犠牲にせずに負荷を削減します。まず、積極的にキャッシュし、条件付きリクエストを使用します—If-Modified-SinceまたはIf-None-Matchを送信し、変更されていないページが完全な本文ではなく小さな304を返すようにし、サーバーとあなたの帯域幅を節約します。次に、並行性を意図的に予算化します:ドメインごとのインフライトリクエストを制限するセマフォは、誤ってバーストするのを防ぎます。最後に、ターゲットのオフピーク時間に重いジョブをスケジュールし、あなたのトラフィックが彼らのトラフィックの小さな割合であり、閾値を超えにくくなります。これらを組み合わせることで、ジョブが必要とするリクエストを半分に減らすことができます—私たちの広範なアンチバンチェックリストの背後にある規律です。

もう一つ明確に言う価値のあること:robots.txtを確認し、サイトの明示されたクロール期待を尊重してください。礼儀正しさは自己保存だけでなく、良い市民であることはオープンウェブを誰もがスクレイプ可能に保ちます。robots.txtの実践に関するガイドでは、それが何を拘束し、何を拘束しないかをカバーしています。

429のソフトリミットから403、仮の禁止、永久的なIPまたはサブネットのブラックリストへのエスカレーションラダーダイアグラム
ソフトリミットを繰り返し押すと硬化します:429が403になり、増加する一時的な禁止、そして永久的なブラックリストになります。

よくある質問

スクレイピング時にレート制限を避けるにはどうすればよいですか?

各ドメインを独自のリクエスト予算でペースし、429でRetry-Afterを尊重し、指数的にバックオフし、クリーンなIPのローテーションプールに負荷を分散して、単一のアドレスが攻撃的に見えないようにします。キャッシングと条件付きリクエストを追加して全体のリクエストを減らし、ターゲットのオフピーク時間に重いジョブをスケジュールします。

HTTP 429は何を意味し、どのように対処すべきですか?

429 Too Many Requestsはソフトレートリミットです—サーバーは減速を求めており、禁止しているわけではありません。Retry-Afterヘッダーを読み、再試行する前に正確にその時間を待ちます;それがない場合は、指数的にバックオフします。それを無視して叩き続けないでください、なぜなら繰り返される429は403にエスカレートし、その後時間制限または永久的な禁止に至ります。

1分間に安全なリクエスト数はどれくらいですか?

普遍的な数はありません—それは完全にターゲットに依存します。小さなサイトは1分間に数リクエストしか許容しないかもしれません;大きなサイトははるかに多くを許容するかもしれません。保守的に開始し(例えばIPごとに1分間に20〜30)、429を監視し、応答から調整します。総スループットをスケールするには、単一のもののレートを上げるのではなく、IPを追加します。

ローテーティングプロキシは無礼と見なされますか?

本物の負荷を分散するために使用する場合はそうではありません。ローテーションは各個別のIPを礼儀正しいレート内に保ちながら、あなたの総スループットを増加させます—サーバーはどのアドレスからも疑わしいスパイクを見ません。それは、サイトが合理的に処理できる総量を超えるために使用する場合にのみ無礼になります;総量をペースし、単にIPごとのレートをペースするのではありません。

礼儀正しさとスケーラビリティは反対ではありません。シグナルを読み取り、Retry-Afterを尊重し、ドメインごとに予算を立て、キャッシュできるものをキャッシュし、残りをクリーンなローテーティングIPに分散します。これにより、無謀なスクレイパーよりも速くなります—なぜなら、あなたは決して禁止されないからです。

ローテーティング住宅プロキシで礼儀正しくスケールする