あなたのScrapyスパイダーはテスト用の100ページでは絶好調なのに、1万ページを超えたあたりで崩れ始める。これは厳密にはスクレイピングのバグではなく、スクレイピングの不具合を装ったネットワーク問題です。多くの人がまず頼る解決策がプロキシで、request.meta に差し込むだけなら30秒ほどで済みます。
ただし、その30秒で終わる対処法の先にこそ、本当に重要な話があります。基本的な解説記事の多くはそこで止まり、meta={"proxy": "http://IP:PORT"} を書くか、ミドルウェアクラスを1つ作って終わりにしてしまいます。しかし実運用では、クロール途中でそのプロキシが死んだらどうするのか、認証情報がGit履歴に残らないようにどう守るのか、失敗したリトライで同じプロキシをまた使ってしまうのをどう防ぐのか、といった論点が欠かせません。このガイドでは、失敗時に直接接続へ逃がさない設定、秘密情報の管理、リトライ時の明示的なプロキシ選択、プロトコルごとの制約、そしてコスト差を測定できる設計まで扱います。
Scrapyのプロキシミドルウェアとは?
プロキシミドルウェアとは、Scrapyのダウンローダーパイプライン内で動き、リクエストが実際に送信される前に「どのIPから来たように見せるか」を決めるコードです。Scrapyには標準で HttpProxyMiddleware があり、request.meta の proxy キーを読み取り、必要に応じてプロキシ認証を付与し、接続を開くダウンロードハンドラーへリクエストを渡します。カスタムプロキシミドルウェアはこの送信処理を置き換えるものではなく、標準機構が動く前に「どのプロキシを使うか」を選ぶセレクターです。この違いは見た目以上に重要で、「自作ミドルウェアが動かない」という不具合報告の大半はここから生まれます。
本番のScrapy案件でプロキシが重要になる理由
プロキシはネットワーク経路と見かけ上の送信元IPを変えます。これは、地域限定の挙動確認、許可されたトラフィックの分散、ネットワーク障害の切り分けには役立ちます。ただし、許可を与えるものではなく、レート制限を回避する魔法でもなく、アクセス保証にもなりません。クロールにプロキシが必要かどうかは、対象サイト、利用規約、リクエスト頻度、そして求められる信頼性で決まります。
プロキシはどれも同じではなく、選び方を間違えるとそれ自体が本番障害になります。
| プロキシ種別 | 信頼性 | 速度 | 検知リスク | 一般的なコスト |
|---|---|---|---|---|
| 無料の公開プロキシ | 大きく変動 | 変動 | 高いことが多い | 無料だが、セキュリティと運用上のリスクが大きい |
| データセンタープロキシ | 提供元と対象次第 | 高速なことが多い | 対象次第 | 多くはGB単位またはIP単位の課金 |
| レジデンシャルプロキシ | 提供元と対象次第 | 変動 | 対象次第 | 多くはGB単位の課金 |
| ISPプロキシ | 提供元と対象次第 | 変動 | 対象次第 | 提供元依存 |
無料プロキシについては、実測リスクがかなり大きいため、あえて触れておく価値があります。30か月にわたる Free Proxies Unmasked study では、11社の64万以上のアドレスを追跡し、そのうち34.5%が少なくとも1回は稼働、16,923件でコンテンツ改変が確認されました。この結果は、公開リストに対する強いセキュリティ警告としては十分ですが、今あるすべてのリスト、課金型プール、対象サイト、ワークロードにそのまま当てはまる故障率ではありません。
また、プロキシがあれば最新のボット対策を簡単に突破できるわけでもありません。Cloudflareのようなサービスは、TLSフィンガープリント、ヘッダーの整合性、JavaScript実行、行動パターンなど、数十種類の信号でリクエストを評価します。IPはその中の1要素にすぎません。ヘッダーが不自然で、cookie jarもないままのクリーンなレジデンシャルプロキシでも、普通にフラグが立つことがあります。「IPを回せばいいだけ」と考えてシステム全体を組む前に、期待値はきちんと調整しておきましょう。
始める前に
- 難易度: 中級
- 所要時間: 本番向け構成全体で約30〜45分、簡易テストなら約5分
- 必要なもの: Python 3.9以上、Scrapyのインストール済み環境(このガイドは Scrapy 2.17.0、2026年7月リリース版で検証)、
scrapy startprojectで作成した動作中のスパイダー、少なくとも1つのプロキシエンドポイント(テストなら任意のデータセンタープロバイダーの無料トライアルで十分)
ステップ1: request.meta でプロキシを試す
プロキシが動くかを最短で確かめるには、ミドルウェア構成を全部飛ばして、まずそのまま使ってみるのが一番です。
Scrapy標準の HttpProxyMiddleware は request.meta の proxy キーをそのまま読み取り、そこ経由でリクエストを流します。設定変更も、ミドルウェアクラスも不要。必要なのは1つのキーワード引数だけです。
import scrapy
class ProxyTestSpider(scrapy.Spider):
name = "proxy_test"
start_urls = ["https://httpbin.org/ip"]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url,
meta={"proxy": "http://203.0.113.10:8080"},
callback=self.parse,
)
def parse(self, response):
self.logger.info(response.text)
scrapy runspider proxy_test.py で実行してください。プロキシが正常なら、httpbin.org/ip は自分のIPではなくプロキシのIPを返します。それが確認結果です。もしレスポンスが止まり、最終的に TCP connection timed out エラーになるなら、そのプロキシは死んでいるか到達不能です。残念ながら、これはプロキシ業者が言うほど珍しいことではありません。
この方法は、単発のスパイダーや短い検証には十分です。ただし、スパイダーが2つ以上になると一気に崩れます。5つのファイルに同じプロキシ文字列をハードコードすることになるからです。
ステップ2: カスタムプロキシミドルウェアを作る
1つのスパイダーだけで済まないなら、プロキシの処理は1か所にまとめるべきです。プロジェクトの middlewares.py に ProxyMiddleware クラスを作成しましょう。
from scrapy.exceptions import NotConfigured
class ProxyMiddleware:
def __init__(self, proxy_url):
self.proxy_url = proxy_url
@classmethod
def from_crawler(cls, crawler):
proxy_url = crawler.settings.get("PROXY_URL")
if not proxy_url:
raise NotConfigured("PROXY_URL is required; refusing silent direct fallback")
return cls(proxy_url=proxy_url)
def process_request(self, request, spider):
if "proxy" not in request.meta:
request.meta["proxy"] = self.proxy_url
次に、settings.py に登録します。
import os
PROXY_URL = os.environ.get("PROXY_URL")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
ここで注目したいのは、単純な __init__ ではなく from_crawler クラスメソッドを使っている点です。Scrapyで設定を読むなら、この形が実際に推奨されるパターンであり、後のステップ4で秘密情報を扱う際にも同じ仕組みを使います。メリットは単純で、設定を1つ変えるだけでプロジェクト内の全スパイダーが新しいプロキシを使うようになります。壊れたIPを差し替えるたびに5ファイルをgrepする必要はもうありません。
ステップ3: ミドルウェアの実行順を理解する(そうしないとプロキシが何もしていないように見える)
ここは多くの解説記事が「とりあえず350にしておけば大丈夫です」で流してしまう部分です。Scrapyのダウンローダーミドルウェアは決まった順序で動き、これを理解していないと、プロキシとは無関係に見える奇妙な不具合が出ます。
リクエスト側のフック(process_request)は 昇順 で実行されます。つまり、数値が小さいものから先です。レスポンス側のフック(process_response、process_exception)は 降順 で実行され、チェーンを逆向きに戻ります。
リクエストの流れ(昇順):
Spider → [100] RobotsTxt → [300] HttpAuth → [350] あなたのプロキシミドルウェア
→ [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
→ Downloader
レスポンスの流れ(降順):
Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
→ [500] UserAgent → [350] あなたのプロキシミドルウェア → [300] HttpAuth
→ [100] RobotsTxt → Spider
以下は、Scrapyの標準ミドルウェアにおける現在のデフォルト優先順位です(2.17.0で確認済み)。
| ミドルウェア | デフォルト優先順位 |
|---|---|
| RobotsTxtMiddleware | 100 |
| HttpAuthMiddleware | 300 |
| DownloadTimeoutMiddleware | 350 |
| DefaultHeadersMiddleware | 400 |
| UserAgentMiddleware | 500 |
| RetryMiddleware | 550 |
| RedirectMiddleware | 600 |
| CookiesMiddleware | 700 |
| HttpProxyMiddleware | 750 |
| DownloaderStats | 850 |
| HttpCacheMiddleware | 900 |
RetryMiddleware がリトライをスケジュールすると、失敗したリクエストを meta も含めてコピーし、その新しいリクエストが再びダウンローダーミドルウェアチェーンに入ります。セレクターの数値が550より前後どちらにあるかは、ローテーションを保証しません。ローテーションが起きるのは、セレクター側のコードがリトライを認識し、コピーされた meta["proxy"] を意図的に上書きしたときだけです。350という値が便利なのは、標準の送信処理である750より前に実行されるからであって、それ自体がローテーション機能という意味ではありません。

よくあるミドルウェア順序のミス
- ミドルウェアを
HttpProxyMiddlewareと同じ優先順位(750)にする: Scrapyの辞書順(あなたのロジックではない)で、どのミドルウェアが先に処理するかが決まるレースコンディションになります。症状: プロキシ挙動が断続的で、理由が説明できない。 - 回転型セレクターで
setdefault()やif "proxy" not in request.metaを使う: コピーされたリトライが古いプロキシをそのまま引き継ぎます。症状: すべてのリトライが同じ失敗ルートを繰り返す。対策: リトライを検出し(例:retry_times > 0)、セレクターが管理するプロキシ値を明示的に差し替える。 HttpProxyMiddlewareを完全に無効化する: 「自作ミドルウェアが全部やるから」として"scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": Noneを設定する解説がありますが、これは危険です。自作ミドルウェアはセレクターであって送信層ではありません。標準機能を止めるとプロキシ認証ヘッダーが付かず、リクエストは認証なしで静かに送られるか、そもそも送られません。
ステップ4: プロキシ認証情報をハードコードしない
上位表示されているScrapyのプロキシ解説記事をいくつか見ましたが、そのほとんどが http://username:password@proxy.example.com:8080 をPythonファイルに直書きしています。それはGit履歴に永遠に残る認証情報であり、クローン、フォーク、そして print() の雑な使い方によるログ出力にも出てしまいます。
やり方には大きく3段階あります。
| 方法 | セキュリティ | 柔軟性 | 向いている用途 |
|---|---|---|---|
| スパイダー/ settings.py に直書き | 弱い — シークレットがリポジトリ内に残る | 低い | 短時間のローカル検証のみ |
http_proxy 環境変数(Scrapy標準) | 良い — コード外に置ける | 低い(1つのプロキシのみ) | CI/CD、Docker |
.env ファイル + python-dotenv + from_crawler | 最良 — コード外、環境ごとに分離可能 | 高い(複数プロキシ、ローテーション) | 本番スクレイパー |
3つ目の方法は、きちんと作る価値があります。まず python-dotenv をインストールし、.env ファイルを作成して、すぐに .gitignore に追加してください。これは本当に、今やってください。
PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080
次に settings.py の先頭で読み込みます。
from dotenv import load_dotenv
import os
load_dotenv()
PROXY_USER = os.getenv("PROXY_USER")
PROXY_PASSWORD = os.getenv("PROXY_PASSWORD")
PROXY_HOST = os.getenv("PROXY_HOST")
PROXY_PORT = os.getenv("PROXY_PORT")
そのうえで、from_crawler を使ってミドルウェア内で安全に読み込みます。これは、「scrapy crawl を実行する前に設定したいが、毎回認証情報が変わる場合はどうするの?」というフォーラムの定番疑問にそのまま効くやり方です。
import os
from urllib.parse import quote
class SecureProxyMiddleware:
def __init__(self, user, password, host, port):
self.user = quote(user, safe="")
self.password = quote(password, safe="")
self.host = host
self.port = port
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(
user=settings.get("PROXY_USER"),
password=settings.get("PROXY_PASSWORD"),
host=settings.get("PROXY_HOST"),
port=settings.get("PROXY_PORT"),
)
def process_request(self, request, spider):
proxy_url = f"http://{self.user}:{self.password}@{self.host}:{self.port}"
request.meta["proxy"] = proxy_url
ここで urllib.parse.quote() で認証情報を囲っている点に注目してください。パスワードに @、:、/ が含まれていると(生成ツールはこれらを好んで入れます)、先にURLエンコードしていないとURLの解釈が壊れます。これは、実はプロキシが無効だったわけではないのに「invalid proxy URL」エラーに1時間費やすような、恥ずかしいバグを防ぐ1行修正です。
認証情報はログから外し、代表的だが偽の値でパーセントエンコードをテストしてください。HTTPSトンネル、SOCKS5、非ラテン文字の認証情報には、それぞれハンドラーごとの境界があります。ピン留めした統合テストなしに、ある認証パターンがすべてで通ると思わないでください。

ステップ5: プロキシローテーションを追加する
良いプロキシでも1本で同じサイトに5,000リクエスト送れば、いずれ検知されます。必要なのはプールです。
方法A — 自作する。 実際かなり簡単です。
import random
class RotatingProxyMiddleware:
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def process_request(self, request, spider):
request.meta["proxy"] = random.choice(self.proxy_pool)
これは基本的な用途には動きますが、どのプロキシが生きているかまでは分かりません。毎回サイコロを振っているようなものです。
方法B — scrapy-rotating-proxies を使う。 このサードパーティ製パッケージには、バン検知と自動バックオフが最初から入っています。
pip install scrapy-rotating-proxies
ここは正直に言っておきます。PyPIの最新版 は2019年公開の0.6.2で、プロジェクトのタグはAlphaです。現代のScrapyで必ず壊れるとは言いませんが、「2019年以降メンテされていない」と「2026年の本番トラフィックで実戦投入できる」は同じ意味ではありません。バージョンを固定し、実際の対象サイトでテストし、認証付きプロキシエンドポイントをそのまま扱えるとは思わないでください。基本的には対応していません。
ステップ6: 障害に強いプロキシミドルウェアを作る(死んだプロキシの検出)
ここは競合記事が丸ごと飛ばしがちな部分で、デモと「6時間の無人クロールに耐えるもの」の差が出るところです。
| シナリオ | 多くの解説記事の扱い | このミドルウェアで追加するもの |
|---|---|---|
| プロキシが407を返す | 触れない | プロキシ認証失敗として扱う |
| 対象が403/429を返す | まとめて扱いがち | ポリシー/レート制限の反応とプロキシ健全性を分離する |
| プロキシがタイムアウトする | 触れない | 設定可能なタイムアウト閾値、健全性スコアの減衰 |
| すべてのプロキシが死んでいる | 触れない | 静かなフェイルオーバー、または警告ログ付きの一時停止 |
| プロキシが不安定に切り替わる | 触れない | プールに戻す前のクールダウン期間 |
import time
import random
from scrapy.exceptions import IgnoreRequest
class FaultTolerantProxyMiddleware:
MAX_FAILURES = 3
COOLDOWN_SECONDS = 300
def __init__(self, proxy_pool):
self.pool = {p: {"failures": 0, "banned_until": 0} for p in proxy_pool}
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def _healthy_proxies(self):
now = time.time()
return [p for p, s in self.pool.items() if s["banned_until"] < now]
def process_request(self, request, spider):
healthy = self._healthy_proxies()
if not healthy:
spider.logger.warning("All proxies unhealthy — pausing crawl")
raise IgnoreRequest("No healthy proxies available")
request.meta["proxy"] = random.choice(healthy)
def process_response(self, request, response, spider):
proxy = request.meta.get("proxy")
if proxy and response.status == 407:
self._mark_failure(proxy)
return response
def process_exception(self, request, exception, spider):
proxy = request.meta.get("proxy")
if proxy:
self._mark_failure(proxy)
def _mark_failure(self, proxy):
state = self.pool[proxy]
state["failures"] += 1
if state["failures"] >= self.MAX_FAILURES:
state["banned_until"] = time.time() + self.COOLDOWN_SECONDS
state["failures"] = 0
実際に作ってみると、いくつか注意点があります。403をすべて「プロキシが死んでいる証拠」とは見なさないでください。RFC 9110 で403は「サーバーは理解したが拒否した」を意味します。つまり、ヘッダーやセッションが怪しいだけの可能性も十分あります。プロキシそのものの問題とは限りません。一方で407は、プロキシが認証を拒否していることを直接示します。こちらのほうが、より強く具体的な信号です。これらを同じ箱に放り込むと、無事なプロキシを無意味に焼き切ることになります。
本番では、Scrapyの一時的なリトライ設定を明示しておくと、レビュー時にポリシーが見えやすくなります。
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2
これはScrapy 2.17のデフォルトです。403や407を安易に追加しないでください。403は対象サイトの拒否であり理由は複数考えられますし、407はプロキシ認証の問題で、一般的なリトライでは直りません。特定の対象契約上どうしても別ステータスを再試行する必要があるなら、その理由を文書化し、個別にテストしてください。

ステップ7: リトライ時にプロキシを切り替える、測定可能なパターン
許可された対象の中には、直接アクセスでも有効なコンテンツを返し、一時的なレスポンスが出たときだけプロキシが必要になるものがあります。この場合、プロキシ経由の転送量を減らせることはありますが、普遍的に何%節約できるという正当化可能な数字はありませんし、直接リクエストの成功率にも移植可能な基準値はありません。採用前に、必ず自分のワークロードで測定してください。
Scrapyの公開ヘルパー get_retry_request() を使い、対象ごとに明示的に分類したステータスにだけエスカレーションを限定します。この例では429と503を再試行対象の圧力シグナルとして扱い、403と407は意図的に除外しています。
from scrapy.downloadermiddlewares.retry import get_retry_request
class CostAwareEscalationMiddleware:
def process_response(self, request, response, spider):
if response.status not in {429, 503}:
return response
retry = get_retry_request(
request,
spider=spider,
reason=f"proxy_escalation_{response.status}",
max_retry_times=2,
)
if retry is None:
return response
current_tier = request.meta.get("proxy_tier", "direct")
if current_tier == "direct":
retry.meta["proxy"] = spider.settings["DATACENTER_PROXY"]
retry.meta["proxy_tier"] = "datacenter"
elif current_tier == "datacenter":
retry.meta["proxy"] = spider.settings["RESIDENTIAL_PROXY"]
retry.meta["proxy_tier"] = "residential"
else:
return response
return retry
計測すべきなのは、直接で有効コンテンツを返した割合、プロキシ別の有効コンテンツ率、成功1件あたりの転送量、成功までのリトライ回数、そして追加レイテンシです。直接優先の設計が許されるのは、直接アクセスが許可されており、しかもプロキシが必要なときにはシステムが確実に失敗停止する場合だけです。節約額は「プロキシ経由のトラフィックがどれだけ減ったか」という実測値であり、推定値ではありません。
自作のプロキシ管理が割に合わないケース
ここまでを全部無意味だったふりをするつもりはありません。正直に言うと、上で説明した内容はどれも実用的なエンジニアリングですし、多くのプロジェクト――高頻度クロール、独自パイプライン、リクエスト制御を細かく握りたいケース――では正しい選択です。
ただし、実際の目的が「プロキシ基盤を運用すること」ではなく「このページから構造化データを取ること」であるなら、抽出APIのほうが適切な境界になる場合があります。Thunderbit の現在の POST /extract ドキュメント はページURLと任意のJSON Schemaを受け付け、Schemaを省略するとページ内容から自動生成することもできます。このエンドポイントには none、basic、full のレンダーモードがあり、タイムアウトや読み込み後の待機時間も制御できます。プロンプトだけでの抽出は現在のサポート範囲には含まれていないため、本番統合は公開されているSchema契約に沿って設計してください。これにより抽出インターフェースは1回のリクエストの後ろに隠せますが、あらゆるボット対策やCAPTCHA対象に万能だと約束するものではありません。
| 観点 | DIYのScrapy + プロキシ | APIベースの抽出(例: Thunderbit) |
|---|---|---|
| セットアップ工数 | 高い — ミドルウェア、ローテーション、リトライロジックが必要 | 低い — Schema付きのAPI呼び出し1回 |
| ネットワーク/レンダリング挙動 | ハンドラー、プロキシ、ヘッダー、遅延を自分で設計 | 公開されたAPIオプションで制御 |
| 保守 | セレクター、プールの健全性、対象変更を自分で管理 | Schema品質、検証、統合挙動を自分で管理 |
| コストモデル | プロキシ費用 + 計算資源 + エンジニア工数 | 現在のドキュメントでは1ページ20ユニット(2026-08-10確認) |
| 制御性 | 完全 — 独自パイプライン、ミドルウェアチェーン | API機能の範囲内 |
| 向いている用途 | 複雑なクロール、高ボリューム、独自ロジック | 対象を絞った抽出、試作、データ拡張 |
主にリード一覧、商品データ、調査用に構造化ページを抜き出したいなら、Thunderbit Chrome拡張機能 かAPIで代表的なサンプルを試し、有効レコード数、レイテンシ、消費ユニット、保守時間を比較してみてください。プロジェクトがカスタムなクロールグラフやパイプライン制御を必要とするなら、Scrapyのほうが依然として適しています。
さらに詳しく
ヒントとよくある落とし穴
- ヒント: 実際の対象に当てる前に、
httpbin.org/ipでプロキシを検証してください。複雑さを足す前に経路確認する最速の方法です。 - 落とし穴:
request.metaではなくrequest.headersにプロキシを入れてしまうこと。これは本当にありがちな、タイプミスに近いバグです。HttpProxyMiddlewareが読むのはmeta["proxy"]だけなので、ヘッダーに書いても静かに失敗します。 - 落とし穴: HTTPプロキシとHTTPSプロキシの設定を同一視すること。HTTPプロキシURLからHTTPS先へは、互換ハンドラー経由でCONNECTトンネルを使うのが一般的ですが、プロキシエンドポイント自体に
https://スキームを使う設定は別物で、対応状況も限られます。混同しないでください。 - ヒント: SOCKS5 が必要なら、まずお使いのScrapyバージョンのダウンロードハンドラー機能を確認してください。Scrapy 2.17 の実験的な Httpx ハンドラーは
httpx[socks]経由で SOCKS5 をサポートしていますが、あくまで実験的です。本番依存には、必ず自前のテストを挟んでください。
代替手段
scrapy-rotating-proxies 以外にも、プロキシロジックをすべてプロバイダーのゲートウェイに任せる方法があります。これは、ベンダー側が裏でローテーション、セッション固定、ジオターゲティングを処理してくれる単一のプロキシURLを使う形です。ミドルウェアのコード量をかなり減らせる代わりに制御性は少し下がるので、自前でプールを作る前に見積もる価値があります。
まとめ
Scrapyでプロキシを設定するのは1行で済みます。しかし、その構成を実際の本番クロールで持ちこたえさせるには、明示的な失敗ポリシー、保護された認証情報、検証済みのハンドラー境界、そしてリトライ時にコピーされたプロキシメタデータを意図的に差し替えるセレクターが必要です。2つだけ持ち帰るなら、次の2つを習慣にしてください。プロキシが必須なら失敗時に直接接続へ逃がさないこと。そして、プロキシのパスワードをPythonファイルに決してコミットしないことです。
FAQ
認証付きのカスタムプロキシをScrapyでどう設定しますか?
protocol://username:password@host:port 形式を使いますが、ユーザー名やパスワードに特殊文字が含まれるなら、先に urllib.parse.quote() でパーセントエンコードしてください。本番では、認証情報をハードコードせず、環境変数から from_crawler クラスメソッドで読み込むのが推奨です。
Scrapyのカスタムプロキシミドルウェアには何番の優先順位を使うべきですか?
350は、HttpProxyMiddleware の750より前に動くため、よく使われるセレクター用の優先順位です。ただし、これでローテーションが保証されるわけではありません。リトライしたリクエストに新しいプロキシを割り当てるのは、セレクターがコピーされたリトライを認識し、古い meta["proxy"] を上書きした場合だけです。
Scrapyで死んだプロキシを自動処理するにはどうすればよいですか?
process_response と process_exception でプロキシごとの失敗回数を管理し、しきい値を超えたらアクティブプールから外し、永久停止ではなくクールダウン後に戻すミドルウェアを作成してください。
ScrapyでSOCKS5プロキシは使えますか?
Scrapy 2.17 の実験的な HttpxDownloadHandler は、httpx[socks] を入れると SOCKS5 をサポートすると記載されています。ただし、デフォルトのHTTP/1.1ハンドラーはSOCKSプロキシをサポートしません。ハンドラーとバージョンを固定し、統合テストを行ってから本番対応と考えてください。
プロキシをリトライ時に切り替える方法で、実際どれくらいコストを削減できますか?
移植可能なパーセンテージはありません。許可されたリクエストのうち、直接で有効コンテンツを返す割合、各プロキシ階層を通ったバイト数、成功1件あたりのリトライ回数、追加レイテンシを測定してください。プロキシ経由の転送量がどれだけ減ったかが、あなたの実際の節約です。直接優先のアクセスが許可されていない、または有効でないなら、この手法は使わないでください。


