错误信息
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
也可能输出为
- urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
尖括号内的文本是底层的 ssl 错误,内容各不相同(“self-signed certificate in certificate chain”、“certificate has expired”等)。Python 以源代码位置和“>”结束这段文本,例如“(_ssl.c:1082)>”。
Python urllib.request — urllib.error.URLError wrapping ssl.SSLCertVerificationError
含义
urllib.request.urlopen() 会把任何连接问题包装成 URLError,因此 TLS 失败显示在“<urlopen error …>”中。如果所有公共网站(包括 www.python.org)都出现这个错误,说明这个 Python 进程根本没有可用的 CA 证书:典型情况是 macOS 上的 python.org 安装程序,其自带的 OpenSSL 在运行“Install Certificates.command”之前不会读取钥匙串。
原因
macOS 上从 python.org 安装的 Python 缺少证书
常见安装程序自带的 OpenSSL 证书存储是空的,另外附带一个辅助脚本“Install Certificates.command”,用于安装并链接 certifi。跳过这个脚本(或者之后安装了新的次版本)会导致 urlopen 无法验证任何证书。
没有 CA 证书包的精简镜像或自定义 Python 构建版本
偶见从源代码编译的 Python,或复制到 scratch 镜像或 distroless 镜像中的 Python,会去查找 OpenSSL 的默认证书包路径,而这个路径在那里并不存在。
执行 TLS 检查的代理
偶见如果只有部分网站失败,或者失败是从接入公司网络后开始的,原因很可能是这个 Python 不信任的代理根证书。
SSL_CERT_FILE 或 SSL_CERT_DIR 指向了错误的位置
罕见这些变量会替换整个进程的默认位置;空文件或无关的文件会导致所有验证失败。
修复方法
- 1.
macOS:运行 Install Certificates.command
为每个 python.org Python 版本从访达(Finder)或终端运行它,然后确认默认的 CA 文件路径存在。
bash # macOS, python.org installer (one script per installed version) "/Applications/Python 3.14/Install Certificates.command" python3 -c "import ssl; print(ssl.get_default_verify_paths().openssl_cafile)" - 2.
传入基于 certifi 构建的上下文
对于必须在任何环境中都能运行的脚本,显式地基于 certifi.where() 构建 SSL 上下文;并及时更新 certifi,使其根证书保持最新。
python # Explicit, portable: use certifi's Mozilla bundle for this request # pip install certifi import certifi, ssl, urllib.request ctx = ssl.create_default_context(cafile=certifi.where()) urllib.request.urlopen("https://www.python.org/", context=ctx) - 3.
或者使用操作系统的证书存储
truststore.inject_into_ssl() 让 urllib、http.client 以及基于 ssl 构建的库通过操作系统进行验证,IT 部门部署的公司根证书也包含在内。
python # Or verify with the operating system's store (macOS Keychain, Windows, Linux bundle) # pip install truststore==0.10.5 import truststore truststore.inject_into_ssl() - 4.
检查环境变量是否覆盖了默认设置
如果 SSL_CERT_FILE 和 SSL_CERT_DIR 指向的文件不包含所需的根证书,就取消设置它们。
bash env | grep -E '^SSL_CERT_(FILE|DIR)=' python3 -c "import ssl; print(ssl.get_default_verify_paths())"