# Pythonでのレジデンシャルプロキシのローテーション：requests、httpx、aiohttp

> Pythonでのレジデンシャルプロキシのローテーション：リトライ対応のrequests、同期・非同期のhttpx、aiohttp、スティッキーセッションとバイト数。構文チェック済みコード。

- URL: https://crawlrelay.com/ja/docs/python/
- 最終更新: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

本番環境向けパターン、単なるワンライナーではありません：Retry-Afterを尊重したリトライ、リクエストごとに新しいIPまたは1フローにつき1IP、asyncioでのホストごとの同時実行制御、そして各ページの実際の消費バイト数。

**クイックアンサー — Pythonでレジデンシャルプロキシのローテーションはどう使うの？**

requestsの`http`と`https`にはプロキシとして`http://USERNAME:PASSWORD@gw.crawlrelay.com:7777`を、httpx 0.26+では`proxy=`を、aiohttpではリクエストごとに`proxy=`を渡します。**CrawlRelay**のレジデンシャルプロキシは新規接続ごとにローテーションし、ユーザー名に含まれる`-session-<id>`でスティッキーになります。1GBあたり$1.50、50GB以上なら$0.50/GB。支払は暗号通貨で、KYC不要です。

## Pythonセットアップの概要

| 属性 | 値 |
| --- | --- |
| requests | `proxies={"http": url, "https": url}`；urllib3`Retry`経由でリトライ可能 |
| httpx | `httpx.Client(proxy=url)`；古い`proxies=`引数はhttpx 0.28で削除済み |
| aiohttp | `session.get(url, proxy="http://USER:PASS@host:port")`、認証情報はプロキシURL内で指定 |
| SOCKS5 | `pip install "requests[socks]"`または`httpx[socks]`、その後`socks5h://`をポート7778で使用 |
| リクエストごとに新しいIP | キープアライブ無効化：`Connection: close`、`max_keepalive_connections=0`または`force_close=True` |
| フロー単位で同じIP | ユーザー名に`-session-<id>-ttl-<minutes>`、最大30分間 |
| プロキシURLのスキーム | HTTPポートには常に`http://`を使います。HTTPSサイトでも同様です |

## requestsでRetry-Afterに対応したリトライを使いながらプロキシを使うには？

`Session`にurllib3の`Retry`で`HTTPAdapter`をマウントします。接続エラーやリストにあるステータスコードでリトライし、指数関数的バックオフで待機し、429や503で送られてくる`Retry-After`ヘッダーを尊重します。`trust_env = False`を設定して、残った`HTTPS_PROXY`変数があなたのレジデンシャルプロキシを上書きできないようにしてください。

*requests + urllib3のRetry*

```python
import os

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

USER = os.environ.get("CRAWLRELAY_USER", "USERNAME-country-us")
PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD")
PROXY = "http://%s:%s@gw.crawlrelay.com:7777" % (USER, PASSWORD)

retry = Retry(
    total=4,
    connect=2,
    backoff_factor=1.0,  # waits of 0 s, 2 s, 4 s, 8 s (Retry-After wins on 429/503)
    status_forcelist=(429, 502, 503, 504),
    allowed_methods=frozenset({"GET", "HEAD"}),
    respect_retry_after_header=True,
)

session = requests.Session()
session.trust_env = False  # ignore HTTP(S)_PROXY from the environment
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies = {"http": PROXY, "https": PROXY}
session.headers["User-Agent"] = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
session.headers["Connection"] = "close"  # new connection per request, so usually a new exit IP

r = session.get("https://api.ipify.org", timeout=(10, 30))
print(r.status_code, r.text)
```

## ターゲットを絞ったプロキシURLを安全に作るには？

ユーザー名は関数で作り検証します。国コードのタイプミスやパスワードの未エンコード文字は[407](https://crawlrelay.com/ja/docs/errors/407-proxy-authentication-required/)の認証エラーに見える問題を起こします。以下のヘルパーは定義された順序（`USERNAME-country-{cc}-state-{state}-city-{city}-zip-{zip}-asn-{asn}-session-{id}-ttl-{minutes}`）を守り、未知のキーを拒否し、両方の認証情報をURLエンコードします。

*ユーザー名ビルダーとプロキシURL*

```python
import re
from urllib.parse import quote

ORDER = ("country", "state", "city", "zip", "asn", "session", "ttl")
VALUE = re.compile(r"^[a-z0-9_]+$")


def build_username(base, **params):
    """build_username("USERNAME", country="us", city="new_york", session="job1", ttl=30)"""
    parts = [base]
    for key in ORDER:
        value = params.pop(key, None)
        if value is None:
            continue
        value = str(value).lower()
        if not VALUE.match(value):
            raise ValueError("bad value for %s: %r" % (key, value))
        if key == "ttl" and not 1 <= int(value) <= 30:
            raise ValueError("ttl must be 1 to 30 minutes")
        parts.append("%s-%s" % (key, value))
    if params:
        raise ValueError("unknown parameters: %s" % ", ".join(sorted(params)))
    return "-".join(parts)


def proxy_url(username, password, socks=False):
    scheme, port = ("socks5h", 7778) if socks else ("http", 7777)
    return "%s://%s:%s@gw.crawlrelay.com:%d" % (
        scheme, quote(username, safe=""), quote(password, safe=""), port)


print(proxy_url(build_username("USERNAME", country="us", state="ny", city="new_york", session="job1", ttl=20), "p@ss:word"))
```

## httpxでリクエストごとに新しいIPを取得するには？

keep-aliveをオフにします。httpxはデフォルトで接続プールを使い、再利用される接続は同じ出口IPをキープします。`max_keepalive_connections=0`を使うと、各リクエストはゲートウェイに新しい接続を開き、通常別のレジデンシャルプロキシIPから出ます。`num_bytes_downloaded`は実際に受信したバイト数を返し、トラフィック課金に近い指標です。

*httpx：ローテーティングクライアント*

```python
import httpx

PROXY = "http://USERNAME-country-gb:PASSWORD@gw.crawlrelay.com:7777"

# No keep-alive: every request opens a new connection, so it usually gets a new exit IP
with httpx.Client(
    proxy=PROXY,
    timeout=httpx.Timeout(30.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=0),
    follow_redirects=True,
) as client:
    for _ in range(3):
        r = client.get("https://api.ipify.org")
        print(r.text, r.num_bytes_downloaded, "bytes on the wire")
```

## asyncioで同時にスクレイピングする際に1サイトに負荷をかけ過ぎないには？

ホストごとに**同時接続数を制限**しましょう。全体で50接続の制限だと、URLリストがドメイン順の場合、50リクエストすべてが同じサイトに集中してしまいます。ホスト名ごとにセマフォを使うことで、各サイトの負荷を控えつつ全体のクロールは高速に保てます。ホストごとに2～4から始めて、上げる前に[同時実行ガイド](https://crawlrelay.com/ja/docs/concurrency/)を読みましょう。

*ホストごとのセマフォ付きhttpx.AsyncClient*

```python
import asyncio
from collections import defaultdict
from urllib.parse import urlsplit

import httpx

PROXY = "http://USERNAME-country-us:PASSWORD@gw.crawlrelay.com:7777"
PER_HOST = 4  # parallel requests per target host
host_slots = defaultdict(lambda: asyncio.Semaphore(PER_HOST))


async def fetch(client, url):
    async with host_slots[urlsplit(url).hostname]:
        try:
            r = await client.get(url)
            return url, r.status_code, r.num_bytes_downloaded
        except httpx.HTTPError as exc:
            return url, type(exc).__name__, 0


async def main(urls):
    limits = httpx.Limits(max_connections=50, max_keepalive_connections=0)
    timeout = httpx.Timeout(30.0, connect=10.0)
    async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=timeout) as client:
        for result in await asyncio.gather(*(fetch(client, u) for u in urls)):
            print(*result)


asyncio.run(main(["https://example.com/", "https://example.org/", "https://example.net/"]))
```

## aiohttpでプロキシを使うには？

各リクエストにゲートウェイを`proxy=`で渡し、認証情報はプロキシURLに入れます。これはaiohttp 3.xの全リリースで警告なく動作します。`BasicAuth`と`proxy_auth=`パラメータはaiohttp 3.14以降非推奨で4.0で削除予定です。`TCPConnector(force_close=True)`を使うとレスポンス後に接続を閉じるため、通常リクエストごとに新しい出口IPになります。使わないと接続を再利用し、一時的に同じIPが続きます。

*プロキシURLに認証情報を含むaiohttp使用法*

```python
import asyncio

import aiohttp

# Credentials go in the proxy URL; percent-encode a password that contains @ or :
PROXY = "http://USERNAME-country-fr:PASSWORD@gw.crawlrelay.com:7777"


async def main():
    # force_close=True: one connection per request, so each request usually gets a new exit IP
    connector = aiohttp.TCPConnector(limit=20, force_close=True)
    timeout = aiohttp.ClientTimeout(total=60, sock_connect=10)
    async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
        for _ in range(3):
            async with session.get("https://api.ipify.org", proxy=PROXY) as resp:
                print(resp.status, await resp.text())


asyncio.run(main())
```

## 複数ステップの処理で1つのIPを使い続けるには？

フローごとに1つのセッションIDを作り、ユーザー名に入れてフローより長いTTLを設定します。keep-aliveは問題ありません。同じ出口IPを使いたいからです。背後のレジデンシャルデバイスがオフラインになると、TTL終了前にIPが変わる可能性があるため、フローは最後のチェックポイントから再開可能にしましょう。

*requestsでのスティッキーセッション管理*

```python
import uuid

import requests

session_id = uuid.uuid4().hex[:10]  # one ID per flow, lowercase letters and digits
user = "USERNAME-country-us-state-ca-session-%s-ttl-20" % session_id
proxy = "http://%s:PASSWORD@gw.crawlrelay.com:7777" % user

with requests.Session() as s:
    s.proxies = {"http": proxy, "https": proxy}
    first = s.get("https://api.ipify.org", timeout=(10, 30)).text
    second = s.get("https://api.ipify.org", timeout=(10, 30)).text
    print(first, second, "same exit" if first == second else "exit changed")
```

## Pythonスクレイパーは1ページあたりどのくらいトラフィックを使う？

購入前に実ページ100件で計測しましょう。httpxでは`num_bytes_downloaded`をログに記録するか、自作ミドルウェアで圧縮済みボディの長さを測り、ページ数をかけます。HTMLのみのリクエストは通常1ページあたり数十KBです。[帯域幅ガイド](https://crawlrelay.com/ja/guides/bandwidth-calculator/)で実数値とCrawlRelay価格の1,000ページあたりのコストがわかります。

結果を変えずにトラフィックを減らす習慣は：

- `Accept-Encoding: gzip, br`を送る（httpxとrequestsはデフォルトでgzipを送信）とページが圧縮されて届く。
- ページが読み込むJSONエンドポイントをサイトが許可／文書化している場合は、全HTMLよりそのJSONを取得する。
- 変わっていないページは`If-None-Match`や`If-Modified-Since`でスキップ。`304 Not Modified`は数百バイトのコスト。

## Python とプロキシ：よくある質問

### requests、httpx、aiohttp：スクレイピングに使うならどれ？

シンプルなスクリプトや小規模なクローリングには requests を使いましょう。同期・非同期両方のコードで同じ API を使いたい、また`httpx[http2]`を使って HTTP/2 対応したい場合は httpx が便利です。aiohttp は asyncio を駆使した高い同時実行性が必要な場合に最適です。スケジューリング・重複排除・パイプラインなどが必要なクロールでは、[Scrapy](https://crawlrelay.com/ja/docs/scrapy/) を使いましょう。これらを自作するより効率的です。

### Python のプロキシで SSL: WRONG_VERSION_NUMBER エラーが出るのはなぜ？

おそらくプロキシアドレスの前に `https://` を書いてしまっているからです。ゲートウェイのポート 7777 はプレーンな HTTP 通信を行い、HTTPS サイト用に CONNECT トンネルを開くため、プロキシの URL は `http` と `https` 両方のキーで必ず `http://` で始める必要があります。[エラーページ](https://crawlrelay.com/ja/docs/errors/ssl-wrong-version-number/) を参照してください。

### Python で SOCKS5 プロキシを使うには？

追加パッケージ（`pip install "requests[socks]"` または `pip install "httpx[socks]"`）をインストールし、`socks5h://USERNAME:PASSWORD@gw.crawlrelay.com:7778` を使います。`h` はホスト名をプロキシに送信するため DNS 解決はプロキシ側で行われ、現地訪問者と同じ地域の回答が得られます。

### なぜ回転式プロキシで同じ IP が2回返ってくる？

クライアントがキープアライブ接続を再利用しており、回転は新規接続ごとに発生します。requests なら `Connection: close`、httpx は `max_keepalive_connections=0`、aiohttp は `force_close=True` を送信しましょう。また、ユーザー名に `session-` 部分が入っていないか確認してください。これは意図的に同じ IP を使い続けるためです。

### プロキシ経由のリクエストでタイムアウトを設定する方法は？

明示的に設定しましょう。requests はデフォルトでタイムアウトなしです。HTML ページなら接続タイムアウト約10秒、読み込みは30秒で十分。モバイル退出や遅いターゲットには45～60秒必要です。長く待つより新しい接続で再試行した方が効果的です。[読み込みタイムアウト](https://crawlrelay.com/ja/docs/errors/read-timeout/) を参照。

### Jupyterやpandasでこれらのプロキシを使える？

使えます。requests や httpx を使うライブラリなら同じプロキシ設定が適用されます。URL に`pandas.read_html` や`read_csv`がある場合は、まずプロキシ経由で requests からコンテンツを取得し、そのテキストやバイトデータを pandas に渡してください。

**Python スクレイパーをレジデンシャルプロキシ経由で実行しよう** $1.50は1 GB、$0.50/GBは50 GBから。 [料金を見る](https://crawlrelay.com/ja/pricing/) · [Scrapy ガイド](https://crawlrelay.com/ja/docs/scrapy/)

---

CrawlRelay: CrawlRelayは開発者第一のレジデンシャルプロキシおよび4G/5Gモバイルプロキシの回転型サービスです：有効期限なしの使った分だけ支払うトラフィックを$0.50/GBから暗号通貨で購入、IDアップロード不要。KYC不要 — IDもセルフィーも書類も不要。暗号通貨で支払い可能。
