「うわっ…私の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別に設定するなら、次のような値になります。
Windows | D:\\wordpress\\uploads |
Linux | /var/www/wp/uploads |
あとは、以下のコード部分に関する補足です。
for image_path in image_path_list: eraser_exif(image_path)
もちろん、ファイルの存在チェックがあればベターです。
例外処理も同じく。
このあたりは、各自で自由に入れてください。
さらには、画像毎にEXIF有無のチェックもすればよいでしょう。
その場合は、以下の記事で紹介したExifReadを使えます。
ExifReadを使えば、画像のEXIF情報を抽出できます。