我的桌面 download 文件夹,已经变成了黑洞。
下过来的 PDF、图片、压缩包、安装包,全堆在一块。每次找东西都要翻半天,最后实在忍不了,写了个 Python 脚本自动归类。折腾一晚上,终于清爽了。
痛点:不是不想整理,是整理一次马上又乱
我之前试过手动建文件夹:
Downloads/
├── 文档/
├── 图片/
├── 软件/
└── 压缩包/

理完挺舒服,但不超过三天,根目录又是一堆新文件。人都有惰性,下载完顺手就关了保存对话框,根本不想选目录。
核心问题不是”不会整理”,是”每次都要做选择”。
所以我要的不是手动分类,而是让脚本在后台自动把新文件搬进对应文件夹。
方案:按扩展名自动分类 + 按日期归档
脚本思路很简单:
- 扫描 `~/Downloads` 根目录下的文件
- 根据扩展名判断类型
- 移动到对应的分类目录
- 每个分类目录下再按月归档
代码如下:
#!/usr/bin/env python3
"""
文件自动整理脚本
把 ~/Downloads 根目录的文件按扩展名分类,并按月份归档。
"""
import os
import shutil
from datetime import datetime
from pathlib import Path
DOWNLOADS = Path.home() / "Downloads"
# 扩展名 -> 分类目录
CATEGORY_MAP = {
"文档": [".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt", ".md"],
"图片": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp"],
"软件": [".dmg", ".pkg", ".exe", ".msi", ".app"],
"压缩包": [".zip", ".rar", ".7z", ".tar", ".gz", ".bz2"],
"视频": [".mp4", ".mov", ".avi", ".mkv", ".flv"],
"音频": [".mp3", ".wav", ".flac", ".aac", ".m4a"],
"代码": [".py", ".js", ".html", ".css", ".json", ".yaml", ".yml", ".sh"],
}
# 反向映射:扩展名 -> 分类
EXT_TO_CATEGORY = {}
for category, exts in CATEGORY_MAP.items():
for ext in exts:
EXT_TO_CATEGORY[ext.lower()] = category
def get_category(path: Path) -> str:
ext = path.suffix.lower()
return EXT_TO_CATEGORY.get(ext, "其他")
def organize():
moved = 0
for item in DOWNLOADS.iterdir():
if not item.is_file():
continue
# 跳过隐藏文件和本脚本
if item.name.startswith(".") or item.name == "organize.py":
continue
category = get_category(item)
month = datetime.fromtimestamp(item.stat().st_mtime).strftime("%Y-%m")
target_dir = DOWNLOADS / category / month
target_dir.mkdir(parents=True, exist_ok=True)
target = target_dir / item.name
# 处理重名
counter = 1
stem = target.stem
suffix = target.suffix
while target.exists():
target = target_dir / f"{stem}_{counter}{suffix}"
counter += 1
shutil.move(str(item), str(target))
print(f"Moved: {item.name} -> {category}/{month}/")
moved += 1
print(f"n整理完成,共移动 {moved} 个文件。")
if __name__ == "__main__":
organize()

跑一次,把我桌面 200 多个文件全归好了。根目录只剩几个分类文件夹,看着顺眼多了。
踩坑一:隐藏文件和脚本自身被误移动
第一次跑的时候,脚本把 `.DS_Store` 也当成普通文件移走了。 macOS 用户都知道这玩意儿有多烦,每个文件夹一个,移动它没问题,但它会污染分类目录。
解决办法:判断文件名以 `.` 开头就跳过。
另外脚本自身如果也放在 `Downloads` 里,会被自己移走。加一行判断跳过 `organize.py`。
踩坑二:重名文件直接覆盖
同一批下载里经常会有同名文件,比如 `image.png`、`image (1).png`。`shutil.move` 默认会直接覆盖目标文件,我差点把几个重要文件搞丢。
解决办法:在移动前检查目标是否存在,存在就加 `_1`、`_2` 后缀,直到不冲突。
counter = 1
stem = target.stem
suffix = target.suffix
while target.exists():
target = target_dir / f"{stem}_{counter}{suffix}"
counter += 1

这个逻辑虽然简单,但没加之前就是隐患。
踩坑三:分类目录本身被扫进去
脚本跑过一次后,`Downloads` 下已经有了 `文档/`、`图片/` 这些分类目录。如果脚本写得不严谨,下次运行时可能把这些目录也当成”待整理的文件”处理。
我的处理方式是只处理 `is_file()`,目录直接跳过。这样分类目录不会再被碰。
踩坑四:扩展名大小写不一致
macOS 默认不区分大小写,但 Python 的 `path.suffix` 是区分大小写的。`.JPG` 和 `.jpg` 如果不做 `.lower()`,会被分到”其他”里。
解决办法:所有扩展名比较前都转成小写。
踩坑五:按月归档还是按类型平铺?
一开始我纠结分类目录下要不要再按月份分。有人可能喜欢所有 PDF 都在一起,找起来方便。
但试了一下,单一目录文件太多时 Finder 会卡。按月归档后,每个目录文件数可控,找最近下载的东西也很快。
最终结构:
Downloads/
├── 文档/
│ ├── 2026-07/
│ └── 2026-06/
├── 图片/
│ ├── 2026-07/
│ └── 2026-06/
└── ...
进阶:配合 crontab 定时执行
手动跑一次只是治标不治本。我把它加到 crontab,每 15 分钟自动执行一次:
*/15 * * * * /Users/mac/.pyenv/shims/python3 /Users/mac/Downloads/organize.py >> /tmp/organize.log 2>&1
这样下载完不用管,过一会儿文件自己就归位了。
总结
这个脚本技术含量不高,但解决的是一个真实痛点:**重复决策带来的消耗**。
踩坑经验:
- 隐藏文件和脚本自身要跳过,避免自毁。
- 移动文件前先处理重名,防止覆盖。
- 只处理文件不处理目录,避免分类目录被误伤。
- 扩展名统一转小写,兼容 macOS 和 Windows 习惯。
- 单一目录文件过多会卡,建议按月再细分。
现在我的 Downloads 根目录每天都是干净的。省下的时间不多,但心情好了很多。