Pythonで指定フォルダ以下にある画像のEXIF情報を削除する

Pythonで指定フォルダ以下にある画像のEXIF情報を削除する プログラミング

「うわっ…私のEXIF情報、漏れすぎ…?」

あなたのサイトは大丈夫ですか?
こんな状況になっていませんか?

あなただけなら、そこまで大事ではないかもしれません。
そう言っても、住所などの個人情報が漏れるのは避けたいところです。
しかし、あなたの作ったサイトでユーザーのEXIF情報が漏れていたら・・・

それは、かなりの問題になるでしょう。
サイト・サービスの信頼性という点では、大きなマイナスになります。

こんなときは、一気にEXIF情報を削除することを考えるでしょう。
でも、一体どうしたらいいのと迷う場合は、この記事をご覧ください。

安全確実に大量の画像から、EXIF情報を削除する方法を解説していきます。

本記事の内容

  • 「指定フォルダ以下にある画像のEXIF情報を削除する」を機能定義する
  • 【関数化】指定フォルダ以下の画像のパスを取得する
  • 【関数化】画像からEXIF情報を削除する
  • 【サンプルコード】指定フォルダ以下にある画像のEXIF情報を削除する

それでは、上記に沿って解説していきます。

「指定フォルダ以下にある画像のEXIF情報を削除する」を機能定義する

「指定フォルダ以下にある画像のEXIF情報を削除する」

今回は、この仕様を満たすプログラムを開発していきます。
この仕様は、大きく次の二つの機能に分けることができます。

  • 指定フォルダ以下の画像のパスを取得する
  • 画像からEXIF情報を削除する

また、指定フォルダ以下にはフォルダもある前提です。
つまり、サブフォルダがあることになります。

もちろん、サブフォルダにはフォルダも画像もあるという想定です。
なお、階層は何階層あるかはわかりません。

では、以下でこれらの機能を開発していきます。
まずは、「指定フォルダ以下の画像のパスを取得する」です。

【関数化】指定フォルダ以下の画像のパスを取得する

具体的には、WordPressを想定しましょう。
具体性がある方が、イメージしやすいです。

WordPressは、次のフォルダの下に画像などのファイルがアップロードされます。

/wp-content/uploads

直接上記フォルダにアップロードはされません。
通常は、uploadsの下に西暦(2020、2021など)でフォルダが作成されます。

さらに、その西暦フォルダの下に月(1、2,3・・・)でフォルダが作成されるのです。
その月フォルダの下に画像が、アップロードされることになります。
サムネイルなどのサイズ別画像も、この月フォルダに作成されます。

言葉による説明より、見た方が絶対に早いですね。

uploads
├── 2019
│   └── 12
├── 2020
│   ├── 01
│   ├── 02
│   ├── 03
│   ├── 04
│   ├── 05
│   ├── 06
│   ├── 07
│   ├── 08
│   ├── 09
│   ├── 10
│   ├── 11
│   └── 12
└── 2021
    ├── 01
    ├── 02
    └── 03

上記のような構成のもとにある画像をすべて洗い出していきます。
それぞれのパスを取得すれば、後は何とでもなります。

ここまで見てきた内容をPythonの関数にしてみましょう。

関数化

関数のコードは以下。

from pathlib import Path
import re

def get_image_path_list(target):
    # 結果用リスト初期化
    result_list = []
    # Pathクラス利用
    path = Path(target)
    # target以下のすべてのパス(フォルダ・ファイル含め)
    all_path_list = path.glob('**/*')
    # 拡張子が画像ファイルであれば画像パスと判定する
    # 大文字(例えば、test.JPG)でも画像と判定する
    for p in all_path_list:
        hit = re.search('/*\.(jpg|jpeg|png|gif|bmp)', str(p), flags=re.IGNORECASE)
        if hit:
            result_list.append(p)

    return result_list

引数targetには、フォルダのパスを設定します。
内容的には、コメントをしっかり残しています。
そちらを確認してください。

この関数を短くすると、次のようにコーディングできます。

def get_image_path_list_bk(target):
    path = Path(target)
    result_list = [p for p in path.glob('**/*') if re.search('/*\.(jpg|jpeg|png|gif|bmp)', str(p), flags=re.IGNORECASE)]
    return result_list

個人的には、短く書くよりは可読性の高いコーディングが好きですね。

【関数化】画像からEXIF情報を削除する

EXIF情報を削除する方法は、何パターンかあります。
今回は、Pillowライブラリを利用した方法を説明します。

この方法が、最も確実にEXIF情報を削除できます。
削除というよりは、EXIF情報抜きで画像を新規に作り直します。

SQL文で言うとUPDATEではなく、DELETE + INSERTに近い考え方です。
よって、プログラムもこの考え方をベースにコーディングすることなります。

上記で出てきたPillow(PIL)に関しては、次の記事で説明しています。

もしまだインストールしていないなら、上記の記事を参考にしてください。
では、画像からEXIF情報を削除する機能を関数にしていきます。

関数化

関数のコードは以下。

from PIL import Image

def eraser_exif(file):

    # 画像を開く
    with Image.open(file) as src:
        # 次の3つを取得
        data = src.getdata()
        mode = src.mode
        size = src.size

        # 上記で取得したデータをもとに新規で画像を作成=上書き
        with Image.new(mode, size) as dst:
            dst.putdata(data)
            dst.save(file)

エラー処理も入れずに、少々強引なコードです。
エラー処理が必要な場合は、各自で入れてください。

引数fileは、画像のパスが設定されることを想定しています。
内容としては、上記で述べたようにPillowで画像を新規に作成しているだけです。

では、これにて必要な関数が用意できました。
最後に、これらの関数を利用したコードを見ていきましょう。

【サンプルコード】指定フォルダ以下にある画像のEXIF情報を削除する

上記で作成してきた関数を用いたサンプルコードは、以下。

from pathlib import Path
import re
from PIL import Image

TARGET_DIR = "uploadsのパス"

def get_image_path_list(target):
    # 結果用リスト初期化
    result_list = []
    # Pathクラス利用
    path = Path(target)
    # target以下のすべてのパス(フォルダ・ファイル含め)
    all_path_list = path.glob('**/*')
    # 拡張子が画像ファイルであれば画像パスと判定する
    # 大文字(例えば、test.JPG)でも画像と判定する
    for p in all_path_list:
        hit = re.search('/*\.(jpg|jpeg|png|gif|bmp)', str(p), flags=re.IGNORECASE)
        if hit:
            result_list.append(p)

    return result_list


def eraser_exif(file):
    # 画像を開く
    with Image.open(file) as src:
        # 次の3つを取得
        data = src.getdata()
        mode = src.mode
        size = src.size


        # 上記で取得したデータをもとに新規で画像を作成=上書き
        with Image.new(mode, size) as dst:
            dst.putdata(data)
            dst.save(file)


image_path_list = get_image_path_list(TARGET_DIR)

for image_path in image_path_list:
    eraser_exif(image_path)

関数に関しては、説明は不要ですね。
定数に関して、一応説明をしておきます。

TARGET_DIR = "uploadsのパス"

ここは、基本的には絶対パスを入れましょう。
OS別に設定するなら、次のような値になります。

WindowsD:\\wordpress\\uploads
Linux/var/www/wp/uploads

あとは、以下のコード部分に関する補足です。

for image_path in image_path_list:
    eraser_exif(image_path)

もちろん、ファイルの存在チェックがあればベターです。
例外処理も同じく。
このあたりは、各自で自由に入れてください。

さらには、画像毎にEXIF有無のチェックもすればよいでしょう。
その場合は、以下の記事で紹介したExifReadを使えます。

ExifReadを使えば、画像のEXIF情報を抽出できます。

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