[Python] ZIP形式によるデータアーカイブと圧縮

Python ファイル操作

ZIPファイルフォーマットは、アーカイブ(書庫)とデータ圧縮を行うための標準フォーマットです。Pythonではzipfileモジュールを使ってZIP形式のファイル作成や書庫ファイルの展開などを行います。本記事では、このモジュールの使い方についてまとめます。
# 2018/11/12 記事更新
# 2019/02/20 記事更新

確認した環境

  • OS: Ubuntu 16.04LTS
  • Python: ver3.7.0

ZIPファイルフォーマットについて

ZIPはアーカイブ(書庫化)データ圧縮を行う標準フォーマットです。
ここで、それぞれの用語の意味について簡単におさらいしておきます。

  • アーカイブ(書庫化): 複数のファイルを一つのファイルにまとめること。
  • データ圧縮: データの情報量を保ったままデータ量を減らして別のデータに変換すること。zipfileモジュールは圧縮アルゴリズム(zlib、bz2、Lzma等)を選択することができます。

ZipFileオブジェクトの生成

PythonでZIPファイルを操作するには、まずはZipFile()メソッドを使ってZipFileオブジェクトを生成します。このメソッドの使い方は以下です。

class zipfile.ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True, compresslevel=None)

第1引数fileは、操作するファイル名を文字列で設定します。
第2引数modeは後述するように、ZIPファイルの操作によって以下のように設定します。

  • ZIPファイルを展開する場合は、’r’
  • ZIPファイルを新規作成する場合は、’w’
  • ZIPファイルにファイルを追加する場合は、’a’

第3引数compressionは、適用する圧縮アルゴリズムを指定します。詳細は下表を参照ください。

下表はこれらを表にまとめたものです。

パラメータ説明
第1引数fileファイルパス(文字列)
第2引数mode='r'モード設定
'r' :読み込み
'w':書き込み(新規上書き)
'a' :書き込み(追記)
※ファイルオブジェクト読み込み時のmode設定と同じ
第3引数compression=ZIP_STORED圧縮アルゴリズム
・ZIP_STORED: 圧縮無し
・ZIP_DEFLATED: 通常の ZIP 圧縮
・ZIP_BZIP2: BZIP2 圧縮
・ZIP_LZMA: LZMA 圧縮
が設定可能(※1))

(※1)これらのアルゴリズムを実行できるモジュール(zlib、bz2、Lzma)が組み込まれていない場合はRuntimeErrorとなります。また、pythonが認識できないアルゴリズムを設定した場合はNotImplementedErrorが返ります。

以下に具体的な例を示します。

# zipfileモジュールの読み込み
import zipfile
import os

# ZipFileオブジェクトを生成
>>> zf = zipfile.ZipFile('./testdir2.zip', mode='r', compression=zipfile.ZIP_STORED)

# ZIPファイルを操作する処理
*** 展開、新規作成、ファイル追加等(詳細は後述) ***

#最後にZipFileオブジェクトをクローズ
>>> zf.close()

尚、最後はclose()メソッドでZipFileオブジェクトを閉じる必要があります。
これは、withステートメントを使ってもっと簡単に書くことも出来ます。

>>> with zipfile.ZipFile('./testdir2.zip', mode='r', compression=zipfile.ZIP_STORED) as zf:
...     filelist = zf.namelist()
...     print(filelist)
... 
['testdir2/', 'testdir2/sample2.txt', 'testdir2/sample1.txt', 'testdir2/sample0/', 'testdir2/sample0/sample01.txt']

ZIPファイルの展開

ZIPファイルを展開する方法について、以下のファイル構成を例にして示します。
このZIPファイルには、次の3つのファイルがアーカイブされています。
(file01.txt, file02.txt, file03.txt)

$ tree
.
└── zipfile.zip

0 directories, 1 file

まず、ZipFIleオブジェクトを読み込みモード(mode=’r’)で開きます。

>>> import zipfile
>>> zf = zipfile.ZipFile('zipfile.zip', mode='r', compression=zipfile.ZIP_STORED)

次に、extractall()メソッドを使って、ZIPファイル中の全てのファイル/フォルダを引数で指定したフォルダへ展開します。
以下は、”extarcted”フォルダへ展開した例です。

>>> zf.extractall('./extracted')
>>> zf.close()

結果を確認します。設定通りにファイルが展開されています。

$ tree
.
├── extracted
│   ├── file01.txt ★ここにファイルが展開されています
│   ├── file02.txt ★ここにファイルが展開されています
│   └── file03.txt ★ここにファイルが展開されています
└── zipfile.zip

1 directory, 4 files

また、圧縮ファイルに含まれるファイルを部分的に展開するには、extractall()メソッドのキーワード引数membersに展開したいファイルをリストとして設定します。

>>> zf = zipfile.ZipFile('zipfile.zip', mode='r', compression=zipfile.ZIP_STORED)

# 部分的に展開
>>> zf.extractall('./extracted02', members=['file01.txt', 'file03.txt'])
>>> zf.close()

結果を確認します。設定した一部のファイルのみ展開されています。
(以下★部分)

$ tree
.
├── extracted
│   ├── file01.txt
│   ├── file02.txt
│   └── file03.txt
├── extracted02
│   ├── file01.txt ★ここにファイルが展開されています
│   └── file03.txt ★ここにファイルが展開されています
└── zipfile.zip

2 directories, 6 files

ZIPファイルの新規生成

ZIPファイルを新規に作成する方法について、以下のファイル構成を例にして示します。

$ tree
.
├── file01.txt
└── file02.txt

0 directories, 2 files

まず、ZipFileオブジェクトを書き込みモード(mode=’w’)で生成します。
第一引数(path)には作りたいZIPファイル名を設定します。

>>> import zipfile
>>> zf = zipfile.ZipFile('zipfile.zip', mode='w')

次に、write()メソッドを使って圧縮したいファイルと圧縮アルゴリズムを指定します。
尚、圧縮アルゴリズムはZipFile()メソッドにも設定できますが、write()メソッドの方が優先されるようです。

>>> zf.write('file01.txt', compress_type=zipfile.ZIP_DEFLATED)
>>> zf.close()

結果を確認します。指定したファイル名でZIPファイルが生成されました。

$ tree
.
├── file01.txt
├── file02.txt
└── zipfile.zip ★ここにZIPファイルが生成

0 directories, 3 files

ZIPアーカイブファイルへファイルを追加

既存のZIPアーカイブファイルにファイルを追加する場合はZipFIleオブジェクトを追記モード(mode=’a’)で開きます。

>>> import zipfile
>>> zf = zipfile.ZipFile('zipfile.zip', mode='a')

上記同様、write()メソッドに、書き込むファイル名と圧縮アルゴリズムを指定します。

>>> zf.write('file02.txt', compress_type=zipfile.ZIP_DEFLATED)
>>> zf.close()

namelist()メソッドを使って追加されているかどうかを確認できます。

>>> zf = zipfile.ZipFile('zipfile.zip', mode='r')
>>> zf.namelist()
['file01.txt', 'file02.txt']
>>> zf.close()

設定通りにファイルが追加されています。

フォルダの圧縮

フォルダの中身をアーカイブして圧縮するには、zipfileオブジェクトは対応したメソッドがなさそうなので、少し工夫が必要です。以下のフォルダ構成において、testdir2フォルダを丸ごと圧縮することを考えてみます。

$ tree
.
└── testdir2
    ├── sample0
    │   └── sample01.txt
    ├── sample1.txt
    └── sample2.txt

2 directories, 3 files

大まかな手順は以下です。

  1. 書き込みモードでZipFileオブジェクトを開く
  2. os.walk()メソッドで圧縮したいフォルダ内のフォルダ、ファイル一覧を取得
  3. 取得したフォルダ、ファイルを順次ZipFileオブジェクトに書き込む

コード例を以下に示します。

>>> import os, zipfile

# dir:圧縮するフォルダ、filename:ZIPファイル名
>>> def make_zip(dir, filename):
        # 絶対パスの取得
...     dir = os.path.abspath(dir)

        # ZIPファイル名に'.zip'を付与
...     zip_filename = filename + '.zip'

        # 書き込みモードでzipfileオブジェクトをオープン
...     with zipfile.ZipFile(zip_filename, 'w') as zf:

            # os.walkメソッドでフォルダ、ファイル一覧を取得
...         for d, s, f in os.walk(dir):
                # フォルダをZIPファイルに追加
...             zf.write(d)

        # ファイルをZIPファイルに追加
...             for s in f:
...                 zf.write(os.path.join(d, s))
... 

>>> make_zip('/home/rensyu/testdir2', 'testdir_compressed')
>>> 

結果を確認します。設定した通りZIPファイルが生成されています。

/home/rensyu$ tree
.
├── testdir2
│   ├── sample0
│   │   └── sample01.txt
│   ├── sample1.txt
│   └── sample2.txt
└── testdir_compressed.zip ★ここにZIPファイルが生成

2 directories, 4 files

ZIPファイルの中身を確認すると、設定通りにフォルダが丸ごと圧縮されていることがわかります。

#圧縮ファイルtestdir_compressed.zipの中身の確認
>>> with zipfile.ZipFile('./testdir_compressed.zip', mode='r') as zf:
...     zf.namelist()
...     
... 
['home/rensyu/testdir2/', 'home/rensyu/testdir2/sample2.txt', 'home/rensyu/testdir2/sample1.txt', 'home/rensyu/testdir2/sample0/', 'home/rensyu/testdir2/sample0/sample01.txt']

まとめ

zipfileモジュールを使ってファイルやフォルダをZIP形式でアーカイブ・データ圧縮する方法についてまとめました。

Learn more...

書籍でもう少し詳しく学びたい場合はこちらもどうぞ。筆者もかなり参考にさせてもらっています!

シェアする
ひびきをフォローする
Hbk project