Scrapy のプロキシ middleware:完全な設定ガイド(2026)

2026年6月4日公開 · 約11分で読めます

Scrapy は 2026 年になっても本番クロールの主力であり続けています — そして相変わらず、プロキシの設定で最も人を混乱させるフレームワークでもあります。なぜなら、プロキシを差し込める場所が 4 つあり、そのうち 3 つはほとんどのプロジェクトにとって誤りだからです。本ガイドが示すのは正しい 1 つ:リクエスト単位のルーティング、sticky セッション、ban 検出、そして妥当なリトライ挙動を備えた、小さなカスタム middleware です。

素の requests/httpx/aiohttp を使っているなら、Python でプロキシをローテーションする方法を参照してください。本稿は Scrapy 特化です。

30 秒版

ローテーティング住宅ゲートウェイの場合、最小限の構成はリクエストごとに 1 行 — middleware は不要です:

def start_requests(self):
    for url in self.urls:
        yield scrapy.Request(
            url,
            meta={"proxy": "http://USERNAME:[email protected]:913"},
        )

Scrapy 組み込みの HttpProxyMiddlewarerequest.meta["proxy"] を読み取り、URL からの認証を処理します。ゲートウェイがあなたのために出口 IP をローテーションします。それだけで十分ならここで終わりです。本ガイドの残りは、制御が必要な場合のためのものです:sticky セッション、国別ルーティング、ban を意識したローテーション、そして並行数のチューニング。

本番向けプロキシ middleware

これを middlewares.py に置いてください。ドメインごとに sticky セッションを割り当て、ban 時にローテーションし、どのセッションが何を取得したかをデバッグできるよう全リクエストにタグを付けます:

import random
import string

GATEWAY = "us.jibaoproxy.com:913"
USERNAME = "USERNAME"          # move to settings.py / env in real projects
PASSWORD = "PASSWORD"

def _new_session(n=8):
    return "".join(random.choices(string.ascii_lowercase + string.digits, k=n))

class JibaoProxyMiddleware:
    """Sticky session per domain; rotate session on ban."""

    def __init__(self):
        self.sessions = {}          # domain -> session id

    def _proxy_url(self, session_id):
        user = f"{USERNAME}-session-{session_id}"
        return f"http://{user}:{PASSWORD}@{GATEWAY}"

    def process_request(self, request, spider):
        domain = request.url.split("/")[2]
        session = self.sessions.setdefault(domain, _new_session())
        request.meta["proxy"] = self._proxy_url(session)
        request.meta["proxy_session"] = session

    def rotate(self, domain):
        """Call when a session is burned."""
        self.sessions[domain] = _new_session()

そして、ban を検出して新しいセッションでリトライする、対になる downloader middleware:

from scrapy.downloadermiddlewares.retry import RetryMiddleware
from scrapy.utils.response import response_status_message

BAN_CODES = {403, 429}
BAN_MARKERS = (b"captcha", b"access denied", b"unusual traffic")

class BanAwareRetryMiddleware(RetryMiddleware):

    def process_response(self, request, response, spider):
        banned = (
            response.status in BAN_CODES
            or any(m in response.body[:2048].lower() for m in BAN_MARKERS)
        )
        if banned:
            domain = request.url.split("/")[2]
            proxy_mw = spider.crawler.engine.downloader.middleware.middlewares
            for mw in proxy_mw:
                if hasattr(mw, "rotate"):
                    mw.rotate(domain)        # burn the session
            reason = response_status_message(response.status)
            return self._retry(request, reason, spider) or response
        return super().process_response(request, response, spider)

両方を settings.py で配線します:

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.JibaoProxyMiddleware": 350,
    "scrapy.downloadermiddlewares.retry.RetryMiddleware": None,   # replace stock retry
    "myproject.middlewares.BanAwareRetryMiddleware": 550,
}
RETRY_TIMES = 2

優先順位が重要です:プロキシ middleware は Scrapy の HttpProxyMiddleware(750)より前に実行されなければなりません。したがって 750 未満なら何でも機能します。350 にしておけば早期かつ予測可能な位置に保てます。

Sticky か Rotating か:どの spider にどのモードか

クロールの種類モード実装
ステートレスなページ収集Rotating素の username、ゲートウェイがリクエストごとにローテーション
ログイン+認証下のクロールアカウントごとに sticky-session-{account_id}、ログイン中は決してローテーションしない
ページネーションの多いリストドメインごとに sticky、ban 時にローテーション上記の middleware
地域別の価格設定Rotating +国の固定USERNAME-country-de 形式のパラメータ

このトレードオフのより深い扱い:Sticky vs Rotating プロキシセッション

ban されない並行数の設定

Scrapy のデフォルトは、礼儀正しい単一 IP クロール向けに調整されています。ローテーティングプールの背後では、もっと強く攻められます — しかしドメインごとの上限は依然として重要です。なぜならターゲットが見るのは集約された挙動だからです:

# settings.py - sane starting point behind a residential pool
CONCURRENT_REQUESTS = 64
CONCURRENT_REQUESTS_PER_DOMAIN = 8     # what the target experiences
DOWNLOAD_DELAY = 0.25                  # jitter applied per slot
RANDOMIZE_DOWNLOAD_DELAY = True        # 0.5x-1.5x the delay
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_TARGET_CONCURRENCY = 6.0
DOWNLOAD_TIMEOUT = 30
ROBOTSTXT_OBEY = True

CONCURRENT_REQUESTS_PER_DOMAIN を引き上げるのは、現在のレベルで数千リクエスト分の 403 率を観察してからにしてください。「どうせプロキシがローテーションするから」と 8 → 32 にするのは、人々がリトライで GB を焼き尽くす典型的なやり方です。

帯域の規律(住宅 GB はお金です)

よくある失敗と、それが実際に意味すること

407 Proxy Authentication Required

認証情報がプロキシに届いていません。meta["proxy"] の URL(http://user:pass@host:port)に入れてください — Scrapy がパースして Proxy-Authorization を設定してくれます。ヘッダーを手動で設定し、かつ URL 認証情報も使うと二重認証の奇妙な挙動が起きます。どちらか一方を選んでください。

TunnelError: Could not open CONNECT tunnel

ほぼ常に、ホスト/ポートのタイプミスか、そのポートで CONNECT を許可しないエンドポイント経由の HTTPS ターゲットです。まず Scrapy の外で curl -x を使って検証してください。

spider は 10 分動いて、その後すべてが 403

あなたの sticky セッションが歓迎される期間を超えたか、ドメインごとのレートが熱すぎます。上記の ban を意識した middleware は前者を処理します。後者には CONCURRENT_REQUESTS_PER_DOMAIN を下げてください。JA4 をチェックするターゲットなら、Scrapy の TLS スタックそのものが正体を暴いているかもしれません — なぜプロキシではそれが直らないのかは JA3/JA4 の解説を参照してください。

無料ツール · 登録不要

クロール前にプロキシリストを検証する

当社の Proxy Checker にエンドポイントを貼り付けてください:接続性、レイテンシ、匿名レベル、出口 IP タイプを一括でテストします — Scrapy がリトライを無駄にする前に、死んでいる、あるいは誤ってラベル付けされたプロキシを見つけられます。

プロキシをチェックする →

無料リストの子守りに疲れましたか?1 つの住宅ゲートウェイがそのすべてを置き換えます — 500MB の無料トラフィックを受け取る →

まとめ

spider を本物のプールに向けよう

1 つのゲートウェイでローテーティングと sticky の住宅セッション。クロールに使える 500MB の無料トラフィック。

無料トライアルを始める

すべてのIP製品に対応 · 膨大なノードプールをいつでも利用可能

今すぐ登録して、チャージ額の最大 100% をキャッシュバック

新規ユーザーは登録時に500MBを獲得、さらに初回チャージにボーナスが付きます。期間限定オファーです。