下载文件夹一团糟?30 行 Python 写个自动归档小工具

下载文件夹一团糟?30 行 Python 写个自动归档小工具

这是什么:一个只干一件事的小工具

每个人的下载文件夹最后都会变成同一个样子:安装包、截图、PDF、压缩包混在一起,找东西全靠搜索框,删东西又怕误删。这个小工具只干一件事——把下载文件夹里的文件按类型分进不同的子文件夹:图片进“图片”,文档进“文档”,压缩包进“压缩包”,以此类推。

它不联网、不装任何第三方库,只用 Python 自带的标准库,代码 30 行左右,改两行就能按自己的习惯调整分类。前置知识只需要两点:电脑上装了 Python 3,会打开终端(命令行)输入命令。没有装 Python 的话,先去 Python 官网下载安装,安装时勾选把 Python 加到系统 PATH。

完整代码:复制下来就能跑

把下面这段代码保存为 organize.py,放在哪里都行,不用和下载文件夹放一起:

#!/usr/bin/env python3
"""把下载文件夹按类型自动归档:干跑先看,确认后再动真格。"""
import argparse, shutil
from pathlib import Path

RULES = {
    "图片": {".jpg", ".jpeg", ".png", ".gif", ".webp", ".heic"},
    "文档": {".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".md"},
    "压缩包": {".zip", ".rar", ".7z", ".tar", ".gz"},
    "视频": {".mp4", ".mkv", ".mov", ".avi"},
    "音频": {".mp3", ".flac", ".wav", ".aac"},
    "安装包": {".exe", ".msi", ".dmg", ".pkg", ".apk"},
}
SKIP_SUFFIXES = {".part", ".crdownload", ".tmp", ".download"}

def classify(p: Path) -> str:
    for folder, exts in RULES.items():
        if p.suffix.lower() in exts:
            return folder
    return "其他"

def unique(target: Path) -> Path:
    if not target.exists():
        return target
    n = 2
    while True:
        cand = target.with_name(f"{target.stem}-{n}{target.suffix}")
        if not cand.exists():
            return cand
        n += 1

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("src", help="下载文件夹路径")
    ap.add_argument("--go", action="store_true", help="不加 --go 只干跑、不移动文件")
    args = ap.parse_args()
    src = Path(args.src).expanduser()
    moved = skipped = 0
    for p in sorted(src.iterdir()):
        if not p.is_file() or p.name.startswith("."):
            skipped += 1
            continue
        if p.suffix.lower() in SKIP_SUFFIXES:
            print(f"跳过(未下载完):{p.name}")
            skipped += 1
            continue
        dest = unique(src / classify(p) / p.name)
        if args.go:
            dest.parent.mkdir(parents=True, exist_ok=True)
            shutil.move(str(p), str(dest))
        print(f"{p.name} -> {dest.relative_to(src)}")
        moved += 1
    print(f"共 {moved} 个文件,跳过 {skipped} 个" + ("(已移动)" if args.go else "(干跑,未移动)"))

if __name__ == "__main__":
    main()

怎么用:先干跑,确认无误再动真格

整理文件的工具最怕一点:还没看清它要干什么,文件已经被挪走了。所以这个脚本默认是干跑模式,只打印计划、不移动任何文件:

python3 organize.py ~/Downloads

终端会逐行打印 report.pdf -> 文档/report.pdf 这样的计划。确认没问题,再加 --go 真正执行:

python3 organize.py ~/Downloads --go

把 ~/Downloads 换成你自己的下载文件夹路径即可。Windows 用户把路径换成类似 C:\Users\你的用户名\Downloads 的形式,注意先干跑一次。

三个容易踩坑的细节,它都处理了

第一,未下载完的文件不动。 浏览器下载中的文件会带 .part、.crdownload 这类临时后缀,脚本会直接跳过并打印提示,不会把一个只下了一半的文件归档成“完成品”。

第二,重名文件不覆盖。 目标文件夹里已经有个同名文件时,新文件会自动改成 report-2.pdf、report-3.pdf,而不是悄悄覆盖旧文件。旧文件永远不会因为一次归档而丢失。

第三,只动顶层文件,不碰子文件夹和隐藏文件。 已经分好类的子文件夹、系统隐藏文件都原样不动。脚本认的是文件后缀:后缀不在规则表里的,一律进“其他”,不会被删掉或乱丢。

实测结果

我在测试文件夹里放了 8 个文件(PDF、图片、压缩包、音频、视频、安装包各一份,外加一个 .part 临时文件和一个隐藏文件),先干跑确认计划无误,再用 --go 执行:8 个文件全部按类型进了对应子文件夹,临时文件和隐藏文件原样留在原地。之后又往根目录放了一个同名 report.pdf 再跑一次,它被正确归档为 文档/report-2.pdf,原文件安然无恙。

还能怎么改:两处就够

  • 改分类:往 RULES 里加一行就行,比如把 .epub、.mobi 归进“电子书”,把 .psd 归进“设计”。
  • 定时跑:觉得手动跑麻烦,可以把它挂到系统的定时任务里每周跑一次(Linux 和 macOS 用 cron,Windows 用任务计划程序),每次跑之前它还是会先打印计划,日志留着,出问题能回查。

最后提醒一句:第一次用任何整理脚本之前,先对下载文件夹做一次备份,或者先在复制出来的测试文件夹里跑一遍。工具的价值是省时间,不是制造一次“文件去哪了”的寻宝游戏。