プロキシ経由でのクッキーの問題を修正する方法
クッキーはプロキシを使用する際の最も一般的なエラーの原因の1つです。セッションが切断され、認証が失敗し、データが失われます。この記事では、これが起こる理由と、安定した動作のためにクッキー処理を正しく設定する方法を説明します。
プロキシ使用時にクッキーが失われる理由
プロキシ経由でリクエストを送信すると、クライアントとターゲットサーバー間に中間ノードが挿入されます。これにより、いくつかの問題が発生します:
- 1つのセッションに対して異なるIPアドレス。 サーバーは異なるアドレスからリクエストが来ていることに気づき、クッキーを疑わしいものとして拒否する可能性があります。
- Set-Cookieヘッダーの損失。 プロキシの設定が不適切な場合、Set-Cookieヘッダーがクライアントに転送されない可能性があります。
- ドメインとパスの不一致。 プロキシがHostヘッダーを書き換える場合、ドメインの不一致によってクッキーが保存されない可能性があります。
- 状態保存の欠落。 クッキーを保存せずに各リクエストを個別に送信する場合、セッションは失われます。
クッキージャーとは何か、その使用方法
クッキージャーはクッキーを保存するリポジトリで、クッキーの送受信を自動的に管理します。各リクエストにCookieヘッダーを手動で追加する代わりに、ライブラリに自動的に行わせることができます。
ほとんどのHTTPクライアントには、クッキージャーの組み込みサポートがあります:
import requests
from requests.cookies import RequestsCookieJar
# クッキーを保存するジャーを作成
jar = RequestsCookieJar()
# 最初のリクエスト — サーバーはSet-Cookieを送信
response1 = requests.get(
'https://example.com/login',
cookies=jar,
proxies={'https': 'http://proxy.example.com:8080'}
)
# クッキーは自動的にjarに保存される
print(jar)
# 2番目のリクエスト — クッキーは自動的に送信される
response2 = requests.get(
'https://example.com/dashboard',
cookies=jar,
proxies={'https': 'http://proxy.example.com:8080'}
)
jarなしでは、Set-Cookieを手動で解析して次のリクエストに追加する必要があります。これは信頼性が低く、面倒です。
リクエスト間でのクッキーの保存
スクリプトが長時間実行される場合、または再起動後にセッションを復元する必要がある場合は、クッキーをファイルに保存します:
import requests
from http.cookiejar import LWPCookieJar
# ファイルに保存するジャーを作成
jar = LWPCookieJar('cookies.txt')
# 古いクッキーがあれば読み込む
try:
jar.load(ignore_discard=True, ignore_expires=True)
except FileNotFoundError:
pass
# リクエストでjarを使用
response = requests.get(
'https://example.com/login',
cookies=jar,
proxies={'https': 'http://proxy.example.com:8080'}
)
# 更新されたクッキーを保存
jar.save(ignore_discard=True, ignore_expires=True)
ignore_discard=Trueとignore_expires=Trueフラグを使用すると、一時的なクッキーも保存できます。
クッキーのドメインバインディングの問題
クッキーにはDomain属性があり、どのドメインに対して送信されるかを決定します。以下の場合に問題が発生します:
- プロキシがHostを書き換える。 プロキシがHostヘッダーを変更する場合、クッキージャーは別のドメインに属するクッキーとして拒否する可能性があります。
- サブドメインが一致しない。
example.comのクッキーはapi.example.comに送信されない可能性があります。 - パスが一致しない。
/apiのクッキーは/adminに送信されません。
クッキー属性を次のように確認します:
import requests
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy.example.com:8080'}
)
# すべてのクッキーを出力
for cookie in response.cookies:
print(f"Name: {cookie.name}")
print(f"Value: {cookie.value}")
print(f"Domain: {cookie.domain}")
print(f"Path: {cookie.path}")
print(f"Secure: {cookie.secure}")
print(f"HttpOnly: {cookie.has_nonstandard_attr('HttpOnly')}")
print("---")
Domainが狭すぎる場合は、自動管理の代わりに明示的にクッキーを指定してみてください:
headers = {
'Cookie': 'session_id=abc123; user_token=xyz789'
}
response = requests.get(
'https://example.com/api',
headers=headers,
proxies={'https': 'http://proxy.example.com:8080'}
)
SecureフラグとHttpOnlyフラグ
Secureフラグは、クッキーがHTTPSでのみ送信されることを意味します。HTTPプロキシを使用してHTTPSリソースにアクセスする場合は、プロキシへの接続が保護されているか、プロキシがHTTPSを正しく転送していることを確認してください。
HttpOnlyフラグはJavaScriptからのクッキーアクセスを禁止します。これはリクエストでのクッキー送信には影響しませんが、ブラウザからそのようなクッキーを読み取ることができないことに注意してください。
レジデンシャルプロキシを使用する場合は、以下を確認してください:
- プロキシはHTTPS(CONNECTメソッド)をサポートしている
- 証明書が有効である(本番環境では
verify=Falseを使用しない) - ヘッダーがプロキシによって書き換えられていない
コード例を含む実践的な例
例1: セッション保存によるログイン
import requests
from requests.cookies import RequestsCookieJar
jar = RequestsCookieJar()
proxy = 'http://proxy.example.com:8080'
# ログイン
login_response = requests.post(
'https://example.com/login',
data={'username': 'user', 'password': 'pass'},
cookies=jar,
proxies={'https': proxy}
)
if login_response.status_code == 200:
print("ログイン成功")
# 保存されたセッションを使用
dashboard = requests.get(
'https://example.com/dashboard',
cookies=jar,
proxies={'https': proxy}
)
print(dashboard.text)
例2: 複数のリクエストの処理
import requests
from http.cookiejar import LWPCookieJar
import time
jar = LWPCookieJar('session.txt')
try:
jar.load(ignore_discard=True)
except:
pass
proxy = 'http://proxy.example.com:8080'
urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3'
]
for url in urls:
response = requests.get(
url,
cookies=jar,
proxies={'https': proxy},
timeout=10
)
print(f"{url}: {response.status_code}")
jar.save(ignore_discard=True)
time.sleep(1) # サーバーに過負荷をかけない
例3: 問題発生時の明示的なクッキー転送
import requests
proxy = 'http://proxy.example.com:8080'
# 自動管理が機能しない場合
cookies_dict = {
'session_id': 'abc123def456',
'user_pref': 'dark_mode'
}
headers = {
'User-Agent': 'Mozilla/5.0...',
'Cookie': '; '.join([f"{k}={v}" for k, v in cookies_dict.items()])
}
response = requests.get(
'https://example.com/api/data',
headers=headers,
proxies={'https': proxy}
)
print(response.json())
クッキーの問題のデバッグ
クッキーが機能しない場合は、これらのツールを使用してください:
| ツール | 目的 |
|---|---|
requests.Session |
セッション内のすべてのリクエストのクッキーを自動的に管理 |
logging |
requestsのDEBUGを有効にして、すべてのヘッダーを表示 |
Fiddler / Charles |
トラフィックをインターセプトして、Set-CookieとCookieヘッダーを確認 |
curl -v |
コマンドラインからプロキシ経由で同じことをテスト |
デバッグのためにログを有効にします:
import logging
import requests
logging.basicConfig(level=logging.DEBUG)
# これですべてのリクエストがヘッダーとクッキーを出力
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy.example.com:8080'}
)
プロキシがクッキーをブロックしていないか確認します:
curl -v -x http://proxy.example.com:8080 https://example.com
# ヘッダーを確認:
# Set-Cookie: ... (存在する必要がある)
# Cookie: ... (次のリクエストで送信される必要がある)
ヒント: レジデンシャルプロキシを使用する場合、リクエスト間でIPがローテーションされる可能性があることに注意してください。クッキー処理ロジックがこれを考慮していることを確認してください。一部のサーバーは同じセッション内の異なるIPからのリクエストを拒否します。
まとめ
プロキシ経由でのクッキーの問題は、適切な設定で解決できます:
- クッキージャーを使用して、クッキーを自動的に管理する
- リクエスト間でクッキーをファイルに保存する
- ドメインバインディングとPath属性を確認する
- プロキシのHTTPS対応を確認する
- デバッグを使用して問題を特定する
自動化とパースのタスクで、プロキシ経由でセッション管理が必要な場合は、レジデンシャルプロキシがHTTPSサポートとクッキー管理に適しています。シンプルなクッキージャーから始めて、必要な場合のみより複雑なスキームに移行してください。