协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
rotate-scraping-proxies【开发与工程】プロバイダ非依存のプロキシローテーションで、ブロックされたスクレイピング キャンペーンを段階的に突破します。データセンター、レジデンシャル、 モバイルの各プールを使い分け、scrapling にローテーションを統合し、 ステートフルなフロー向けにセッションの固定性を設定し、コストと健全性を 監視しつつ、法的・倫理的な境界の内側に留まります。`headless-web-scraping` のクライアントサイドのステルス(StealthyFetcher、レート制限、robots.txt) では不十分で、かつトラフィックが正当である場合の次の一手として利用して ください。
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
headless-web-scraping
のクライアントサイドのステルス(StealthyFetcher、レート制限、robots.txt)
では不十分で、かつトラフィックが正当である場合の次の一手として利用して
ください。
license: MIT
allowed-tools: Bash Read Write Grep Glob
metadata:
author: Philipp Thoss
version: "1.0"
domain: web-scraping
complexity: intermediate
language: Python
tags: web-scraping, proxies, rotation, residential, scrapling, networking
locale: ja
source_locale: en
source_commit: 75ded7a1
translator: "Claude + human review"
translation_date: "2026-04-14"クライアントサイドのステルス対策をすでに使い切ったスクレイピング キャンペーンに対する、ネットワーク層での段階的な対処です。プロキシ ローテーションは最終手段であり、既定の選択肢ではありません。コストが 高く、倫理的に繊細で、容易に悪用されます。このスキルは、うまく使う 方法と同じくらい、使うべきでない場面を教えます。
headless-web-scraping(Fetcher → StealthyFetcher → DynamicFetcher)
を試したにもかかわらず、対象サイトが依然として 403/429/地理ブロックを
返すrobots.txt が該当
パスを許可しているpython-requests ではない)利用しない場面: 公開 API が存在する場合(それを使う)、サイトの 利用規約が自動アクセスを禁止している場合、地理ライセンスを回避する ことになる場合、または目的が不正行為・クレデンシャルスタッフィング・ スニーカー bot・コンテンツ海賊行為である場合。
ワークフロー全体を、文書化された法的・倫理的レビューの通過を条件として ゲートします。この手順を飛ばすことが、害の最大の発生源です。
コードを書き始める前に確認すべき入力:
Expected: すべての質問に、説明可能な書面での回答が揃っている。最初に 「いいえ」または「不明」が出た時点で、解決するまで手順を停止する。
On failure:
プールの種別ごとに、コスト・検知されやすさ・倫理的プロファイルが異なり ます。実際にブロックを解消できる範囲で、最も安価なティアを選びます。
| プール種別 | 検知されやすさ | コスト | 適した用途 |
|---|---|---|---|
| Datacenter | 高(Cloudflare/Akamai に容易にブロックされる) | $ | 本格的な bot 対策がないサイト、地理的切り替えのみ |
| Residential | 低(実 ISP の IP) | $$ | データセンター ASN をブロックするサイト |
| Mobile | 極めて低(キャリアグレード NAT、数千人と共有) | $$ | Residential さえブロックするサイト(稀) |
Residential と Mobile に関する倫理的留意点: これらのプールは実際の 一般消費者の接続を介してトラフィックを中継します。プール運営者の同意 モデルは多様で、ユーザに対価を支払う事業者もあれば、「無料 VPN」の EULA に出口ノード同意を紛れ込ませ、ユーザが実際には読んでいない形で 成立させている事業者もあります。監査済みかつオプトインの同意モデルを 持つプロバイダを選んでください。見知らぬ他人が自分の家のルータ経由で スクレイピングトラフィックを送ることに抵抗があるなら、他人の回線を 通して自分のトラフィックを送ってはいけません。
Expected: 最も安価で実用可能なティアを選択したことが文書化され、 なぜより高いティアを棄却したか(またはなぜ高いティアが必要か)の短い メモが添えられている。
On failure:
scrapling のフェッチャーにプロキシを組み込みます。認証情報は環境変数
から読み込みます。ハードコードは厳禁であり、.env を git にコミット
しては絶対にいけません。
import os
import random
from scrapling import Fetcher, StealthyFetcher
# Pattern A: provider-managed rotating endpoint (one URL, provider rotates per request)
PROXY_URL = os.environ["SCRAPING_PROXY_URL"] # http://user:pass@gateway.example:7777
fetcher = StealthyFetcher()
fetcher.configure(
headless=True,
timeout=60,
network_idle=True,
proxy=PROXY_URL,
)
# Pattern B: explicit pool, rotate yourself
POOL = os.environ["SCRAPING_PROXY_POOL"].split(",") # comma-separated URLs
def fetch_with_rotation(url):
proxy = random.choice(POOL)
fetcher = StealthyFetcher()
fetcher.configure(headless=True, timeout=60, proxy=proxy)
return fetcher.get(url)
Expected: リクエストが成功し、呼び出しごとに送信元 IP が変化する。
実際のスクレイピングを走らせる前に、IP エコー用エンドポイント
(例: https://api.ipify.org)で確認する。
On failure:
-rotating やリクエスト単位の
フラグの有無をドキュメントで確認するワークロードごとにローテーションの粒度を決め、その上でプールを健全に 保ちます。
# Sticky session for stateful flows (login, multi-page checkout-like crawls)
# Most providers expose a session ID via the username:
# user-session-abc123:pass@gateway.example:7777
# All requests with the same session ID exit through the same IP for ~10 min.
# Per-request rotation for anonymous bulk scraping (default)
# Pool health check — call before bulk run
def check_pool(pool, sample_size=5):
sample = random.sample(pool, min(sample_size, len(pool)))
alive = []
for proxy in sample:
try:
r = StealthyFetcher().configure(proxy=proxy, timeout=10).get(
"https://api.ipify.org"
)
if r.status == 200:
alive.append(proxy)
except Exception:
pass
return alive
# Backoff on transient proxy failures
def fetch_with_backoff(url, max_attempts=3):
for attempt in range(max_attempts):
try:
r = fetch_with_rotation(url)
if r.status not in (407, 502, 503):
return r
except Exception:
pass
time.sleep(2 ** attempt)
return None
Expected: ステートフルなフローではリクエストをまたいで Cookie が 保持される。匿名の大量スクレイピングではリクエストごとに IP がばらつく。 死んだプロキシはループせずにスキップされる。
On failure:
プロキシトラフィックには GB 単価とリクエスト単価が発生します。暴走した スクレイパは暴走した請求書を生みます。常に上限と停止機構を組み込んで ください。
import time
class ScrapeBudget:
def __init__(self, max_requests, max_duration_seconds, max_failures):
self.max_requests = max_requests
self.max_duration = max_duration_seconds
self.max_failures = max_failures
self.requests = 0
self.failures = 0
self.start = time.monotonic()
def allow(self):
if self.requests >= self.max_requests:
return False, "request cap reached"
if time.monotonic() - self.start >= self.max_duration:
return False, "time cap reached"
if self.failures >= self.max_failures:
return False, "failure cap reached (circuit breaker)"
return True, None
def record(self, success):
self.requests += 1
if not success:
self.failures += 1
budget = ScrapeBudget(max_requests=1000, max_duration_seconds=3600, max_failures=20)
for url in target_urls:
ok, reason = budget.allow()
if not ok:
print(f"Aborting: {reason}")
break
response = fetch_with_backoff(url)
budget.record(success=response is not None)
time.sleep(1) # rate limiting still applies even with rotation
Expected: 予算上限が暴走コストに先んじて発動する。ログにプロキシ ごとの成功率が出力され、問題のある送信元 IP を特定して除外できる。
On failure:
gateway.、proxy=、プロバイダのホスト名で
grep する).env(または同等のファイル)が .gitignore に含まれているrobots.txt が依然として尊重されている。ローテーションがこれを
上書きすることはないStealthyFetcher とレート制限を試す。ローテーションは高コストで、
不要に展開するのは倫理に反する。robots.txt を無視する:
ローテーションは許可を与えるものではない。ディレクティブは
ディレクティブである。