前回はWord文書(docxファイル)から画像ファイルをPythonで取り出しましたが、今回はExcelブックxlsxファイル)から画像ファイルを取り出します。

xlsxファイルもdocxファイルと同様に、「ZIP形式」で圧縮されているので、拡張子を.zipに書き換えて解凍すると埋め込まれている画像ファイルを取得できます

手動で展開して取り出すこともできますが、とても面倒な作業なので、今回もPythonで自動化する方法をご紹介します。

商品の管理などで「Excelに大量に貼り付けた画像を取り出したい!」といった場合に、ぜひ活用してみてください。Pythonに備え付けの標準ライブラリだけで簡単にプログラミングできます。

本記事の目次
本記事のプログラムは、「Windows10 + Python3.6」で実行確認しています。Excelブックは「Excel2019」で作成しています。

サンプルのExcelブック

今回は、以下のような「Excelのセルに挿入してある画像」を取り出して別ファイルに保存します。Excelのブックには、Excelの[挿入タブ]▶[画像]で、PNG形式の画像を挿入しています。

sample1.xlsx

xlsx sanmple

サンプルとして、同様のExcelブックを全部で3つ用意して、以下のようにPythonプログラムと同じフォルダー内の「xl_files」というフォルダーに入れておきます。

xl_files/
  │  ├ sample1.xlsx
  │  ├ sample2.xlsx
  │  └ sample3.xlsx
  │
  ├ xlsx_zip_research.py <= xlsxファイルの中身を覗いてみるプログラム
  ├ xlsx_extract_image.py <= 1つのxlsxファイルから画像を取り出すプログラム
  └ xlsx_files_extract_image.py <= 複数のxlsxファイルから画像を取り出すプログラム

PythonでExcelブックの中身を覗いてみる

Excelブックの中に格納されているファイルを調べるには、Pythonの標準ライブラリの「zipfileモジュール」を利用します。zipfileの詳細については、以下の公式ドキュメントで参照できます。

https://docs.python.org/ja/3/library/zipfile.html

ExcelブックをZIP形式のファイルとして、zipfile.ZipFile()で開き、その中に含まれるファイルリストをnamelist()で取得します。ファイルリストはループで1つずつ表示して確認します。

xlsx_zip_research.py
import zipfile

# xlsxをZIP形式のファイルとして開く
xlsx_zip = zipfile.ZipFile("xl_files/sample1.xlsx")
# xlsxの中にあるファイル一覧を取得
zipped_files = xlsx_zip.namelist()

# ファイルを1つずつ表示
for file in zipped_files:
    print(file)

xlsx_zip.close()
出力結果(xlsxファイルの中身)
[Content_Types].xml
_rels/.rels
xl/workbook.xml
xl/_rels/workbook.xml.rels
xl/worksheets/sheet1.xml
xl/theme/theme1.xml
xl/styles.xml
xl/sharedStrings.xml
xl/drawings/drawing1.xml
xl/media/image1.jpg <= 挿入されている画像ファイル
xl/media/image2.jpg <= 挿入されている画像ファイル
xl/worksheets/_rels/sheet1.xml.rels
xl/drawings/_rels/drawing1.xml.rels
xl/printerSettings/printerSettings1.bin
docProps/core.xml
docProps/app.xml

出力結果を見ると、Excelブックの中身は複数のファイルで構成されているのがわかります。

このリストから、sample1.xlsxには、image1.pngimage2.pngの2つの画像ファイルが挿入されていることが確認できます。ここで画像ファイルはxl/media/の中に格納されるので、以下のようにstartswith()を用いて選別できます。

xlsx_zip_research.py(改良)
...

# ファイルを1つずつ表示
for file in zipped_files:
    # 画像ファイルだけ選別
    if file.startswith("xl/media/"):
        print(file)

xlsx_zip.close()
出力結果
xl/media/image1.jpg
xl/media/image2.jpg

なお、画像ファイルの名前は、挿入した元のファイル名ではなくimage1.png,image2.png..のように「連番」が自動で振られます。Excelブックに挿入した順番ではなく、上から順に付番されるようです(公式なドキュメントを見つけることはできませんでしたが、何度か試してみるとそうなりました)。

Word文書の場合は、画像ファイルはword/media/の中に格納されています。

Pythonでxlsxから画像ファイルを取り出す方法

xlsxファイルの中の画像ファイルを読み取るには、zipfileモジュールで開いたxlsxファイルから、open()によりパスを指定して画像ファイルを開きます。例えば、1つめの画像ファイルは、xlsx_zip.open("xl/media/image1.png")で開けます。

次に、開いた画像ファイルからread()を実行すると、画像のデータが読み込めるので、それをファイル名を指定して保存します。

保存する画像のファイル名は、どのExcelブックから取り出したのかが分かるように、先頭に「xlsxファイル名_」を付けます。ここで、拡張子を除いたファイル名を取得できるstemなど、パス操作に便利な標準ライブラリの「pathlib 」を活用しています。

xlsx_extract_image.py
import zipfile
from pathlib import Path

# 画像を取り出すExcelブックのパス
xlsx_path = Path("xl_files/sample1.xlsx")

# zipfileモジュールでExcelブックを開く
xlsx_zip = zipfile.ZipFile(xlsx_path)
zipped_files = xlsx_zip.namelist()

# 画像を保存するフォルダー
img_dir = Path("xl_images")
img_dir.mkdir(exist_ok=True)

# xlsxファイルの中身を1つずつループ
for file in zipped_files:
    if file.startswith("xl/media/"):
        # 画像ファイルを開く
        img_file = xlsx_zip.open(file)
        # 画像ファイルの読み込み
        img_bytes = img_file.read()

        # 保存する画像ファイル名には、「xlsxファイル名_」を先頭に付ける
        img_path = img_dir / (xlsx_path.stem + "_" + Path(file).name)
        # 画像ファイルの保存
        with img_path.open(mode="wb") as f:
            f.write(img_bytes)
        img_file.close()

xlsx_zip.close()

画像ファイルは「xl_images」というフォルダーを作成して保存します。上記のPythonプログラム実行すると、以下のように画像ファイルが保存されます。

出力結果
xl_files/
  │  └ sample1.xlsx
  │
  ├ xl_images/
  │  ├ sample1_image1.png <= 取り出した画像ファイル
  │  └ sample1_image2.png <= 取り出した画像ファイル
  │
  └ xlsx_extract_image.py
sample1_image1.png

sample1_image1.png

sample1_image2.png

sample1_image2.png

このように、zipfileモジュールを使うと、ZIPファイルの中にある特定のファイルのデータを直接読み取れます。ZIPファイルを展開する必要がないので、とても便利です。

ZIPファイルを展開したい場合は?

zipfileモジュールには、extract()というZIPを「展開」するメソッドもあります。このメソッドを用いて、仮のフォルダーに展開してから、画像ファイルをコピーすることもできます。

複数のxlsxファイルから自動で画像ファイルを取り出してみる

複数のxlsxファイルから、自動で画像ファイルを取り出せるように、もう少しコードを改良します。以下のように関数にして、「xl_files」フォルダー内にあるすべてのxlsxファイルから画像ファイルを抽出して保存します。

xlsx_files_extract_image.py
import zipfile
from pathlib import Path

# Excelブック内の画像ファイルを保存する関数
def save_xlsx_image(xlsx_path, save_dir):
    xlsx_zip = zipfile.ZipFile(xlsx_path)
    zipped_files = xlsx_zip.namelist()
    for file in zipped_files:
        if file.startswith("xl/media/"):
            img_file = xlsx_zip.open(file)
            img_bytes = img_file.read()
            img_path = save_dir / (xlsx_path.stem + "_" + Path(file).name)
            with img_path.open(mode="wb") as f:
                f.write(img_bytes)
            img_file.close()
    xlsx_zip.close()

xl_files_dir = Path("xl_files")
img_dir = Path("xl_images")
img_dir.mkdir(exist_ok=True)

# xl_filesフォルダー内のxlsxファイルを検索
for xlsx in xl_files_dir.glob("*.xlsx"):
    save_xlsx_image(xlsx, img_dir)

実行すると、今回は1つのExcelブックに2つ画像が挿入されているので、以下のように合計6つの画像ファイルを取り出して保存できたのが確認できます。

出力結果
xl_files/
  │  ├ sample1.xlsx
  │  ├ sample2.xlsx
  │  └ sample3.xlsx
  │
  ├ xl_images/
  │  ├ sample1_image1.png <= 取り出した画像ファイル
  │  ├ sample1_image2.png <= 取り出した画像ファイル
  │  ├ sample2_image1.png <= 取り出した画像ファイル
  │  ├ sample2_image2.png <= 取り出した画像ファイル
  │  ├ sample3_image1.png <= 取り出した画像ファイル
  │  └ sample3_image2.png <= 取り出した画像ファイル
  │
  └ xlsx_files_extract_image.py

現在のMicrosoft Officeのファイルは、ZIP形式で複数のファイルを圧縮しているので、今回のようにzipfileモジュールで直接中身を読むことができます。今回と同じ方法で、Word文書から画像を取り出すには、以下のページを参考にしてください。

no image
ワード文書(docxファイル)は、実は「ZIP形式」で圧縮されているので、拡張子を.zipに書き換えて解凍すると埋め込まれている画像ファイルを取得できます。 手作業では結構な手間になるので、今回…