即使凭据正确,含有 @ 或 # 的密码也可能让手工拼接的代理 URL 出错。这些字符在 URL 中已有特定含义,客户端可能因此将部分密码解析成主机名或片段。

分别编码两个凭据字段

此示例保留 URL 的结构分隔符,只编码用户名和密码:

from urllib.parse import quote

username = "user@team"
password = "p:a/ss#?"
proxy_url = (
    "http://"
    + quote(username, safe="")
    + ":"
    + quote(password, safe="")
    + "@203.0.113.10:8080"
)

使用实际的端点与凭据。这里的地址是文档示例。safe 参数为空很重要:quote 通常不转义斜杠,但凭据字段中的斜杠需要编码。

不要编码整个代理 URL。若编码协议方案的分隔符及凭据与主机之间的 @,就会破坏原本想保留的结构。也不要对已编码的密码再次编码。

在 Requests 中使用结果

import requests

response = requests.get(
    "https://example.com/",
    proxies={"http": proxy_url, "https": proxy_url},
    timeout=(5, 15),
)
response.raise_for_status()

若请求仍失败,检查是代理拒绝了登录,还是客户端更早在解析或连接阶段失败。407 认证质询与无效 URL 是不同的故障。

HTTPX 可以将认证信息分开处理

HTTPX Proxy 对象可以分别接收端点与凭据参数:

import httpx

proxy = httpx.Proxy(
    "http://203.0.113.10:8080",
    auth=("user@team", "p:a/ss#?"),
)

with httpx.Client(proxy=proxy, trust_env=False, timeout=15) as client:
    response = client.get("https://example.com/")
    response.raise_for_status()

这避免了自行构造 userinfo 部分。它既不会独立于代理连接另行加密凭据,也不会为你选择一种所有服务都支持的认证方式。

如果免费列表只提供 IP 和端口,本示例并不提供默认凭据。只有运营方确实提供账户时才使用认证。不要将含凭据的 URL 写入共享日志和源文件。

相关文章