标签:Python、SSL、monkey-patch、httpx、aiohttp、requests、OpenAI
1 为什么会有这篇文章?
在本地调试 OpenAI 代理、数据抓取、私有服务、访问外网 时,经常会碰到如下报错:
SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.openai.com', port=443): Max retries exceeded with url: /v1/models (Caused by SSLError(SSLError(1, '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1017)')))
File ".venv\lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions
| raise mapped_exc(message) from exc
| httpx.ConnectError: All connection attempts failed
An error occurred: Expecting value: line 1 column 1 (char 0)
An error occurred during the request: HTTPSConnectionPool(host='en.wikipedia.org', port=443): Max retries exceeded with url: /w/api.php?list=search&srprop=&srlimit=1&limit=1&srsearch=langchain&format=json&action=query (Caused by ProxyError('Unable to connect to proxy', SSLError(SSLEOFError(8, '[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1000)'))))
File ".venv\lib\site-packages\requests\adapters.py", line 698, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='r.jina.ai', port=443): Max retries exceeded with url: /https://baijiahao.baidu.com (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:997)')))
为了快速定位 “是不是证书问题” 而不是“逻辑问题”,我们需要临时、零侵入地把所有 HTTP 客户端的 SSL 校验关掉。
把下面这段脚本调用后就能跑,调试结束直接删除,零副作用。
或者为了临时绕过所有SSL证书验证,使代码逻辑跑通。
2 完整代码(复制即用)
def monkey_patch():
"""
通过关闭 SSL/TLS 证书验证,解决 ssl 验证引发的 443 超时问题
(注:临时调试使用,生产务必移除)
"""
import functools
# 1) 全局标准库 SSL 上下文
try:
import ssl
# 全局取消证书验证
ssl._create_default_https_context = ssl._create_unverified_context
except Exception:
pass
# 2) httpx 同步 & 异步
try:
import httpx
httpx.Client.__init__ = functools.partialmethod(
httpx.Client.__init__, verify=False
)
httpx.AsyncClient.__init__ = functools.partialmethod(
httpx.AsyncClient.__init__, verify=False
)
except Exception:
pass
# 3) OpenAI 私有 AsyncHttpxClientWrapper
try:
import openai._base_client as bc
_old = bc.AsyncHttpxClientWrapper.__init__
def _new_init(self, *a, **k):
k.setdefault("verify", False)
return _old(self, *a, **k)
bc.AsyncHttpxClientWrapper.__init__ = _new_init
except Exception:
pass
# 4) aiohttp
try:
import aiohttp
aiohttp.TCPConnector.__init__ = functools.partialmethod(
aiohttp.TCPConnector.__init__, verify_ssl=False
)
except Exception:
pass
# 5) requests
try:
import requests
from functools import wraps
# 5-1) 修改 Session 实例默认属性
_orig_init = requests.Session.__init__
@wraps(_orig_init)
def _patched_init(self):
_orig_init(self)
self.verify = False
requests.Session.__init__ = _patched_init
# 5-2) 快捷函数也兜底
for name in ("get", "post", "put", "patch", "delete", "head", "options"):
_orig = getattr(requests, name)
setattr(
requests,
name,
(lambda _o: lambda *a, **k: _o(*a, **dict(k, verify=False)))(_orig),
)
except Exception:
pass
使用:
# 在业务逻辑最顶部调用即可
monkey_patch()
3 逐段技巧拆解
位置 | 技巧 | 一句话解释 |
---|---|---|
ssl._create_default_https_context = ... |
全局钩子 | 把标准库 HTTPS 默认上下文换成“不校验”。 |
functools.partialmethod |
一行改默认参数 | 不继承、不派生,直接把类方法的形参默认值改掉。 |
setdefault("verify", False) |
不覆盖显式传参 | 只在用户没传时兜底,传了 True 仍然生效。 |
@wraps |
栈信息不丢失 | 调试时能看到原函数名,而不是 <lambda> 。 |
except Exception: |
不吞系统信号 | 保留 KeyboardInterrupt 、SystemExit ,脚本可 Ctrl-C。 |
4 何时删除?
场景 | 建议 |
---|---|
单测/本地调试 | 保留 |
代码评审 / 上线 | 必须删除 |
CI / Docker | 用环境变量 SSL_NO_VERIFY=1 控制开关,而非硬编码 |
5 延伸阅读
- Python 官方文档:
ssl.create_default_context
- httpx 文档:
verify
- requests 文档:
Session.verify
一句话总结
通过侵入式修改,关掉全部 SSL 校验;调试完删掉,干净不留痕。