パーサーをスケールアップする際の典型的な間違いは、プロキシを「目分量」で購入することです: 100 IPを取得し、パーサーを起動し、30分後に禁止されました。問題はアドレスの数ではなく、誰も各IPの実際の負荷を計算しないことです。RPS(リクエスト毎秒)、遅延、およびターゲットサイトの制限に基づいたプロキシプールの計算式を詳しく見ていきましょう — 具体的な数字とPythonのコードを使って。
なぜIPの数でプールを計算するのが間違いなのか
パーシングの初心者の典型的なアプローチ: 「50,000の商品カードを集める必要があるので、500のプロキシを購入して負荷を分散させる」。論理は健全に思えますが、重要な点を考慮していません — WildberriesやOzonのようなサイトは、絶対的なリクエスト数ではなく、1つのIPからのリクエストの強度によって禁止します。つまり、各プロキシが1秒あたり20リクエストを送信する500のプロキシは、瞬時に禁止されます — アンチボットシステムはDDoSに似たパターンを検出します。
一方、50のプロキシがあり、それぞれが5-10秒ごとにランダムな間隔で1リクエストを行う場合、人間の行動を模倣して、何週間も禁止されることなく安定してパーシングできます。IPの数は安定性の理由ではなく、正しく計算された負荷の結果です。だからこそ、式は「いくつのIPを購入するか」ではなく、「達成すべきRPSはいくつで、1つのIPに対する安全な負荷はどれくらいか」に基づくべきです。
もう一つのポイント: 異なるタイプのプロキシは、1つのIPあたりの「耐久性」が異なります。データセンターのプロキシは、高頻度のリクエストでより早く禁止されるため、サブネットがホスティングとして簡単に認識されます。レジデンシャルおよびモバイルIPは通常のユーザーのように見え、ブロックのリスクなしにやや高い頻度を許可できますが、制限を完全に無視することはできません。
プロキシプールの基本的な計算式
プール内のプロキシの数を計算する式は次のようになります:
N = (RPS_target × Delay_per_ip) / Concurrency_per_ip
ここで:
- N — プール内の必要なプロキシの数;
- RPS_target — パーシングの目標速度(システム全体のリクエスト毎秒);
- Delay_per_ip — 1つのIPからのリクエスト間の最小安全間隔(秒単位);
- Concurrency_per_ip — 1つのIPで許可される並列スレッドの数(通常は1、レジデンシャルプロキシの場合は最大2)。
論理は簡単です: 合計で1秒あたり10リクエストを維持したい場合、1つのIPからのリクエスト間の安全な間隔が8秒であれば、1つのIPは物理的に8秒ごとに1リクエストしか送信できず、つまり0.125 RPSです。合計で10 RPSを得るには、10 / 0.125 = 80のプロキシが必要です。これが「500 IPをとりあえず」という推測を置き換える計算です。
パーシングのためのターゲットRPSの計算方法
プールを計算する前に、RPS_target — 実際に必要なリクエスト毎秒を定義する必要があります。ここでの式は逆です:
RPS_target = Total_requests / Time_budget_seconds
例: Wildberriesから8時間(28,800秒)で100,000の商品カードを収集する必要があります。各カードに1リクエストがかかる場合、RPS_target = 100,000 / 28,800 ≈ 3.47リクエスト毎秒です。これは見た目ほど多くはありません — 多くの人が必要な速度を過大評価し、過剰な数のプロキシを購入します。
タスクに複数のタイプのリクエストが含まれている場合(たとえば、最初にカテゴリのリストを取得し、次にカード、次にレビューを取得する)、各ステージのRPSを別々に計算してください — それらは異なるプロキシプールで並行して実行でき、プラットフォームへの合計負荷は異なるエンドポイントに分散されます。
1つのIPあたりの制限: 1分あたりの安全なリクエスト数
Delay_per_ip — 式の最も重要なパラメータであり、各サイトに対して経験的に定義する必要があります。マーケットプレイスのパーシングに基づいた一般的な指標は次のとおりです:
| プラットフォーム | 1つのIPからのリクエスト間の安全な間隔 | 1つのIPからの最大リクエスト数/分 |
|---|---|---|
| Wildberries(カードAPI) | 4-6秒 | 10-15 |
| Ozon(商品ページ) | 5-8秒 | 8-12 |
| Avito(広告) | 6-10秒 | 6-10 |
| Yandex.Market | 5-7秒 | 8-12 |
これらの数字は出発点であり、絶対的なものではありません。保守的な値(間隔の上限)から始め、429および403エラーの割合を監視し、禁止が増えない場合は徐々に間隔を短くしてください。RPSを急激に増加させることは、プロキシプール全体を1日で失う最も一般的な方法です。
実践的な計算: Wildberries、Ozon、Avito
3つの実際のシナリオを式に基づいて完全に計算してみましょう。
シナリオ1: Wildberriesの価格監視。 20,000商品の価格を2時間ごとに更新する必要があります。RPS_target = 20,000 / (2 × 3600) ≈ 2.78 RPS。1つのIPあたりの間隔が5秒で、Concurrency = 1の場合: N = (2.78 × 5) / 1 ≈ 14プロキシ。IPの一部が禁止される場合に備えて、1.5-2倍の係数でプールを取得することをお勧めします、つまり21-28プロキシです。
シナリオ2: Ozonのカタログの一回限りの収集。 24時間で500,000カード。RPS_target = 500,000 / 86,400 ≈ 5.79 RPS。6秒の間隔の場合: N = (5.79 × 6) / 1 ≈ 35プロキシ。予備を持つために50-60プロキシ。
シナリオ3: Avitoでの競合他社のリアルタイム監視。 5,000広告、15分ごとに更新。RPS_target = 5,000 / 900 ≈ 5.56 RPS。8秒の間隔の場合: N = (5.56 × 8) / 1 ≈ 45プロキシ。ここでは、Avitoがデータセンターのサブネットを積極的に禁止しているため、このタスクにはレジデンシャルまたはモバイルIPをすぐに考慮するのが賢明です。
式におけるレジデンシャル、モバイル、データセンターのプロキシ
プロキシのタイプは、Delay_per_ipに直接影響し、したがって式の最終的なNに影響を与えます。データセンターのプロキシは安価で高速ですが、リクエスト間の間隔が長くなる必要があり、高頻度で禁止されることが多いです — 実際には、同じ5 RPSの場合、レジデンシャルIPよりも2-3倍のデータセンターIPが必要になることがあります。
| プロキシのタイプ | 平均Delay_per_ip | いつ使用するか |
|---|---|---|
| データセンターのプロキシ | 8-15秒 | オープンAPI、厳しいアンチボットがないサイト |
| レジデンシャルプロキシ | 4-8秒 | Wildberries、Ozon、Avito、およびその他のアンチボットを持つマーケットプレイス |
| モバイルプロキシ | 3-6秒 | 最も攻撃的な保護、ソーシャルメディア、モバイルAPI |
WildberriesやOzonのようなマーケットプレイスをパーシングする場合、通常はレジデンシャルプロキシが最適な選択肢です — 価格と安定性のバランスを取り、禁止の急増なしに短い間隔を維持できます。データセンターのプロキシは、攻撃的なアンチボットがないプラットフォームや、低RPSの一回限りのタスクにのみ検討すべきです。
Pythonでのプールの実装: ローテーション付きのコード
以下は、各IPのリクエスト頻度を制御するプロキシプールの簡略化された実装です。論理: 各プロキシは最後の使用時間を保持し、スケジューラは最後のリクエストから十分な時間が経過したIPのみを選択します。
import time
import random
from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class ProxyNode:
address: str
delay_per_ip: float # 安全な間隔(秒)
last_used: float = field(default=0.0)
class ProxyPool:
def __init__(self, proxies: List[str], delay_per_ip: float):
self.nodes = [ProxyNode(address=p, delay_per_ip=delay_per_ip) for p in proxies]
def get_available_proxy(self) -> Optional[ProxyNode]:
now = time.time()
available = [
node for node in self.nodes
if now - node.last_used >= node.delay_per_ip
]
if not available:
return None
# 利用可能な中からランダムに選択し、キューのパターンを回避
node = random.choice(available)
node.last_used = now
return node
def size(self) -> int:
return len(self.nodes)
def calculate_pool_size(rps_target: float, delay_per_ip: float, concurrency: int = 1) -> int:
"""式: N = (RPS_target * Delay_per_ip) / Concurrency_per_ip"""
return max(1, int((rps_target * delay_per_ip) / concurrency))
# 使用例
rps_target = 5.79 # タスクの量と時間から計算
delay_per_ip = 6.0 # Ozonの安全な間隔
n_proxies = calculate_pool_size(rps_target, delay_per_ip)
print(f"プールに必要なプロキシ数: {n_proxies}") # ~35、予備を持つ50-60
実際のパーサーでは、このロジックにタスクキュー(たとえば、asyncioやmultiprocessingを使用)を追加する必要があります。これにより、ワーカーは自由なプロキシが現れるのを待ち、存在しない場合にエラーで終了することはありません。また、429/403エラーを受け取った場合に指数バックオフを追加することも重要です — これは、ブロックの兆候がある場合に特定のIPの間隔を自動的に増加させます。
プールの監視と動的修正
式による静的な計算は出発点であり、最終的な解決策ではありません。実際の運用では、3つの重要なメトリックを追跡する必要があります:
- 成功率 — 総リクエスト数に対する成功したリクエストの割合(コード200)。90-95%未満に低下すると、Delay_per_ipを増加させるか、プールにプロキシを追加する必要があります;
- 禁止率 — 過去1時間に禁止の兆候を示したプロキシの割合(キャプチャ、403、検証フォームへのリダイレクト);
- 実際のRPS — タイムアウトや再試行のために計算された値とは異なる可能性がある、リクエスト処理の実際の速度。
実践的なルール: 禁止率が1時間あたり5-7%を超えた場合、Delay_per_ipを20-30%増加させ、式に従ってNを再計算してください。成功率が数時間にわたって98%を超えて安定している場合は、間隔を徐々に短くし、プールのサイズを減らすことができます — これは、禁止や障害のリスクを失うことなくプロキシの予算を直接節約することになります。
各IPごとにログを保持する良いプラクティスです: 最後の成功したリクエストの時間、連続エラーの数、平均応答時間。これにより、「壊れた」プロキシを30-60分間ローテーションから自動的に除外することができ、リクエストを送信し続けて各ステップでキャプチャを受け取ることを防ぎます。
プロキシプール計算時の一般的な間違い
式を知っていても、計算の詳細で間違えるのは簡単です。以下は典型的なミスのリストです:
- 禁止に対する予備を無視する。 完璧な計算をしても、5-10%のプロキシはブロックやネットワークの問題で一時的に利用できなくなります。常に基本のNに1.3-2倍の係数を追加してください;
- すべてのエンドポイントに同じDelay_per_ip。 カテゴリページと商品カードページは異なる制限を持つ可能性があります — 別々に計算してください;
- テストなしでConcurrencyを1以上にする。 1つのIPからの並列リクエストは、特にレジデンシャルプロキシでは禁止のリスクを急激に高めます — Concurrency = 1から始めてください;
- User-Agentやヘッダーのローテーションがない。 正しく計算されたプロキシプールでも、すべてのリクエストが同じブラウザのフィンガープリンティングで行われる場合は救えません;
- ジッターなしの固定間隔。 リクエスト間の厳密に同じ間隔(たとえば、正確に5.0秒)は、アンチボットによって簡単に認識されるパターンです。±20-30%のランダムな偏差を追加してください。
結論
N = (RPS_target × Delay_per_ip) / Concurrency_per_ipの式は、プロキシプールの計算を推測から具体的な数字を持つエンジニアリングの課題に変えます。まず、データの量と時間に基づいてパーシングの目標速度を決定し、次に特定のプラットフォームに対する安全な間隔を経験的に見つけ、それから必要なIPの数を計算します — 禁止や障害に対して30-100%の予備を持って。
このアプローチは予算を節約します: 「とりあえず」過剰な数のIPを購入するのではなく、目標RPSを達成するために必要なプロキシの量だけを正確に支払います。マーケットプレイスやその他の保護されたサイトをパーシングする場合は、レジデンシャルプロキシから始めることをお勧めします — Wildberries、Ozon、Avitoのアンチボットシステムでの作業において、安定性とコストの最良のバランスを提供します。