禁用SSL验证仍遇SSL错误:Urllib3无法下载文件
解决requests下载文件时的SSL意外EOF错误
问题重现
使用以下代码下载文件时,即使设置verify=False仍报错:
import requests response = requests.get('https://download.inep.gov.br/informacoes_estatisticas/indicadores_educacionais/taxa_transicao/tx_transicao_municipios_2019_2020.zip', stream=True, verify=False) with open('tx_transicao_municipios_2019_2020.zip', 'wb') as f: for chunk in response.iter_content(chunk_size=1024): if chunk: f.write(chunk)
错误信息:
urllib3.exceptions.SSLError: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1006)
Chrome可正常下载,尝试verify=certifi.where()也无效,多系统测试均失败,环境信息:
- Windows 10 Enterprise 22H2 (19045.3448);
- Python v3.11.5;
- OpenSSL v3.0.9;
- Urllib3 v2.0.6;
- Requests v2.31.0;
- Certifi v2023.7.22;
解决方案
1. 调整SSL上下文(推荐)
OpenSSL 3.0+默认安全级别较高,部分旧服务器不兼容,可自定义SSL上下文,指定TLS版本并降低安全级别:
import requests import ssl from requests.adapters import HTTPAdapter from urllib3.poolmanager import PoolManager class TLSAdapter(HTTPAdapter): def init_poolmanager(self, connections, maxsize, block=False): ctx = ssl.create_default_context() # 降低安全级别,兼容旧服务器 ctx.set_ciphers('DEFAULT@SECLEVEL=1') # 禁用TLSv1.3,部分服务器不支持该协议 ctx.options |= ssl.OP_NO_TLSv1_3 self.poolmanager = PoolManager( num_pools=connections, maxsize=maxsize, block=block, ssl_version=ssl.PROTOCOL_TLSv1_2, ssl_context=ctx ) url = 'https://download.inep.gov.br/informacoes_estatisticas/indicadores_educacionais/taxa_transicao/tx_transicao_municipios_2019_2020.zip' session = requests.Session() # 给HTTPS请求挂载自定义适配器 session.mount('https://', TLSAdapter()) session.verify = False with session.get(url, stream=True) as response: # 检查请求是否成功 response.raise_for_status() with open('tx_transicao_municipios_2019_2020.zip', 'wb') as f: # 增大chunk_size提升下载稳定性 for chunk in response.iter_content(chunk_size=8192): f.write(chunk)
2. 禁用分块传输
stream模式下的分块读取可能触发服务器SSL异常,尝试直接下载完整内容:
import requests url = 'https://download.inep.gov.br/informacoes_estatisticas/indicadores_educacionais/taxa_transicao/tx_transicao_municipios_2019_2020.zip' response = requests.get(url, verify=False) # 确保请求成功 response.raise_for_status() with open('tx_transicao_municipios_2019_2020.zip', 'wb') as f: f.write(response.content)
3. 模拟浏览器请求头
服务器可能校验User-Agent,添加Chrome的请求头模拟浏览器:
import requests headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36' } url = 'https://download.inep.gov.br/informacoes_estatisticas/indicadores_educacionais/taxa_transicao/tx_transicao_municipios_2019_2020.zip' response = requests.get(url, stream=True, verify=False, headers=headers) response.raise_for_status() with open('tx_transicao_municipios_2019_2020.zip', 'wb') as f: for chunk in response.iter_content(chunk_size=8192): if chunk: f.write(chunk)
原因说明
该错误本质是服务器与本地OpenSSL的加密协议/套件不兼容:
- OpenSSL 3.0+默认启用了更高的安全级别(SECLEVEL=2),部分旧服务器不支持对应的加密套件;
- 部分服务器对TLSv1.3支持不完善,禁用该协议即可正常通信;
- 服务器可能对非浏览器的请求头或分块传输模式有拦截,模拟浏览器行为可绕过限制。
内容的提问来源于stack exchange,提问作者Jackson Mourão
相关产品推荐
相关产品推荐

