Pythonで重複ファイル・重複写真を見つけて整理する方法

python
自動化・詳細手順

Pythonで重複ファイル・重複写真を見つけて整理する方法

ファイルの中身を比較して、重複だけを安全に見つけ出します

プログラミング未経験OK 所要時間の目安:20分程度

この記事は、「Pythonでできる日常の自動化6選」で紹介した⑤重複ファイル・重複写真の整理の詳しい作り方をまとめた記事です。

スマホからパソコンに何度も写真を取り込むうちに、同じ写真が「IMG_001.jpg」「IMG_001(1).jpg」のように別名で何個も残っていませんか?ファイル名ではなく中身そのものを比較することで、本当に同じファイルだけを正確に見つけ出すことができます。今回もPython標準の機能だけを使うので、追加のライブラリのインストールは不要です。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5

用意するもの

今回もPythonに標準で付属している機能だけを使います。pip installで追加のライブラリを用意する必要はありません。

  • Python本体
  • コードを書くためのエディタ(VS Codeなど)
INFO

Pythonのインストール、コードを書くためのエディタ(VS Code)の準備、コードの書き方・実行方法がまだの方は、先に「Pythonを始める前に:インストールとエディタの準備」を済ませてから、この続きに進んでください。

注意

このあとのコードは、実際にパソコン上のファイルを移動します。まずはテスト用に新しく作った空のフォルダにコピーしたファイルで試してから、普段使っているフォルダに適用することを強くおすすめします。

重複検出の仕組みを知る

ファイル名や更新日時で比較すると、「中身は同じだけど名前が違うファイル」を見逃してしまいます。そこでこの記事では、ファイルの中身そのものから計算される「ハッシュ値」という指紋のような値を使って比較します。中身が1バイトでも違えば、まったく違うハッシュ値になります。逆に言えば、ハッシュ値が同じ2つのファイルは、中身が完全に同じということになります。

MEMO

ハッシュ値の計算には、Python標準のhashlibというライブラリを使います。追加のインストールは不要です。

ファイルのハッシュ値を計算してみる

まずは1つのファイルで、ハッシュ値を計算する基本の書き方を試してみましょう。

import hashlib

def get_file_hash(file_path):
    hasher = hashlib.md5()
    with open(file_path, "rb") as f:
        while chunk := f.read(8192):
            hasher.update(chunk)
    return hasher.hexdigest()

print(get_file_hash("photo1.jpg"))

実行すると、ファイルの中身から計算された英数字の文字列(ハッシュ値)が表示されます。同じ中身のファイルであれば、ファイル名が違っても、どのパソコンで実行しても、必ず同じハッシュ値になります。

MEMO

ファイルを少しずつ(8192バイトずつ)読み込んでいるのは、動画などの大きなファイルでも一度にメモリを使いすぎないようにするための工夫です。

フォルダ内の重複ファイルを見つける

先ほどの関数を使って、フォルダ内のすべてのファイルのハッシュ値を計算し、同じハッシュ値を持つファイルをグループ化します。

from pathlib import Path
import hashlib

def get_file_hash(file_path):
    hasher = hashlib.md5()
    with open(file_path, "rb") as f:
        while chunk := f.read(8192):
            hasher.update(chunk)
    return hasher.hexdigest()

target_dir = Path.home() / "Desktop" / "test_photos"

hash_to_files = {}

for file in target_dir.iterdir():
    if file.is_file():
        file_hash = get_file_hash(file)
        hash_to_files.setdefault(file_hash, []).append(file)

# 同じハッシュ値のファイルが2つ以上あるものだけを「重複」として取り出す
duplicates = {h: files for h, files in hash_to_files.items() if len(files) > 1}

for file_hash, files in duplicates.items():
    print(f"重複グループ({len(files)}件):")
    for f in files:
        print(f"  {f.name}")

実行すると、中身が同じファイルどうしがグループごとに一覧表示されます。hash_to_files.setdefault(file_hash, []).append(file)は、「同じハッシュ値のリストがまだなければ新しく作り、あればそこに追加する」という書き方です。

重複候補を安全にまとめて確認する

重複が見つかっても、いきなり削除するのはおすすめしません。まずは、各グループの1つ目だけを「残すファイル」として、残りを専用のフォルダへ移動し、目で見て確認できるようにしましょう。

import shutil

review_dir = target_dir / "重複候補"
review_dir.mkdir(exist_ok=True)

for file_hash, files in duplicates.items():
    keep = files[0]
    remove_candidates = files[1:]
    print(f"残す: {keep.name}")
    for f in remove_candidates:
        shutil.move(str(f), str(review_dir / f.name))
        print(f"  移動: {f.name}")

print("重複候補フォルダへの移動が完了しました")

実行すると、「重複候補」という新しいフォルダが作られ、各グループの2つ目以降のファイルがそこに移動します。元のフォルダには重複のないファイルだけが残るので、あとは「重複候補」フォルダの中身を確認し、問題なければまとめて削除する、という流れが安全です。

POINT

shutil.moveの代わりにshutil.copyを使えば、元のファイルを残したまま「重複候補」フォルダにコピーするだけの、さらに安全な確認もできます。

うまくいかないときのトラブルシューティング

見た目は同じでも少し編集した写真は重複として検出されますか?
検出されません。ハッシュ値は中身が1バイトでも違うと別の値になるため、リサイズや再保存によってファイルの中身が変わった写真は、別のファイルとして扱われます。
ファイル数が多いと時間がかかりますか?
ファイルの合計サイズが大きいほど時間がかかりますが、一般的な写真や書類であれば、数千枚程度でも数分以内に終わることがほとんどです。

まとめ

Python標準のhashlibを使って、ファイルの中身からハッシュ値を計算し、重複ファイルを正確に見つける方法を紹介しました。見つけた重複はいきなり削除せず、まず専用フォルダに移動して確認する、という安全な流れで整理してみてください。

他の自動化アイデアも見てみる

家計簿の自動集計やExcel作業の自動化など、Pythonでできる生活の自動化アイデアを6つ紹介しています。

6テーマ一覧を見る

コメント

タイトルとURLをコピーしました