每个Python HTTP库都将代理视为形如scheme://user:pass@host:port的URL;不同之处在于参数名、是每个客户端还是每个请求设置,以及SOCKS5和DNS的处理方式。以下代码片段是当前版本的requests、httpx和aiohttp可用的确切代码——每段代码在本文发布前都通过认证代理运行过——随后是导致大多数“我的代理在Python中不起作用”问题的三个原因:从旧教程复制的错误参数名、缺少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由代理解析(推荐使用)
有两个容易出错的地方。首先,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和HTTP/3)。
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不读取HTTP_PROXY环境变量,除非你使用trust_env=True创建会话。原生aiohttp仅支持HTTP代理;对于SOCKS,请使用aiohttp-socks包及其ProxyConnector。
轮换代理:会话ID模式
使用回连网关(参见轮换网关的工作原理),你很少需要在Python中轮换——你通过更改用户名来轮换。新的会话ID意味着新的出口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和新的cookie jar重试——相同IP和相同cookie意味着同一访客。
任何代理都无法修复的错误:TLS指纹
所有三个库都通过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 support | requests缺少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,或使用真实浏览器 |
| 工作一段时间后出现429 | 达到每IP速率限制 | 轮换会话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是否变化,然后在此基础上构建。有关完整的预检(泄漏、头部、黑名单),请参见如何测试代理;有关为特定目标选择数据中心还是住宅代理,请参见代理类型比较。
常见问题
如何在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与新的cookie jar配对。
为什么我的脚本被阻止,而同一代理上的浏览器没有被阻止?
TLS和HTTP/2指纹识别:Python的ssl模块看起来不像浏览器。使用curl_cffi的impersonate=或自动化真实浏览器。
我应该使用socks5还是socks5h?
socks5h。h使代理解析主机名,防止DNS泄漏,并允许你访问只有代理才能解析的主机。