Python で MIME Type を取得する

## はじめに

Python の標準ライブラリの [mimetypes モジュール](https://docs.python.org/ja/3/library/mimetypes.html) を使って、
MIME Type を取得しようとしたら少しだけハマったのでメモを残す。

[公式ドキュメント](https://docs.python.org/ja/3/library/mimetypes.html) にもしっかり書いてあるが、
**「ファイル名拡張子に関連付けられた MIME 型とを変換します」** となっている。
どうやらファイル名に含まれる拡張子しか見てくれていないっぽい。

今回は拡張子は無しの状態でファイルを判定したかったので、
mimetypes モジュールではダメだったというお話です。

## mimetypes モジュール で試してみた

### 対象のファイルは png 画像

```py:main.py
import mimetypes

IMAGE_FILE_PATH = './image.png'

print('===== .png =====')
print(f'-- mimetypes: {mimetypes.guess_type(IMAGE_FILE_PATH)[0]}')
```

```bash:実行結果
$ python main.py 
===== .png =====
-- mimetypes: image/png
```

ちゃんと取れてます。

### 拡張子無しで試す

```py:main.py
import mimetypes
import shutil

IMAGE_FILE_PATH = './image.png'

print('===== 拡張子無し =====')
tmp = './dummy_image'
shutil.copy(IMAGE_FILE_PATH, tmp)
print(f'-- mimetypes: {mimetypes.guess_type(tmp)[0]}')
```

```bash:実行結果
$ python main.py 
===== 拡張子無し =====
-- mimetypes: None
```

None になってしまった。

### .mp4 に偽装して試してみる

```py:main.py
import mimetypes
import shutil

IMAGE_FILE_PATH = './image.png'

print('===== 拡張子を .mp4 の偽装 =====')
tmp = './dummy_image.mp4'
shutil.copy(IMAGE_FILE_PATH, tmp)
print(f'-- mimetypes: {mimetypes.guess_type(tmp)[0]}')
```

```bash:実行結果
$ python main.py 
===== 拡張子を .mp4 の偽装 =====
-- mimetypes: video/mp4
```

`video/mp4` と判断されてしまった。

## 代わりに python-magic モジュールを使用

[python-magic](https://github.com/ahupp/python-magic) は `libmagic` を使ってファイル種別を判定してくれる。
また `libmagic` を使用するため、実行環境に `libmagic` がインストールされている必要がある

インストールされていない場合は以下のように実行時にエラーが出てしまいます。

```bash:libmagicがインストールされていない環境でpython-magicを使った時のエラー
ImportError: failed to find libmagic.  Check your installation
```

### libmagic をインストール

自分は Mac を使用しているため、Homebrew でインストールしました。

```bash:libmagicをインストール
$ brew install libmagic
```

### python-magic をインストール

まずは python-magic モジュールをインストール

```bash
$ pip install python-magic
```

## python-magic モジュールで試してみた

試し方は前述の mimetypes モジュールと同じです。

- png 画像
- 上記をコピーした拡張子無しファイル
- 同様に拡張子を .mp4 にしたファイル

### png 画像

```py:main.py
import magic

IMAGE_FILE_PATH = './image.png'

print('===== .png =====')
print(f'-- python-magic: {magic.from_file(IMAGE_FILE_PATH, mime=True)}')
```

```bash:実行結果
$ python main.py 
===== .png =====
-- python-magic: image/png
```

普通に取得できます。

### 拡張子無し

```py:main.py
import shutil

import magic

IMAGE_FILE_PATH = './image.png'

print('===== 拡張子無し =====')
tmp = './dummy_image'
shutil.copy(IMAGE_FILE_PATH, tmp)
print(f'-- python-magic: {magic.from_file(tmp, mime=True)}')
```

```bash:実行結果
$ python main.py 
===== 拡張子無し =====
-- python-magic: image/png
```

mimetypes では None だったのに対し、ちゃんと取得できています。

### .mp4 に偽装

```py:main.py
import shutil

import magic

IMAGE_FILE_PATH = './image.png'

print('===== 拡張子を .mp4 の偽装 =====')
tmp = './dummy_image.mp4'
shutil.copy(IMAGE_FILE_PATH, tmp)
print(f'-- python-magic: {magic.from_file(tmp, mime=True)}')
```

```bash:実行結果
$ python main.py 
===== 拡張子を .mp4 の偽装 =====
-- python-magic: image/png
```

mimetypes では `video/mp4` だったのに対し、ちゃんと取得できています。

## まとめ

ファイル種別判定には mimetypes より python-magic を使ったほうが良い！

```py:main.py
import mimetypes
import shutil

import magic

IMAGE_FILE_PATH = './image.png'

print('===== .png =====')
print(f'-- mimetypes: {mimetypes.guess_type(IMAGE_FILE_PATH)[0]}')
print(f'-- python-magic: {magic.from_file(IMAGE_FILE_PATH, mime=True)}')

print('')
print('===== 拡張子無し =====')
tmp = './dummy_image'
shutil.copy(IMAGE_FILE_PATH, tmp)
print(f'-- mimetypes: {mimetypes.guess_type(tmp)[0]}')
print(f'-- python-magic: {magic.from_file(tmp, mime=True)}')

print('')
print('===== 拡張子を .mp4 の偽装 =====')
tmp = './dummy_image.mp4'
shutil.copy(IMAGE_FILE_PATH, tmp)
print(f'-- mimetypes: {mimetypes.guess_type(tmp)[0]}')
print(f'-- python-magic: {magic.from_file(tmp, mime=True)}')
```

```bash:実行結果
$ python main.py 
===== .png =====
-- mimetypes: image/png
-- python-magic: image/png

===== 拡張子無し =====
-- mimetypes: None
-- python-magic: image/png

===== 拡張子を .mp4 の偽装 =====
-- mimetypes: video/mp4
-- python-magic: image/png
```