はじめに
Pythonで画像処理やZipを扱っているときに、なぜか __MACOSX という謎のフォルダや ._xxx という余計なファイルが出てきてエラーになった経験はありませんか?
僕も初めて機械学習用のデータセットを扱ったときに、Pillow(PIL)で画像を開こうとしたら
OSError: cannot identify image file
と怒られて「え?ファイル壊れてる?」とめちゃくちゃ時間を溶かしたことがあります。
原因は macOSが勝手に作る __MACOSX フォルダ でした。
この記事では、Pythonで実務中にハマったこの問題を整理して、実際の対処法をまとめます。
__MACOSX フォルダって何?
macOS の Finder でZip圧縮すると、自動で作られるメタデータ用フォルダ。
アイコンの位置やFinderの表示情報など「macOS独自の情報」を保存しています。
普段は見えないが、WindowsやLinuxで解凍すると __MACOSX ディレクトリ と ._image.jpg のようなファイル として出てきます。
例:
dataset.zip/
├── __MACOSX/
│ └── ._image1.jpg
├── image1.jpg
├── image2.jpg
Python から見ると「中身はゴミ」なので、処理に邪魔になるだけです。
Pythonで起こるトラブル
画像処理のエラー
from PIL import Image
import os
for fname in os.listdir("dataset"):
img = Image.open(os.path.join("dataset", fname))
このとき ._image1.jpg を読もうとしてエラー。
OSError: cannot identify image file
ファイル数が合わない
本当は100枚あるはずなのに、os.listdir すると ._xxx が混ざって101枚になってしまう。
機械学習で「train: 80, test: 20」に分けたいのにズレる…地味にイライラします。
Pythonでの対処法
1. フィルタして無視する
シンプルに「_MACOSX と . で始まるものを無視」します。
import os
files = [
f for f in os.listdir("dataset")
if not f.startswith("._") and f != "__MACOSX"
]
これだけで大体のトラブルは回避可能。
2. 再帰的に探索する場合
os.walk で下層まで探索するならこう書きます。
for root, dirs, files in os.walk("dataset"):
if "__MACOSX" in root:
continue
for f in files:
if f.startswith("._"):
continue
path = os.path.join(root, f)
print(path)
3. Zip解凍時にスキップ
最初から展開しないのが一番クリーンです。
import zipfile
with zipfile.ZipFile("dataset.zip") as zf:
for name in zf.namelist():
if "__MACOSX" in name or name.startswith("._"):
continue
zf.extract(name, "dataset")
4. そもそも入れない方法(macOS側)
Zipを作るときに -X オプションを付ければ、余計な情報を含めません。
zip -r -X dataset.zip dataset/
これで __MACOSX なしのクリーンなZipが作れます。
社内で配布するときや、Kaggle用のデータを作るときにめちゃ便利。
まとめ
__MACOSX は macOSがZipに勝手に入れるメタデータ用フォルダのこと。
Pythonで扱うときはエラーやファイル数不一致の原因になることがあります。
対処法は
- startswith("._") や __MACOSX を無視する
- os.walk でスキップする
- zipfile で展開時に除外する
- 根本的には zip -X で作成 すると最初から入らない
最後に
この __MACOSX 問題、初めて遭遇すると「Pythonが悪いのか?データが壊れてるのか?」と混乱しがちですが、実際には macOSの仕組み由来の「おまけファイル」 です。
Python側では 「無視する」 ことが正解。
実務でデータ処理や機械学習をしている人は、一度は遭遇するので「見かけたら捨てる」習慣を持つと楽になります。