手机导出的照片叫 IMG_0001、微信图片_2024xxxx,混在一起根本没法按时间找。下面脚本按 EXIF 拍摄时间重命名成 20230501_103000_IMG_0001.jpg,本次完整实跑过:3 张不同年份 EXIF 照片全部按拍摄日期命名,1 张无 EXIF 照片自动回退到文件修改时间,先预览再执行。
#!/usr/bin/env python3
"""按拍摄日期(EXIF DateTimeOriginal)批量重命名照片
用法: python3 rename_by_date.py <照片目录> [--go]
不加 --go 是预览模式,确认无误再执行"""
import sys, os, shutil
from pathlib import Path
from PIL import Image
def get_date(path):
try:
exif = Image.open(path)._getexif() or {}
v = exif.get(36867) or exif.get(306) # DateTimeOriginal / DateTime
if v:
d = v.strip() # "2023:05:01 10:30:00"
return d[:10].replace("-","").replace(":",""), d[11:19].replace(":","")
except Exception:
pass # 无EXIF/损坏 -> 回退修改时间
import datetime
t = datetime.datetime.fromtimestamp(os.path.getmtime(path))
return t.strftime("%Y%m%d"), t.strftime("%H%M%S")
def main(folder):
folder = Path(folder)
dry = "--go" not in sys.argv
used, n = set(), 0
for f in sorted(folder.iterdir()):
if f.suffix.lower() not in (".jpg",".jpeg",".png",".heic",".webp") or not f.is_file():
continue
d, t = get_date(f)
stem = f"{d}_{t}"; base = stem; i = 1
while base in used: # 同一秒多张 -> _1/_2
base = f"{stem}_{i}"; i += 1
used.add(base)
newname = f"{base}_{f.stem}{f.suffix.lower()}"
print(("[预览] " if dry else "") + f"{f.name} -> {newname}")
if not dry:
f.rename(folder / newname)
n += 1
print(f"共 {n} 张,{'预览模式' if dry else '已重命名'}")
if __name__ == "__main__":
main(sys.argv[1])
--- 预览 --- [预览] IMG_0001.jpg -> 20230501_103000_IMG_0001.jpg (EXIF 2023-05-01) [预览] IMG_0002.jpg -> 20241231_103000_IMG_0002.jpg (EXIF 2024-12-31) [预览] noexif.jpg -> 20261005_111701_noexif.jpg (无EXIF,回退修改时间) [预览] photo_a.jpg -> 20220115_103000_photo_a.jpg (EXIF 2022-01-15) --- 加 --go 执行后 ls --- 20220115_103000_photo_a.jpg 20230501_103000_IMG_0001.jpg 20241231_103000_IMG_0002.jpg 20261005_111701_noexif.jpg
2023:05:01,冒号分隔。直接替换 : 全删会把日期和时间糊成一坨;要先把前两个冒号变连字符再统一去分隔符。--go 只打印改名对照,人眼确认后再真跑。_1/_2,否则后面的 rename 直接把前面的顶掉。也可以用 PowerShell:Get-ChildItem *.jpg | Rename-Item -NewName {$_.LastWriteTime.ToString("yyyyMMdd_HHmmss") + "_" + $_.Name} —— 但它只能用修改时间,照片复制/转发过时间就不准了,EXIF 才是拍摄那一刻的真实时间。