← 返回博客

Python请求:ProxyError、SSLError WRONG_VERSION_NUMBER和407的实际修复方法

详细分析在Python requests中使用代理时常见的错误:ProxyError、SSLError WRONG_VERSION_NUMBER、407代理身份验证所需。每种情况的工作代码示例。

📅2026年11月11日

如果您在 Python 中通过代理解析网站或自动化请求,您迟早会遇到三种经典错误: ProxyError、 SSLError: WRONG_VERSION_NUMBER 和 HTTP 407 Proxy Authentication Required。在本文中,我们将分析 每个错误的原因,并展示有效的代码来修复它们。

requests 如何与代理协作:基本语法

requests 库通过字典 proxies 接受代理,其中键是请求的协议 (http 和 https), 值是带有可选授权的代理服务器 URL。

import requests

proxies = {
    "http": "http://user:password@proxy_host:port",
    "https": "http://user:password@proxy_host:port",
}

response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.json())

请注意:即使是 HTTPS 请求,代理的协议通常也指定为 http://,因为大多数代理服务器通过 HTTP 协议接受 CONNECT 隧道,而不是将其封装在 SSL 中。如果指定 https:// 作为不支持此功能的代理,您将 遇到 SSLError,其分析将在下文中进行。

如果您使用 SOCKS5 代理,则需要额外的包:

pip install requests[socks]

# 代理的语法也会有所变化:
proxies = {
    "http": "socks5://user:password@proxy_host:port",
    "https": "socks5://user:password@proxy_host:port",
}

ProxyError:原因和解决方案

requests.exceptions.ProxyError 是低级网络异常的封装, 当尝试与代理服务器建立连接时会发生。最常见的情况有四种。

1. 代理不可用或地址/端口不正确

requests.exceptions.ProxyError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by ProxyError('Cannot connect to proxy.',
ConnectionResetError(104, 'Connection reset by peer')))

请检查主机和端口是否输入正确,并确保代理服务器确实在运行。最快的诊断方法是 尝试通过 curl 连接:

curl -x http://user:password@proxy_host:port https://httpbin.org/ip -v

2. 代理阻止了您的请求(IP 或 User-Agent 封禁)

数据中心代理常常因其 IP 范围被反欺诈系统识别而被目标网站封禁。 如果在访问特定网站(例如 Wildberries、Ozon 或社交媒体)时错误稳定出现,而不是在所有网站上同时出现, 那么问题不在于代理本身,而在于其声誉。在这种情况下,切换到 住宅代理 会有所帮助——它们使用 真实用户的 IP,较少受到反欺诈系统的封锁。

3. 超过了并发连接限制

如果您运行多线程解析并通过同一个代理打开数百个连接,服务器可能会开始重置多余的连接。解决方案是使用 requests.adapters.HTTPAdapter 通过限制 pool_maxsize 和 max_retries 来使用连接池:

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

session = requests.Session()
retries = Retry(total=3, backoff_factor=1, status_forcelist=[502, 503, 504])
adapter = HTTPAdapter(max_retries=retries, pool_connections=20, pool_maxsize=20)

session.mount("http://", adapter)
session.mount("https://", adapter)

response = session.get("https://httpbin.org/ip", proxies=proxies, timeout=10)

4. 代理服务器超时

如果代理“挂起”超过设定的超时,您将看到 ProxyError,被包装在 ReadTimeoutError 中。将超时增加到合理的值 (通常 15-30 秒足够)并单独添加异常处理:

try:
    response = requests.get(url, proxies=proxies, timeout=20)
except requests.exceptions.ProxyError as e:
    print(f"代理问题:{e}")
except requests.exceptions.Timeout:
    print("代理在规定时间内没有响应")

SSLError WRONG_VERSION_NUMBER:问题分析

这个错误乍一看是最难理解的,但实际上几乎总是有一个原因。

requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443):
Max retries exceeded with url: / (Caused by SSLError(SSLError(1, '[SSL:
WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1123)')))

WRONG_VERSION_NUMBER 表示客户端试图在服务器期望普通 HTTP 流量(未加密)的地方执行 SSL/TLS 握手。最常见的原因是 proxies 字典中指定的代理协议不正确。

常见错误: 在代理服务器地址中指定 https:// 而不是 http://。大多数 HTTP(S) 代理即使在通过 CONNECT 机制代理 HTTPS 流量时也会通过普通 HTTP 接受连接。

# 错误 — 将导致 WRONG_VERSION_NUMBER
proxies = {
    "http": "https://user:password@proxy_host:port",
    "https": "https://user:password@proxy_host:port",
}

# 正确
proxies = {
    "http": "http://user:password@proxy_host:port",
    "https": "http://user:password@proxy_host:port",
}

第二个常见原因是物理上不支持 TLS 的端口。例如,如果代理提供商仅为 HTTP 流量分配了端口 8080,而您试图直接在其上建立 TLS 连接(绕过 CONNECT), 服务器将以普通文本响应,而不是 TLS 握手,OpenSSL 客户端库将返回此错误。 请检查您的代理提供商的文档,了解每个端口可用的端口和协议(HTTP/HTTPS/SOCKS5)。

第三个选项是您使用 socket 或第三方库直接与代理建立 TCP 连接,绕过标准的 requests 机制。在这种情况下,请确保 TLS 封装仅应用于实际目标网站,而不是与代理的连接。

407 Proxy Authentication Required:授权

响应代码 407 是标准 401 的类似物,但适用于代理服务器。它表示服务器要求 授权,而您要么没有提供,要么以错误的格式提供。

方法 1:直接在 URL 中输入用户名和密码

proxies = {
    "http": "http://login:password@proxy_host:port",
    "https": "http://login:password@proxy_host:port",
}

response = requests.get("https://httpbin.org/ip", proxies=proxies)

这种方法最简单,但如果用户名或密码中包含特殊字符(例如 @, :, /), 需要通过 urllib.parse.quote 进行转义:

from urllib.parse import quote

login = quote("user@domain")
password = quote("p@ssw:ord!")

proxy_url = f"http://{login}:{password}@proxy_host:port"
proxies = {"http": proxy_url, "https": proxy_url}

方法 2:手动设置 Proxy-Authorization 头

如果代理提供商使用非标准授权方案或您需要更多控制,可以通过 base64 编码手动构建头:

import base64
import requests

login = "user"
password = "password"
credentials = base64.b64encode(f"{login}:{password}".encode()).decode()

headers = {
    "Proxy-Authorization": f"Basic {credentials}"
}

proxies = {
    "http": "http://proxy_host:port",
    "https": "http://proxy_host:port",
}

response = requests.get(
    "https://httpbin.org/ip",
    proxies=proxies,
    headers=headers,
    timeout=10
)

重要提示:某些代理提供商将授权绑定到特定的静态客户端 IP(白名单)。如果您更改 IP(例如,通过 VPN 或动态 IP 提供商工作),407 错误 可能会在凭证正确的情况下出现——因为您当前的外部 IP 未在代理的白名单中。解决方案是登录到提供商的个人账户并更新允许的 IP 列表,或者切换到 通过用户名/密码而不是 IP 白名单进行授权。

重试逻辑和异常处理

在实践中,代理错误很少是一次性的——尤其是在大规模解析时。代码的正确架构 应该考虑到带有延迟的重试和每个错误的单独日志记录。

import time
import requests
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def fetch_with_retry(url, proxies, max_retries=3, backoff=2):
    for attempt in range(1, max_retries + 1):
        try:
            response = requests.get(url, proxies=proxies, timeout=15)
            response.raise_for_status()
            return response
        except requests.exceptions.ProxyError as e:
            logger.warning(f"[尝试 {attempt}] ProxyError: {e}")
        except requests.exceptions.SSLError as e:
            logger.warning(f"[尝试 {attempt}] SSLError: {e}")
        except requests.exceptions.HTTPError as e:
            if response.status_code == 407:
                logger.error("407: 请检查代理的用户名/密码或 IP 白名单")
                break
            logger.warning(f"[尝试 {attempt}] HTTPError: {e}")
        except requests.exceptions.Timeout:
            logger.warning(f"[尝试 {attempt}] 连接超时")

        time.sleep(backoff * attempt)

    raise RuntimeError(f"在 {max_retries} 次尝试后无法获取 {url}")

这种方法节省了调试时间:而不是在第一次网络故障时脚本崩溃,您将获得一份清晰的日志, 指出每次尝试的具体错误类型。

代理轮换和防止封禁

如果 ProxyError 和 407 通常与代码设置有关,那么大规模封禁和在特定域名上稳定的连接拒绝几乎总是指向 IP 池质量的问题。对于解析 市场(Wildberries、Ozon、Avito)或社交媒体等任务,建议在每次请求或通过指定间隔进行 IP 轮换。

import random

proxy_pool = [
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
    "http://user:[email protected]:8000",
]

def get_random_proxy():
    proxy = random.choice(proxy_pool)
    return {"http": proxy, "https": proxy}

response = requests.get(url, proxies=get_random_proxy(), timeout=15)

对于生产环境的解析器,通常更方便使用提供商端的自动轮换代理——这样就不需要自己维护 IP 列表,轮换通过一个端点透明地进行。在这种情况下, 住宅代理 特别适合绕过具有严格检测系统的网站上的反机器人保护,而 数据中心代理 是一个不错的选择, 如果请求速度重要,而目标网站并不积极阻止数据中心范围。

代理类型 速度 封禁风险 任务
数据中心代理 高 中高 快速解析开放 API
住宅代理 中等 低 解析市场、社交媒体
移动代理 中低 最低 与严格的反机器人系统合作

Session、超时和SSL验证

对于长期运行的脚本(例如循环解析器),建议使用 requests.Session() 而不是单独调用 requests.get()。Session 复用 TCP 连接, 这降低了代理服务器的负载,并减少了大量请求中的 ProxyError。

session = requests.Session()
session.proxies.update({
    "http": "http://user:password@proxy_host:port",
    "https": "http://user:password@proxy_host:port",
})

for url in urls_list:
    try:
        resp = session.get(url, timeout=15)
        print(resp.status_code, url)
    except requests.exceptions.RequestException as e:
        print(f"{url} 上的错误:{e}")

另外,值得提及的是 verify 参数。如果您使用的代理自我替换 SSL 证书(用于调试的 MITM 代理或某些企业代理), requests 可能会因证书不匹配而发出警告。完全禁用验证 (verify=False) 仅应在测试环境中进行——在生产环境中 这会带来“中间人攻击”的风险:

# 仅用于本地调试!
response = requests.get(url, proxies=proxies, verify=False, timeout=15)

# 正确的方法是指定代理的受信任证书路径
response = requests.get(url, proxies=proxies, verify="/path/to/proxy_cert.pem")

错误诊断检查表

如果脚本因代理错误崩溃,请从上到下检查此列表——在 90% 的情况下,原因会在前三个点中找到。

  1. 检查字典 proxies 中的协议——几乎总是应该是 http://,即使是对于 HTTPS 请求。
  2. 通过 curl -x 直接测试代理,以排除代码侧的问题。
  3. 确保用户名和密码在 URL 中不包含未转义的特殊字符。
  4. 检查代理是否绑定到特定 IP(白名单),而不是通过用户名/密码进行授权。
  5. 在认为代理不工作的情况下,将 timeout 增加到 15-30 秒。
  6. 如果错误仅在特定网站上出现——问题在于 IP 的声誉,而不是代码。
  7. 添加带有指数延迟的重试逻辑,而不是单次尝试。
  8. 在大规模解析时使用 IP 轮换,而不是在所有请求中使用一个静态代理。

结论

Python requests 中的大多数代理错误归结为三件事:代理 URL 中的协议不正确 (http 而不是 https), 授权缺失或格式错误,以及 IP 池的质量。ProxyError 通常表示连接问题或 IP 被封禁,SSLError WRONG_VERSION_NUMBER 几乎总是指向代理协议不正确,而 407 — 则表示用户名/密码或白名单存在问题。

如果您在解析市场或社交媒体时经常遇到封禁和 407 错误,您应该重新审视代理的来源,而不仅仅是代码。为了实现稳定的解析并降低封禁率, 我们建议尝试 住宅代理—— 它们使用真实的 IP 地址,并且在受保护的网站上更少引发 ProxyError。如果您更关注速度和请求量,而不是绕过复杂的反机器人系统,请考虑 数据中心代理——它们更便宜 并且在简单解析任务中更快。