如何从不带文件名的URL下载文件并识别文件类型与原始文件名
动态URL文件下载问题解决方案
以下是可直接运行的完整实现代码,同时覆盖两个问题的解决需求:
import requests import os from cgi import parse_header # 配置要下载的URL url = "https://procurement-notices.undp.org/view_file.cfm?doc_id=257280" # url = "https://readthedocs.org/projects/python-guide/downloads/pdf/latest/" # 配置文件保存目录 save_dir = "./downloads" os.makedirs(save_dir, exist_ok=True) # 发送请求,开启重定向跟随、流式下载(适合大文件),添加超时避免卡死 resp = requests.get(url, allow_redirects=True, stream=True, timeout=30) resp.raise_for_status() # 请求出错直接抛出异常,避免保存错误页面内容 # 1. 解析原始文件名 filename = None content_disposition = resp.headers.get("Content-Disposition") if content_disposition: disposition, params = parse_header(content_disposition) if "filename" in params: filename = params["filename"] # 适配UTF-8编码的非英文文件名场景 elif "filename*" in params: filename_encoding, _, filename = params["filename*"].split("'", 2) filename = filename.encode('latin-1').decode(filename_encoding) # 2. fallback逻辑:未拿到原始文件名时,通过Content-Type推断后缀 if not filename: # 常见Content-Type到后缀的映射,可根据需求自行扩展 content_type = resp.headers.get("Content-Type", "") type_suffix_map = { "application/pdf": ".pdf", "application/msword": ".doc", "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx", "application/vnd.ms-excel": ".xls", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx", "text/plain": ".txt", "application/zip": ".zip" } suffix = type_suffix_map.get(content_type, ".bin") # 未知类型默认用.bin后缀 filename = f"download_file{suffix}" # 拼接完整保存路径 save_path = os.path.join(save_dir, filename) # 流式写入文件,避免大文件占用过多内存 with open(save_path, "wb") as f: for chunk in resp.iter_content(chunk_size=8192): f.write(chunk) print(f"文件已保存至:{save_path}")
问题1:如何提前判断返回的文件类型,使用正确后缀保存
- 优先从响应头
Content-Disposition的filename字段提取后缀,这是最准确的方式,和浏览器拿到的原始文件名后缀完全一致 - 若响应头没有返回
Content-Disposition,则通过Content-Type响应头匹配对应后缀,提前维护好常见的媒体类型和后缀的映射表即可,未知类型可以默认用.bin后缀避免错误
问题2:如何获取浏览器下载的原始文件名
浏览器展示的原始文件名绝大多数都存放在响应头的Content-Disposition字段中,格式一般为attachment; filename="xxx.docx",部分站点会用UTF-8编码的filename*=UTF-8''xxx.docx格式避免非英文文件名乱码,用标准库cgi模块的parse_header方法可以直接解析该字段的参数,不需要自己写正则匹配。
优化点说明
- 加入
stream=True流式下载,大文件不会一次性加载到内存,占用资源更低 - 加入
resp.raise_for_status(),4xx/5xx错误会直接抛出异常,避免把错误页面内容当成文件保存 - 新增文件名编码适配、目录自动创建、超时配置等鲁棒性逻辑,适合生产环境使用
内容的提问来源于stack exchange,提问作者Talib Daryabi
相关产品推荐
相关产品推荐

