PythonのHTTPライブラリは、プロキシをscheme://user:pass@host:port形式のURLとして受け取ります。違いはパラメータ名、クライアント単位かリクエスト単位か、SOCKS5とDNSの処理方法です。 以下のスニペットは、requests、httpx、aiohttpの現在のバージョンで動作する正確なコードです(それぞれ、この記事の公開前に認証付きプロキシを経由して実行済み)。その後に、「Pythonでプロキシが動かない」という質問の大半を占める3つのポイントを説明します。古いチュートリアルからコピーした間違ったパラメータ名、SOCKS拡張の欠落、そしてIPに関係なくスクリプトを見破るTLSフィンガープリントです。

プロキシURL

http://user:pass@proxy.example.com:8080     # HTTPプロキシ。http:// と https:// の両方のターゲットに使用
socks5://user:pass@proxy.example.com:1080   # SOCKS5。DNSはローカルで解決
socks5h://user:pass@proxy.example.com:1080  # SOCKS5。DNSはプロキシ側で解決(こちらを使用)

よくつまずく2つのポイント。まず、HTTPプロキシはHTTPSターゲットも処理します。クライアントはCONNECTを送信し、TLSをトンネリングします。したがって、https://スキームのプロキシはほとんど必要ありません。それはプロキシ自体へのTLSを意味し、提供しているプロバイダーはほとんどありません。次に、ユーザー名にローテーションパラメータ(user-country-de-session-abc123など)が含まれている場合や、パスワードに@、:、/が含まれている場合は、パーセントエンコード(urllib.parse.quote(pw, safe=''))してください。そうしないとURLが正しく解析されません。

requests

import requests

PROXY = "http://user:pass@proxy.example.com:8080"
proxies = {"http": PROXY, "https": PROXY}

# リクエストごと
r = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=20)
print(r.json())

# セッションごと(接続を再利用。通常はこちら)
s = requests.Session()
s.proxies.update(proxies)
r = s.get("https://api.ipify.org?format=json", timeout=20)

辞書のキーはターゲットのスキームです。http:// URLへのリクエストは"http"エントリを、https:// URLへのリクエストは"https"エントリを使用します。特別な理由がない限り、両方に同じプロキシを設定してください。常にtimeoutを指定しましょう。プロキシがハングすると、スクリプトも永久にハングします。

SOCKS5を使用するには、追加のインストールが必要です:pip install "requests[socks]"。その後、proxies = {"http": "socks5h://…", "https": "socks5h://…"}とします。追加インストールがないと、InvalidSchema: Missing dependencies for SOCKS supportエラーが発生します。

環境変数:requestsはデフォルトでHTTP_PROXY / HTTPS_PROXY / NO_PROXYを尊重します(trust_env=True)。設定していないのにプロキシが使われている、または設定したのに使われていない場合は、環境変数を確認してください。

httpx

import httpx

PROXY = "http://user:pass@proxy.example.com:8080"

# 同期
with httpx.Client(proxy=PROXY, timeout=20) as client:
    r = client.get("https://api.ipify.org?format=json")
    print(r.json())

# 非同期
import asyncio
async def main():
    async with httpx.AsyncClient(proxy=PROXY, timeout=20) as client:
        r = await client.get("https://api.ipify.org?format=json")
        print(r.json())
asyncio.run(main())

パラメータはproxy=(単数形)です。 古いチュートリアルではproxies={"http://": …, "https://": …}と書かれていますが、このキーワードはhttpx 0.26で非推奨となり、0.28で削除されました。現在渡すとTypeError: Client.__init__() got an unexpected keyword argument 'proxies'が発生します。ターゲットごとに異なるプロキシが必要な場合は、mountsを使用します:

mounts = {
    "https://api.example.com": httpx.HTTPTransport(proxy="http://user:pass@dc-proxy:8080"),
    "all://": httpx.HTTPTransport(proxy="http://user:pass@resi-proxy:8080"),
}
client = httpx.Client(mounts=mounts)

SOCKS5:pip install "httpx[socks]"を実行し、proxy="socks5://…"とします。httpxはデフォルトでSOCKSプロキシ経由でホスト名を解決します。HTTP/2:pip install "httpx[http2]"を実行し、httpx.Client(http2=True, …)とします。プロキシ経由のブラウザはHTTP/2を使用しているため、関連性があります(HTTP/2 and HTTP/3 through proxiesを参照)。

aiohttp

import aiohttp, asyncio

PROXY = "http://user:pass@proxy.example.com:8080"

async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.ipify.org?format=json",
                               proxy=PROXY,
                               timeout=aiohttp.ClientTimeout(total=20)) as r:
            print(await r.json())

asyncio.run(main())

プロキシはリクエストごとに渡します(またはカスタムコネクタで一度設定)。認証情報はURLに含めます。別のproxy_auth=aiohttp.BasicAuth(...)形式は3.xではまだ動作しますが、3.14で非推奨となり、4.0で削除予定です。URL形式を使用してください。aiohttpは、trust_env=Trueでセッションを作成しない限り、HTTP_PROXY環境変数を読み取りません。ネイティブのaiohttpはHTTPプロキシのみ対応です。SOCKSを使用する場合は、aiohttp-socksパッケージとそのProxyConnectorを使用してください。

ローテーションプロキシ:セッションIDパターン

バックコネクトゲートウェイ(ローテーションゲートウェイの仕組みを参照)を使用する場合、Pythonでローテーションする必要はほとんどありません。ユーザー名を変更することでローテーションします。新しいセッションIDは新しい出口IPを意味し、再利用すると同じIPが維持されます:

import secrets, requests

def proxy_for(session_id: str, country: str = "de") -> dict:
    user = f"customer-country-{country}-session-{session_id}"   # プロバイダー固有の形式
    url  = f"http://{user}:PASSWORD@gate.provider.com:7777"
    return {"http": url, "https": url}

# スティッキー:複数ステップのフローで同じIP
sid = secrets.token_hex(4)
s = requests.Session(); s.proxies.update(proxy_for(sid))
s.get("https://shop.example/login"); s.post("https://shop.example/cart", data=...)

# ローテーション:アイテムごとに新しいIP
for url in urls:
    requests.get(url, proxies=proxy_for(secrets.token_hex(4)), timeout=20)

失敗時にもローテーションします。403/429やCAPTCHAマーカーをキャッチし、新しいセッションIDと新しいクッキージャーで再試行します。同じIPとクッキーは同じ訪問者と見なされます。

どのプロキシも解決しない間違い:TLSフィンガープリント

3つのライブラリはすべて、Pythonのsslモジュール(OpenSSL)を介してTLS接続を行います。アンチボットシステムはそのハンドシェイクをフィンガープリントし、User-Agentをどのように設定しても、ブラウザではないことを認識します。完璧な住宅用IPの背後でも、スクリプトはスクリプトとして識別されます。その仕組みはTLSフィンガープリントの解説にあります。保護されたターゲットには、偽装クライアントに切り替えてください。curl_cffiはrequestsに似たAPIを提供します:

# pip install curl_cffi
from curl_cffi import requests as cffi_requests

r = cffi_requests.get("https://protected.example/",
                      impersonate="chrome",                 # ChromeのTLS + HTTP/2フィンガープリントを再現
                      proxies={"https": "http://user:pass@proxy.example.com:8080"},
                      timeout=20)

その後、他の要素も一貫させます。ChromeのUser-Agent、対応するsec-ch-uaクライアントヒント、プロキシの国に妥当なAccept-Language。JavaScriptチャレンジが必要なサイトでは、HTTPクライアントでは不十分です。Playwright、Puppeteer、Seleniumでのプロキシのように、実際のブラウザを使用してください。

エラーとその意味

表示されるエラー意味修正方法
ProxyError … 407 Proxy Authentication Required認証情報が間違っているか、IPがホワイトリストにないユーザー名/パスワードを確認(長いローテーション文字列!)、特殊文字をパーセントエンコード、ホワイトリストを確認
InvalidSchema: Missing dependencies for SOCKS supportrequestsにsocks拡張がないpip install "requests[socks]"
TypeError: … unexpected keyword argument 'proxies'httpx 0.28以上で削除されたパラメータを使用proxy=またはmounts=を使用
ProxyError … Cannot connect to proxy / ConnectTimeoutホスト/ポートが間違っている、プロキシがダウン、またはファイアウォールcurl -xで確認、別のプロトコルを試す
IPエコーエンドポイントが実際のIPを返すプロキシが適用されていない(辞書のキーが間違い、環境変数の上書き、aiohttpでリクエストごとのパラメータが欠落)session.proxiesを表示、trust_envを確認、リクエストにproxy=を渡す
プロキシ経由でのみSSLErrorプロキシがTLSを傍受(MITM)しているか、スキームが間違っているプロキシにはhttp://スキームを使用、プロバイダーにTLS終端の有無を確認
保護されたサイトで403/CAPTCHA、IPは正常に見えるTLS/HTTPフィンガープリントがPythonを識別curl_cffiのimpersonateを使用、または実際のブラウザを使用
しばらく動作してから429IPごとのレート制限に達したセッションIDをローテーション、速度を落とす、ローテーションガイドを参照

最小限の正しいテンプレート

import requests, secrets
from urllib.parse import quote

GATE, PORT = "gate.provider.com", 7777
USER_BASE, PASS = "customer-country-de", quote("p@ss:word", safe="")

def make_session() -> requests.Session:
    sid = secrets.token_hex(4)
    url = f"http://{USER_BASE}-session-{sid}:{PASS}@{GATE}:{PORT}"
    s = requests.Session()
    s.proxies.update({"http": url, "https": url})
    s.headers.update({"User-Agent": "python-requests"})   # 寛容なターゲットには正直なUA
    return s

s = make_session()
r = s.get("https://api.ipify.org?format=json", timeout=20)
r.raise_for_status()
print(r.json())

IPが変わることを確認してから、これを基に構築してください。完全な事前チェック(リーク、ヘッダー、ブロックリスト)についてはプロキシのテスト方法を、ターゲットに応じたデータセンター vs 住宅用プロキシの選択についてはプロキシタイプの比較を参照してください。

よくある質問

Pythonのrequestsでプロキシを使用するには?

リクエストにproxies={"http": url, "https": url}を渡すか、Sessionに設定します。urlはhttp://user:pass@host:port形式です。timeout=を追加してください。SOCKS5の場合はrequests[socks]をインストールし、socks5h://を使用します。

httpxで「unexpected keyword argument proxies」と表示されるのはなぜ?

proxies=がhttpx 0.28で削除されたためです。単一のプロキシにはproxy="http://…"を、ターゲットごとのプロキシにはmounts=を使用してください。

aiohttpでプロキシを設定するには?

各リクエストにproxy="http://user:pass@host:port"を渡します(session.get(url, proxy=…))。proxy_auth=パラメータは非推奨です。認証情報はURLに含めてください。SOCKSにはaiohttp-socksを使用します。

Pythonでプロキシをローテーションするには?

バックコネクトプロバイダーを使用する場合、リクエストまたはフローごとにプロキシユーザー名のセッションIDを変更します。IPリストは不要です。403/429でローテーションし、各新しいIPに新しいクッキージャーを組み合わせてください。

同じプロキシを使っているのに、ブラウザはブロックされずスクリプトだけブロックされるのはなぜ?

TLSとHTTP/2のフィンガープリントによるものです。Pythonのsslモジュールはブラウザのように見えません。impersonate=を指定したcurl_cffiを使用するか、実際のブラウザを自動化してください。

socks5とsocks5hのどちらを使うべき?

socks5hです。hはプロキシがホスト名を解決することを意味し、DNSリークを防ぎ、プロキシのみが解決できるホストに到達できます。