Pythonでファイル名を一括リネーム!日付+連番で写真を整理して失敗しても元に戻せる自動化入門
「スマホから取り込んだ写真が IMG_0012.JPG だらけで、いつの写真か分からない…」「ファイル名を1個ずつ手で変えるのがつらい」
こういう地味な作業、意外と時間を取られますよね。
この記事では、Python の標準ライブラリだけでフォルダ内のファイル名を「日付+連番」に一括リネームするスクリプトを作ります。ただ名前を変えるだけでなく、
- いきなり変えずに「変更予定」だけを表示するお試しモード
- 同じ名前のファイルを上書きしない安全チェック
- 失敗しても元の名前に戻せる記録(CSV)
まで入れて、「うっかり大事なファイルを壊した…」を防ぐ形にします。一緒に見ていきましょう!
難易度:初級(for文・if文・リストが分かる方向け。追加のインストールは不要です)。動作確認は Python 3.13(Windows)で行いました。
ファイル名の一括リネームで気をつけること

リネームは一瞬で終わる処理ですが、やり直しがききにくいのが怖いところです。イメージとしては「消しゴムなしでボールペン清書する」ようなものですね。
そこで今回は、次の3段階で進めます👇
- 練習用のフォルダを作って試す(いきなり本物の写真フォルダで実行しない)
- お試しモードで変更予定を目で確認する
- 本番実行するときは変更前後の名前をCSVに記録し、いつでも元に戻せるようにする
使うのは pathlib(ファイル・フォルダ操作)、datetime(日付)、csv(記録)の3つだけです。
ステップ1:練習用のファイルを作る
まずは練習用に photos フォルダを作り、ダミーのファイルを5つ置きます。os.utime() を使うと、ファイルの更新日時を好きな日時に書き換えられるので、「撮った日がバラバラの写真」を再現できます。
# 練習用のファイルを作るスクリプト(本物の写真フォルダで試す前に!)
import os
from datetime import datetime
from pathlib import Path
folder = Path("photos")
folder.mkdir(exist_ok=True)
# (ファイル名, 更新日時)の組み合わせ
samples = [
("IMG_0012.JPG", "2026-09-28 10:15"),
("IMG_0013.jpg", "2026-09-28 10:16"),
("DSC00451.jpg", "2026-10-03 18:40"),
("スクリーンショット 2026-10-05.png", "2026-10-05 09:02"),
("memo.txt", "2026-10-05 12:00"),
]
for name, stamp in samples:
path = folder / name
path.write_text("dummy", encoding="utf-8")
t = datetime.strptime(stamp, "%Y-%m-%d %H:%M").timestamp()
os.utime(path, (t, t)) # 更新日時を書き換える
print(sorted(p.name for p in folder.iterdir()))
実行すると、photos フォルダに IMG_0012.JPG・IMG_0013.jpg・DSC00451.jpg・スクリーンショット 2026-10-05.png・memo.txt の5つができます。拡張子の大文字・小文字が混ざっているのもポイントです。
ステップ2:お試しモードで変更予定を確認する
ここがメインのスクリプトです。ポイントをまとめるとこんな感じです👇
TARGET_EXTSで画像(jpg・jpeg・png)だけを対象にする(memo.txtは変えない)p.suffix.lower()で拡張子を小文字にそろえて比べるので、.JPGも対象になる- ファイルを更新日時の古い順に並べ、日付ごとに
001,002… と連番をふる DRY_RUN = Trueの間は表示するだけで、ファイルには一切さわらない
import csv
from datetime import datetime
from pathlib import Path
FOLDER = Path("photos")
TARGET_EXTS = {".jpg", ".jpeg", ".png"} # 画像だけを対象にする
DRY_RUN = True # True の間は「予定」を表示するだけ。確認できたら False に
# 対象ファイルを更新日時の古い順に並べる
files = sorted(
(p for p in FOLDER.iterdir() if p.is_file() and p.suffix.lower() in TARGET_EXTS),
key=lambda p: p.stat().st_mtime,
)
plan = [] # (元のパス, 新しいパス)
counters = {} # 日付ごとの連番
for p in files:
day = datetime.fromtimestamp(p.stat().st_mtime).strftime("%Y-%m-%d")
counters[day] = counters.get(day, 0) + 1
new_name = f"{day}_{counters[day]:03d}{p.suffix.lower()}"
plan.append((p, p.with_name(new_name)))
# 名前がかぶるファイルがないかチェック(上書き事故を防ぐ)
for old, new in plan:
if new != old and new.exists():
raise SystemExit(f"中止: {new.name} はすでに存在します")
for old, new in plan:
print(f"{old.name} -> {new.name}")
if DRY_RUN:
print(f"\n[お試しモード] {len(plan)}件の変更予定を表示しました(まだ何も変えていません)")
else:
# 元に戻せるように、変更前後の名前をCSVに記録してからリネーム
with open("rename_log.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["old", "new"])
for old, new in plan:
writer.writerow([old.name, new.name])
old.rename(new)
print(f"\n{len(plan)}件をリネームしました(記録: rename_log.csv)")

実行結果はこうなりました。
memo.txt はちゃんと対象外になり、同じ日(9月28日)の2枚には _001 と _002 が付いています。IMG_0012.JPG の拡張子も .jpg にそろいました😊
ここが重要です👇 このスクリプトは何も変えずに「予定」だけを出しています。まず表示を見て、「意図どおりか?」を確認するクセをつけておくと安心です。
本番実行:DRY_RUN を False にする
予定に問題がなければ、DRY_RUN = False に書き換えてもう一度実行します。今回の環境では「4件をリネームしました(記録: rename_log.csv)」と表示され、フォルダの中身は次のようになりました。
2026-09-28_001.jpg/2026-09-28_002.jpg2026-10-03_001.jpg/2026-10-05_001.pngmemo.txt(そのまま)
同時に作られる rename_log.csv には、old,new の見出しの下に「元の名前,新しい名前」が1行ずつ記録されます。
上書き事故を防ぐチェックの意味
スクリプトの途中にある「名前がかぶるファイルがないかチェック」は、地味ですがとても大事です。
実は Path.rename() は、変更先に同じ名前のファイルがあったときの動きが OS によって違います。公式ドキュメントによると、Windows ではエラー(FileExistsError)になりますが、Mac や Linux では黙って上書きされます⚠️
実際に、リネーム済みのフォルダに「9月28日 8:00 の写真(IMG_0001.jpg)」を追加してもう一度実行してみると、新しい写真が 2026-09-28_001.jpg になろうとして既存のファイルとぶつかるため、次のように1件も変えずに止まりました。
中止: 2026-09-28_001.jpg はすでに存在します
つまり「迷ったら止まる」設計にしておけば、OS の違いを気にせず安全に使えるということです。
ステップ3:記録CSVを使って元に戻す
「やっぱり元の名前に戻したい!」というときのためのスクリプトです。
csv.DictReaderで記録を読み、row["old"]・row["new"]で取り出すreversed()で後から変えたものから順に戻す- 戻し先にすでにファイルがある場合などは、無理に戻さずスキップする
import csv
from pathlib import Path
FOLDER = Path("photos")
with open("rename_log.csv", newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
count = 0
for row in reversed(rows): # 後から変えたものから順に戻す
new_path = FOLDER / row["new"]
old_path = FOLDER / row["old"]
if new_path.exists() and not old_path.exists():
new_path.rename(old_path)
count += 1
else:
print(f"スキップ: {row['new']}")
print(f"{count}件を元の名前に戻しました")
print(sorted(p.name for p in FOLDER.iterdir()))

実行結果はこうなりました。4件すべてが元の名前に戻り、ステップ1で作った直後と同じ状態になっています。
これ、本番で使う前に「リネーム → 元に戻す」を練習用フォルダで一度やっておくのがおすすめです。戻せることが分かっていると、安心して本物のフォルダに使えますよ🚀
自分のフォルダで使うときの注意点
- まずはコピーしたフォルダで試す:
FOLDER = Path("photos")を自分のフォルダのパスに変えるだけで動きますが、最初はバックアップを取ってからにしましょう - 更新日時=撮影日とは限らない:コピーやダウンロードの方法によっては、更新日時がその作業をした日時に変わることがあります。お試しモードの表示で日付がおかしくないか確認してください
- 命名ルールは1行で変えられる:
new_nameの f文字列をf"旅行_{day}_{counters[day]:03d}{p.suffix.lower()}"のように変えれば、先頭に好きな文字を付けられます - rename_log.csv は上書きされる:本番実行のたびに作り直されるので、元に戻したいときは次の本番実行の前に戻しましょう
まとめ
今回は、Python の標準ライブラリだけでファイル名を一括リネームするスクリプトを作りました。ポイントをおさらいしましょう👇
pathlibのiterdir()・suffix・stat().st_mtime・rename()でファイル情報の取得と名前変更ができる- お試しモード(DRY_RUN)で変更予定を先に確認する
Path.rename()は OS によって上書きの動きが違うので、名前の衝突は自分でチェックして止める- 変更前後の名前をCSVに記録しておけば、いつでも元に戻せる
写真の整理だけでなく、請求書やスキャンした書類の整理にもそのまま応用できます。「むずかしそう」を「できそう」に変える第一歩として、ぜひ練習用フォルダで試してみてください😊
\ 読んだら、次は自分で作ってみよう /
筆者が運営する無料の入門コース「作って学ぶPythonプログラミング入門」では、こうした基本文法を、割り勘計算機・おみくじ・数当てゲームなどを作りながら身につけます。各章末の定着ドリル(全42問)で、自分の手で書く練習まで。最初の4レッスンは登録なしで読めます。
こちらも読まれています
📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。





