资讯动态

Python实现文件自动分类的操作步骤

发布时间:2026/10/16 15:15:59 来源:尧图企业网站定制
前言文件自动分类要解决的是一个很朴素的痛点下载目录、桌面、微信文件夹里堆了几百个文件想按类型图片、文档、压缩包、音视频自动归到不同子目录里。这件事用 Python 做只需要几十行但能跑通和敢在真实目录上跑是两码事——前者是几行for循环后者要考虑重名覆盖、移动失败、误伤隐藏文件这些问题。本文先把分类逻辑拆成扫描 → 判定 → 目标路径 → 移动四步再给出一份完整的、可以先空跑dry-run确认再真正执行的脚本。核心依赖是标准库里的pathlib和shutil不装任何第三方包。先明确一个边界本文只讨论按扩展名分类。按文件内容比如判断一个 PDF 是合同还是论文需要读内容甚至上模型那属于另一个话题。一、四步拆解第一步扫描。用Path.iterdir()只列当前一层不递归。注意它会连隐藏文件、目录一起列出来需要自己筛。第二步判定类别。取文件后缀.suffix如.jpg转小写后查一张映射表。第三步算出目标路径。目标目录 源目录 / 类别名若文件已在目标目录里跳过否则会死循环。第四步移动。用shutil.move(src, dst)它跨磁盘分区时自动退化为先复制再删除比os.rename()更安全——os.rename()跨分区会直接抛OSError。关于扩展名有一个必须记住的细节Path.suffix返回的是最后一个点之后的部分archive.tar.gz的.suffix是.gz要拿全部后缀得用.suffixes返回列表。分类时按最后一个后缀通常够用。二、类别映射与判定# 适用于 Python 3.8from pathlib import PathCATEGORIES {图片: {.jpg, .jpeg, .png, .gif, .bmp, .webp, .svg},文档: {.txt, .md, .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx},压缩包: {.zip, .rar, .7z, .tar, .gz, .bz2},音视频: {.mp3, .wav, .flac, .mp4, .mkv, .mov, .avi},代码: {.py, .js, .ts, .html, .css, .json, .yaml, .yml},}def classify(path):根据后缀返回类别名没匹配上就归到 其他。ext path.suffix.lower()for name, exts in CATEGORIES.items():if ext in exts:return namereturn 其他用set而不是list存后缀判定是 O(1)其实对几百个文件差别可以忽略但写成集合语义更贴切。path.suffix.lower()必须转小写否则.JPG和.jpg会被分到两类。三、处理重名不要静默覆盖如果目标目录里已经有同名文件shutil.move的行为取决于os.rename()的语义——官方文档的原话是若dst已存在但不是目录它可能被覆盖取决于os.rename()的语义。这意味着在 POSIX 上它默认静默覆盖在 Windows 上则倾向于抛FileExistsError。两种结果都不是整理文件想要的而且行为随平台漂移所以必须自己加一层防冲突逻辑检测到重名就追加_1、_2。# 适用于 Python 3.8def unique_path(dst):dst 已存在时返回带序号的新路径a.txt - a_1.txt - a_2.txtif not dst.exists():return dststem, suffix, parent dst.stem, dst.suffix, dst.parenti 1while True:candidate parent / f{stem}_{i}{suffix}if not candidate.exists():return candidatei 1四、完整脚本含 dry-run把上面的碎片拼成一个可执行的脚本。它默认只打印将要做什么加--apply才真正移动。# 适用于 Python 3.8import argparseimport shutilfrom pathlib import PathCATEGORIES {图片: {.jpg, .jpeg, .png, .gif, .bmp, .webp},文档: {.txt, .md, .pdf, .docx, .xlsx},压缩包: {.zip, .rar, .7z, .tar, .gz},}def classify(path):ext path.suffix.lower()for name, exts in CATEGORIES.items():if ext in exts:return namereturn 其他def unique_path(dst):if not dst.exists():return dststem, suffix, parent dst.stem, dst.suffix, dst.parenti 1while (parent / f{stem}_{i}{suffix}).exists():i 1return parent / f{stem}_{i}{suffix}def organize(root, applyFalse):root Path(root)moved, skipped 0, 0for item in root.iterdir():if not item.is_file() or item.name.startswith(.):continue # 跳过目录、隐藏文件含 .DS_Store 之类category classify(item)target_dir root / categoryif item.parent target_dir:continue # 已经在目标目录里避免自我移动dst unique_path(target_dir / item.name)if apply:target_dir.mkdir(exist_okTrue)shutil.move(str(item), str(dst))moved 1else:print(f[dry-run] {item.name} - {category}/{dst.name})print(f共处理 {moved} 个文件其余未匹配或已就位)if __name__ __main__:ap argparse.ArgumentParser(description按扩展名整理目录)ap.add_argument(root, help要整理的目录)ap.add_argument(--apply, actionstore_true, help真正执行移动)args ap.parse_args()organize(args.root, applyargs.apply)几点值得说明。shutil.move(src, dst)从Python 3.9 起接受路径类对象path-like object所以在 3.9 上直接传Path也可以为了兼容 3.8 及更早版本写成str(dst)更保险。mkdir(exist_okTrue)只会创建一层如果类别目录本身是嵌套的比如图片/2024要用parentsTrue。item.is_file()会跟随符号链接判断如果不希望跟链接用item.is_symlink()先排除。候选函数行为适合度os.rename()同分区改名跨分区抛OSError只在确定同盘时用shutil.move()跨分区自动退化为复制删除推荐shutil.copy2()复制并保留元数据源文件还在想保留原文件时Path.rename()底层就是os.rename()同样有跨盘限制同分区改名常见坑点❌ 边遍历目录边往里移动文件导致重复处理✅ 先list(root.iterdir())固化文件列表再操作并跳过已经处于目标目录内的项❌ 直接shutil.move同名文件以为行为一定一致✅ 文档说是否覆盖取决于os.rename()语义POSIX 上可能静默覆盖Windows 上可能抛FileExistsError整理前统一做重名检测并生成_1、_2后缀别依赖平台差异❌ 用path.suffix去解析archive.tar.gz以为拿到.tar.gz✅.suffix只给最后一个.gz要全部后缀用path.suffixes❌ 忘了.lower().JPG和.jpg分进两类✅ 一律先path.suffix.lower()再比对❌ 把隐藏文件和系统文件.DS_Store、Thumbs.db也一起归类✅ 用name.startswith(.)跳过必要时再维护一张黑名单❌ 用os.rename()把文件从 C 盘移到 D 盘✅ 跨分区会抛OSError: [WinError 17]改用shutil.move()❌ 脚本直接在真实目录上第一次就执行移动✅ 先跑 dry-run 打印计划确认无误再加--apply重要目录先备份❌ 手工拼路径字符串root / name✅ 用Path的/运算符拼接它会按平台选择分隔符总结步骤用什么关键点扫描Path.iterdir()只列一层筛掉目录与隐藏文件判定path.suffix.lower()后缀映射用集合定目标重名检测避免静默覆盖丢数据移动shutil.move()跨分区安全自动分类的难点从来不在分类算法而在移动这个不可逆动作上的防护。把 dry-run、重名检测、跳过隐藏文件这三件事做进去几十行的脚本就能从玩具变成敢用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑