用 zipfile 解压 ZIP 文件最稳妥:需用 with 语句打开、is_zipfile() 预检、路径净化防遍历、extractall() 指定 path、异常分治捕获 BadZipFile/PermissionError 等。

用 zipfile 解压单个 ZIP 文件最稳妥
Python 标准库的 zipfile 模块不依赖外部工具,跨平台稳定,适合批量任务。别用 os.system("unzip ..."),Windows 没 unzip 命令,Linux/macOS 版本行为也不一致。
关键点:必须显式调用 extractall(),且传入 path 参数指定解压目录;否则默认解到当前工作目录,容易污染项目根路径。
-
ZipFile对象需用with语句打开,避免文件句柄泄漏 - 解压前建议检查
is_zipfile(),跳过损坏或非 ZIP 文件 - 如果 ZIP 内含绝对路径或
../,默认会解压到上级目录——有安全风险,要用os.path.abspath()+os.path.commonpath()做路径净化(见下一点)
批量遍历 ZIP 文件时要过滤和防重名冲突
常见场景是处理一个文件夹下的所有 .zip,但实际遇到:.ZIP、.Zip、隐藏文件、临时文件(如 ~$archive.zip)、已解压过的同名文件夹。直接 glob("*.zip") 不够用。
- 用
pathlib.Path遍历更可靠:list(p for p in folder.iterdir() if p.suffix.lower() == ".zip" and p.is_file()) - 解压目标文件夹名建议基于原 ZIP 名生成,例如
archive.zip → archive_extracted/,避免多个 ZIP 解到同一目录导致覆盖 - 若目标目录已存在,
extractall()默认会覆盖同名文件——加if not target_dir.exists(): target_dir.mkdir()可跳过已处理项
处理 ZIP 中的危险路径(路径遍历漏洞)
恶意 ZIP 文件可能包含 ../../etc/passwd 这类路径,直接 extractall() 会写入系统关键位置。标准库不自动防御,必须手动校验。
立即学习“Python免费学习笔记(深入)”;
核心逻辑:对 ZIP 内每个 ZipInfo.filename,先转为绝对路径,再确认其是否仍在预期解压根目录之下:
with ZipFile(f) as zf:
for member in zf.filelist:
# 规范化路径,防止 ../ 绕过
safe_path = os.path.normpath(member.filename)
if safe_path.startswith("..") or os.path.isabs(safe_path):
raise ValueError(f"Unsafe path in ZIP: {member.filename}")
# 确保解压后路径不会逃逸
target = Path(output_dir) / safe_path
if os.path.commonpath([target.resolve(), output_dir.resolve()]) != str(output_dir.resolve()):
raise ValueError(f"Path escape attempt: {member.filename}")
zf.extractall(output_dir)
这段校验必须放在 extractall() 前,且不能省略 os.path.normpath() —— 否则 "....oo.txt" 和 "..\..\foo.txt" 会被绕过。
解压失败时要捕获具体异常并继续执行
批量任务里个别 ZIP 损坏或密码保护很常见。用裸 try/except Exception: 会吞掉关键信息,应区分处理:
-
BadZipFile:文件不是 ZIP 或已损坏,可记录日志后continue -
RuntimeError:遇到加密 ZIP(无密码),可跳过或统一提示“需密码” -
PermissionError:目标目录不可写,应中断整个流程,而不是静默失败 - 不要忽略
OSError,它可能代表磁盘满、路径过长等真实系统问题
错误日志至少包含文件名和异常类型,方便排查。别用 print(e),改用 logging.error(f"{f.name}: {type(e).__name__}")。
批量解压看似简单,真正上线跑几百个文件时,路径安全、异常分治、文件筛选这三点最容易出问题——尤其是那个 os.path.commonpath() 校验,少写一行就可能把 ZIP 里的恶意路径解到系统目录。


















