按最内层错误快速定位故障分支

同一条 ProxyError 在不同运行环境下指向的环节并不相同,排查时应翻到异常链最内层一行进行对照,且每次只执行一项优先操作,改动后回到同一目标验证效果。

  • 出现 WRONG_VERSION_NUMBER 或 only speaks HTTP:通常为代理端点协议错配,应核对端点实际支持的协议并修正 URL 的 scheme。
  • 出现 Connection refused 或 WinError 10061:说明端口上没有进程监听;容器内的 127.0.0.1 指向当前命名空间而非宿主机,需按实际运行环境核对监听地址与端口。
  • 出现 NameResolutionError 或 getaddrinfo failed:需分清是代理域名还是目标域名解析失败,并在同一环境核对解析结果。
  • 出现 Tunnel connection failed: 407:认证方式、凭证或白名单出口不正确,应核对认证方式、凭证与提供方所见出口。
  • 出现 Tunnel connection failed: 502/503:网关或上游不可用、目标未授权,应保存脱敏记录并核查授权范围。
  • 笔记本成功而 IDE 或容器失败:多为环境变量覆盖 session.proxies,可用最小诊断 Session 设置 trust_env=False 隔离验证。
  • 短效节点使用一段时间后批量失败:节点过期或被回收,应查获取时间与有效期,并隔离失效节点。

执行优先操作后若同目标恢复即可锁定分支;未生效则保留记录进入下一行,不要并行改动多个变量。需注意 502/503 属于隧道阶段拒绝,而目标站的 403、429 发生在 CONNECT 之后,两者不可混淆。

读懂四层异常链的结构与含义

长串异常实际由四层构成:对外异常类型、连接池上下文、重试包装以及最内层底层失败。示意结构为 requests.exceptions.ProxyError、HTTPSConnectionPool(host='target.example', port=443)、Max retries exceeded with url: /path,以及 Caused by ProxyError('Cannot connect to proxy.', NewConnectionError(... Connection refused ...))。

  • 最外层类型为 requests.exceptions.ProxyError,部分版本措辞写作 Unable to connect to proxy,含义一致。
  • Max retries exceeded with url 并不代表真的重试了很多次,适配器默认 Retry(0),首次失败也会被包装成该形式。
  • HTTPSConnectionPool(host=目标站) 只说明请求目标是该站点,不能证明已连到目标站或目标封了 IP;HTTPS 经 HTTP 代理时 TCP 建在代理端口,CONNECT 通过后才进行目标 TLS。
  • ProxyError 继承自 ConnectionError,捕获顺序不当会吞掉该分支;隧道 403 不等于目标 403,隧道 502 不等于 IP 被封。

内层错误与后续章节的对应关系为:WRONG_VERSION_NUMBER、Connection refused、NameResolutionError 指向配置类问题;Tunnel connection failed: 407 与 502/503 指向认证与转发问题;短效节点批量失败指向节点失效问题。若采集任务在半夜批量报 ProxyError,应优先排查节点是否过期,按内层错误分流、一次只改一项。

配置类错误:协议、端口与环境变量

配置类错误的特点是在同一端点、同一目标下可稳定复现,改完立即见效,可按内层线索进入对应分支。

协议错配

报错含 WRONG_VERSION_NUMBER 或 only speaks HTTP,基本是协议写错,应将代理 URL 的 scheme 改为端点实际支持的 http。proxies 中的 https 键表示目标是 HTTPS 站点,并不要求代理地址本身是 https。同一端点能建立 CONNECT 并拿到有效响应即可关闭该分支;真正的 HTTPS 端点不能强改为 http,关闭证书校验也无法修复协议错配。

连接拒绝

Connection refused 或 WinError 10061 说明端口上没有进程在监听,可按三步核对:核对代理地址与监听端口并清理残留的旧代理进程;容器内的 127.0.0.1 指向当前命名空间而非宿主机,按实际运行环境核对;确认能连通代理端口后再通过代理发请求。端口连通只是前提,TCP 通不代表认证与 CONNECT 成功。

DNS 解析失败

NameResolutionError 或 getaddrinfo failed 需分清是哪一端域名失败:若代理域名解析失败,换 socks5h 也无济于事;只有目标域名失败才涉及 socks5(本地解析)与 socks5h(远端解析)的区别。对应一端恢复解析且请求通过即可结案。

环境变量覆盖

笔记本正常而 IDE、定时任务或容器报错,多半是环境变量覆盖了 session.proxies。用最小诊断 Session 设置 trust_env=False 隔离后重跑,若实际入口用上预期代理、报错消失且数据有效即可定位。需注意该设置会同时停用环境 CA 与 netrc。

407 与隧道 502/503 的区分处理

隧道类错误的前提是代理已连上,拒绝发生在认证或转发阶段,407 与 502/503 的分工如下。

  • 407:拒绝发生在认证环节,即认证方式、凭证或白名单出口不对,应核对认证方式、凭证有效期与提供方所见出口。
  • 502/503:拒绝发生在转发环节,即网关或上游不可用,应核查目标授权范围、套餐覆盖与上游可用性。

处理 407 应围绕凭证展开:Basic 认证按代理要求放入代理 URL,用户名和密码中的特殊字符分别进行 URL 编码,不要对整个 URL 编码;白名单模式下需核对提供方所见的客户端出口,NAT 后的出口常与本机不同。同一端点不再返回认证失败且能访问授权目标即算通过。

遇到 502/503 不要急于更换节点,应先保存脱敏的状态码、时间与请求 ID,核查授权范围与套餐覆盖。只有 CONNECT 通过并完成目标 TLS 与数据请求才算恢复;若所有节点报同样的错,问题多半在统一配置或服务端,循环盲换没有意义。目标站的 403、429 出现在 CONNECT 之后,与代理认证失败分属两个阶段,不可混为一谈。

短效节点失效识别与代理池管理

短效节点使用一段时间后批量失败、而健康节点对照成功,属于节点生命周期问题,可按四步处理:记录节点获取时间与提供方有效期并在到期前刷新;过期节点立即隔离、不再分发;连续失败按分类有限退避,407 与额度类错误不重试;新节点入池前做连通验证。定位成立的标志是失效节点失败、有效节点成功、采集恢复,仅凭“换个 IP 又好了”不能作为依据。

最小探针脚本可参考以下写法:

import os
import requests

proxy_url = os.environ['PROBE_PROXY_URL']
target_url = os.environ['PROBE_TARGET_URL']
proxies = {'http': proxy_url, 'https': proxy_url}  # 协议按端点能力

with requests.Session() as session:
    session.trust_env = False
    try:
        response = session.get(target_url, proxies=proxies, timeout=(5, 20))
    except requests.exceptions.ProxyError:
        print('ProxyError:按内层错误分流')
        raise SystemExit(1)

print('HTTP status:', response.status_code)
print('核对预期字段,200 不算成功')

确认节点失效后需管理代理池。代理池有两种形态:固定入口轮换隧道,以及应用自维护的短效 IP 列表。确认失效后用健康节点替换;轮换无法修复协议、认证、额度或账号问题,也不承诺每次 requests.get 都更换出口。IPxSS 动态住宅IP 面向采集场景,可将健康节点替换与失效隔离交给服务端处理,避免采集中断(官网地址:https://www.ipxss.com/)。

恢复验收与常见问题

端口可连接、归属查询成功、HTTP 200 均不能单独作为恢复依据,因为 200 也可能是登录页。验收清单包括:用真实启动方式(IDE、容器、定时任务)跑通授权目标;核对预期内容字段,排除登录页或挑战页;覆盖原先失败的时间窗口并多次低频运行;归档失败分类、节点获取时间与依赖版本。仅“能跑通一次”往往只是刚换节点的对照窗口,只通半截应按未恢复处理,仍被拒绝则回到认证与转发章节排查上游。

  • 没设代理为什么仍报 ProxyError:环境变量会参与合并,容器与终端不同,可用 trust_env=False 隔离验证。
  • HTTPS 目标的代理地址必须用 https 吗:看端点协议而非目标协议,http 端点同样可转发 HTTPS 目标。
  • Max retries exceeded with url 是重试太多吗:未必,默认 Retry(0) 也会包装首次失败,不要据此加重试。
  • 换成动态住宅IP 为什么仍失败:该方案只解决节点失效,协议、认证、额度问题不会因更换出口而消失。
  • proxies={} 为什么仍走代理:环境配置仍可能参与合并,用 trust_env=False 验证来源。

综上,ProxyError: Cannot connect to proxy 只是最外层包装,根因藏在最内层错误中。排查顺序为读取内层错误,按协议错配、连接拒绝、DNS、认证、隧道与节点失效分流,一次只改一个变量并回同一目标验证。配置类错误改配置,认证类错误核凭证,确认短效节点失效后才进入代理池管理:到期前刷新、失效即隔离、入池前验证。验收不含糊,HTTP 200 可能是登录页,须在实际运行入口核对预期字段。长期受节点过期与代理池健康问题拖累的团队,可了解 IPxSS 动态住宅IP方案,将失效隔离与健康检查交给服务端处理。