標簽: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 sendraise 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_contextexcept Exception:pass# 2) httpx 同步 & 異步try:import httpxhttpx.Client.__init__ = functools.partialmethod(httpx.Client.__init__, verify=False)httpx.AsyncClient.__init__ = functools.partialmethod(httpx.AsyncClient.__init__, verify=False)except Exception:pass# 3) OpenAI 私有 AsyncHttpxClientWrappertry: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_initexcept Exception:pass# 4) aiohttptry:import aiohttpaiohttp.TCPConnector.__init__ = functools.partialmethod(aiohttp.TCPConnector.__init__, verify_ssl=False)except Exception:pass# 5) requeststry:import requestsfrom functools import wraps# 5-1) 修改 Session 實例默認屬性_orig_init = requests.Session.__init__@wraps(_orig_init)def _patched_init(self):_orig_init(self)self.verify = Falserequests.Session.__init__ = _patched_init# 5-2) 快捷函數補丁: 防止它們內部 new Session 時傳 verifyfor 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 校驗;調試完刪掉,干凈不留痕。