Python3使用cgi.parse_header下载特殊文件名文件时报错如何解决
问题根因
你的报错和文件名里的连字符、空格没有关系,核心原因是这两个站点的响应头没有返回Content-Disposition字段,你直接读取该字段得到的是None值,传入cgi.parse_header时触发了类型错误。
修复方案
先判断响应头是否携带Content-Disposition,如果没有就自定义文件名规则,同时添加浏览器请求头避免被站点反爬拦截,最后对拿到的文件名做清洗处理特殊字符即可,以下是完整可运行代码:
from urllib.request import urlopen, Request, urlretrieve import cgi import os from urllib.parse import urlparse # 存储路径 save_path = '/home/edmint/2results/holdingPen/' # 自动创建路径避免不存在报错 os.makedirs(save_path, exist_ok=True) # 可替换为你要下载的任意地址 url = "https://www.vaneck.com/etf/equity/smog/holdings/download/xlsx/" # url = "https://www.globalxetfs.com/funds/driv/?download_full_holdings=true" # 模拟Chrome浏览器请求头,避免被站点反爬拦截 headers = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } req = Request(url, headers=headers) remotefile = urlopen(req) # 安全获取Content-Disposition头,不存在则返回None content_disposition = remotefile.info().get('Content-Disposition', None) filename = "" if content_disposition: value, params = cgi.parse_header(content_disposition) filename = params.get("filename", "") # 未从响应头拿到文件名则从URL生成默认名 if not filename: parsed_url = urlparse(url) base_name = os.path.basename(parsed_url.path) or "download_holdings" # 可根据站点类型自行调整默认后缀 filename = base_name if '.' in base_name else f"{base_name}.xlsx" # 清洗文件名:移除首尾空格、替换Linux文件系统不支持的非法字符 filename = filename.strip().replace('/', '_').replace('\\', '_') # 安全拼接保存路径,适配不同操作系统规则 full_save_path = os.path.join(save_path, filename) # 执行下载 urlretrieve(url, full_save_path) print(f"文件已成功保存到:{full_save_path}")
代码说明
- 新增浏览器UA模拟正常访问,避开大部分站点的爬虫拦截规则
- 对
Content-Disposition做了存在性校验,完全避免读取空值触发的类型错误 - 无响应头文件名时自动生成备用名称,下载流程不会中断
- 自动清洗文件名首尾空格、特殊字符,带连字符、末尾空格的文件名都可以正常保存
- 用系统自带的路径拼接方法,避免手动拼接路径的语法错误
内容的提问来源于stack exchange,提问作者KalaDude
相关产品推荐
相关产品推荐

