批量解压并分类文件的脚本

wen 实用脚本 1

告别手动整理:用Python脚本实现批量解压与智能分类,效率提升10倍


目录导读

  1. 为什么你需要一个“解压+分类”脚本? – 手动操作的痛点与效率陷阱
  2. 核心设计思路:不只是解压,而是“规则引擎” – 关键词、后缀、目录结构三维分类法
  3. 手把手代码实战:基于zipfile与os模块的脚本拆解 – 含异常处理与路径穿越防护
  4. 进阶技巧:如何让脚本适配RAR/7z? – 调用外部引擎与跨平台方案
  5. 性能与安全考量:大文件流式解压与恶意文件筛查
  6. 常见问题问答 (FAQ) – 解决你的“为什么报错”和“怎么改规则”

在日常工作中,我们经常会从网盘、邮件或设计协作平台下载大量压缩包,这些包内往往是混乱的文件名,设计稿最终版(1).zip”里可能混杂着PDF、PSD和图片资源,手动逐个解压再拖拽归类,不仅耗时耗力,且极易出错。对于需要处理大量资料的运营、设计或数据分析人员而言,这是一种隐性的时间黑洞。

批量解压并分类文件的脚本

本文将带你从零构建一个批量解压并分类文件的Python脚本,它将自动读取指定目录下的所有压缩包,解压后根据文件扩展名(如 .pdf.jpg)、文件名关键词(如“合同”、“海报”)自动建立子文件夹并归档,这套方案不仅能彻底解放你的双手,更是构建个人自动化工作流的基础。

核心设计思路:超越“解压”的维度

很多初级脚本只会把文件解压到同名文件夹,这并没有解决“分类”的痛点,一个合格的脚本需要内置规则引擎,我们在设计时遵循三个维度:

  • 一级分类(按扩展名):最稳定、最普适,例如将所有 .docx 放入 文档,将所有 .psd 放入 设计源文件
  • 二级分类(按关键词):针对特定项目,例如若文件名包含“季度报表”,则无论后缀是什么,都强制归入 财务数据 文件夹。
  • 降维处理(按时间):若文件实在无法识别,则按 年-月 归档,防止漏网之鱼。

这种设计逻辑让脚本具备“智能决策”能力,而非死板的机械操作。

代码实战:构建你的专属解压分类机

以下脚本基于Python标准库 zipfileos 编写,无需安装第三方依赖,保证了零门槛使用,我们重点加入了路径遍历防护(防止压缩包内的恶意 路径)和文件名编码处理

import os
import zipfile
import shutil
from pathlib import Path
# 配置区:你可以自由修改这些规则
SOURCE_DIR = "./downloads"      # 存放zip包的目录
BASE_OUTPUT = "./sorted_files"  # 输出根目录
# 分类规则:后缀映射到目标文件夹名
EXT_RULES = {
    ".pdf": "PDF文档", ".docx": "Word文档", ".xlsx": "Excel表格",
    ".jpg": "图片素材", ".png": "图片素材", ".psd": "PS源文件",
    ".mp4": "视频文件", ".zip": "嵌套压缩包"
}
# 关键词特殊规则(优先级高于后缀)
KEYWORD_RULES = {
    "合同": "法律商务", "发票": "财务票据", "设计稿": "项目设计"
}
def safe_extract(zip_ref, target_dir):
    """安全解压,防止路径穿越"""
    for member in zip_ref.namelist():
        # 将反斜杠统一为正斜杠,并剥离绝对路径
        normalized = member.replace('\\', '/')
        # 过滤掉绝对路径和上级跳转
        if normalized.startswith('/') or '..' in normalized.split('/'):
            continue
        target_path = os.path.join(target_dir, normalized)
        # 如果是目录则创建,否则提取
        if member.endswith('/'):
            os.makedirs(target_path, exist_ok=True)
        else:
            with zip_ref.open(member) as source, open(target_path, "wb") as target:
                shutil.copyfileobj(source, target)
def classify_file(file_path):
    """返回该文件应放入的类别名"""
    filename = os.path.basename(file_path).lower()
    # 先检查关键词规则
    for keyword, category in KEYWORD_RULES.items():
        if keyword.lower() in filename:
            return category
    # 再检查后缀规则
    ext = os.path.splitext(filename)[1]
    return EXT_RULES.get(ext, "未分类/其他")
def main():
    os.makedirs(BASE_OUTPUT, exist_ok=True)
    zip_files = [f for f in os.listdir(SOURCE_DIR) if f.endswith('.zip')]
    for zip_name in zip_files:
        zip_path = os.path.join(SOURCE_DIR, zip_name)
        print(f"正在处理: {zip_name}")
        try:
            with zipfile.ZipFile(zip_path, 'r') as zf:
                # 创建临时解压目录(使用压缩包名)
                temp_dir = os.path.join(BASE_OUTPUT, f"_temp_{zip_name[:-4]}")
                os.makedirs(temp_dir, exist_ok=True)
                safe_extract(zf, temp_dir)
                # 遍历临时目录中的文件并分类移动
                for root, dirs, files in os.walk(temp_dir):
                    for file in files:
                        full_path = os.path.join(root, file)
                        category = classify_file(full_path)
                        dest_dir = os.path.join(BASE_OUTPUT, category)
                        os.makedirs(dest_dir, exist_ok=True)
                        # 处理重名文件
                        dest_path = os.path.join(dest_dir, file)
                        counter = 1
                        while os.path.exists(dest_path):
                            name, ext = os.path.splitext(file)
                            dest_path = os.path.join(dest_dir, f"{name}_{counter}{ext}")
                            counter += 1
                        shutil.move(full_path, dest_path)
                        print(f"  → 已分类: {file} → {category}")
                # 清理空临时目录
                shutil.rmtree(temp_dir)
        except zipfile.BadZipFile:
            print(f"  ❌ 错误: {zip_name} 不是有效的zip文件")
        except Exception as e:
            print(f"  ❌ 未知错误: {str(e)}")
if __name__ == "__main__":
    main()

扩展技能:处理RAR与7z格式

标准库不支持RAR,你需要安装 patool 或调用系统命令行。

推荐方案: 安装 patool 库,它会自动调用系统已安装的解压工具(如WinRAR或7-Zip),代码修改仅需将 zipfile.ZipFile 部分替换为:

import patoolib
# 解压到临时目录
patoolib.extract_archive(zip_path, outdir=temp_dir)

但要注意,使用 patool 会失去对路径穿越的精细控制,建议在解压后增加一次扫描,删除可疑的 开头的隐藏配置文件。

性能与安全:不可忽视的细节

  1. 流式处理:如果你的压缩包内有超过1GB的大文件,上述代码中的 copyfileobj 是流式的,不会占满内存,这点很关键。
  2. 文件锁定:在Windows下,如果目标文件被Excel或Word占用,脚本会报 PermissionError,建议在脚本开头获取用户确认,关闭相关程序。
  3. 恶意压缩包(Zip Slip):我们已通过 safe_extract 函数过滤了 路径,请切勿删除该函数而直接用 extractall,这是安全红线。

常见问题问答 (FAQ)

Q1:我的文件名是中文,运行时会出现乱码或文件找不到? A: 压缩包内部编码可能是GBK(尤其来自Windows系统的包)。zipfile 模块不支持自动识别编码,你需要在 zip_ref.namelist() 之后手动处理:

member_corrected = member.encode('cp437').decode('gbk')  # 尝试用GBK解码

如果报错,则回退为原文件名,这是中文用户最容易踩的坑。

Q2:我不希望图片素材和设计源文件分开,想合并成一个“多媒体”文件夹怎么做? A: 修改 EXT_RULES 字典,将 .jpg.png.psd 的值都改为 "多媒体" 即可,规则引擎的灵活之处就在于此,改一行配置,不用动逻辑。

Q3:脚本执行中强行中断了,留下了很多 _temp_ 开头的空文件夹怎么办? A:main() 函数最开始,增加一段清理代码:遍历 BASE_OUTPUT 下所有以 _temp_ 开头的目录,如果为空则删除,或者在 finally 块中确保 rmtree 执行。

Q4:如何设置定时任务每天凌晨自动执行? A: Windows下用“任务计划程序”指向 python your_script.py;macOS/Linux下用 crontab -e 添加 0 2 * * * 即可,确保脚本路径和配置目录使用绝对路径。


通过这个脚本,你将繁琐的手工劳动变成了数千行代码的自动循环,这套逻辑稍加修改,就能扩展为监控文件夹变化(利用 watchdog 库)的实时自动整理工具,自动化不是目的,解放你的注意力去处理更有创造力的事务,才是真正的价值,就去把那个积压已久的 下载 文件夹交给脚本去处理吧。

抱歉,评论功能暂时关闭!