このページではPythonでZIPファイルを扱う方法について解説します。
Contents
zip形式で圧縮する
PythonでZIPファイルを扱うには外部ライブラリのインストールは不要で標準のzipfileモジュールで操作することが可能です。
https://docs.python.org/ja/3/library/zipfile.html#module-zipfile
ファイルを圧縮する
まずは簡単なサンプルから、一つのテキストファイルをzipファイルに圧縮してみましょう。
import zipfile with zipfile.ZipFile('sample.zip', 'w')as zf: zf.write('data/sample1.txt')
上のサンプルではdataディレクトリ配下のsample1.txtというテキストファイルを、sample.zipというzipファイルに圧縮しています。
zipfile.ZipFileクラスのコンストラクタの第1引数で指定したファイルに対して入出力操作が可能です。また、第2引数でファイルオープンモードを指定することが可能です。上のサンプルでは書き込みなので'w'を指定していますが、既存のファイルを読み込む場合は'r'を、追記する場合は'a'を指定します。
コンテキスト内のwriteメソッドで圧縮する対象のファイルを指定することができます。writeメソッドのarcnameを指定するとzipファイル内部でのパスを指定することができます。
import zipfile with zipfile.ZipFile('sample.zip', 'w')as zf: zf.write('data/sample1.txt', arcname='sample1.txt')
上のサンプルでは最初のサンプルと同様、data/sample1.txtを圧縮していますが、直下にファイルが格納されます。
複数のファイルを圧縮する
先程はファイルを1つだけ圧縮しましたが、コンテキスト内で圧縮するファイルを追加することができます。複数ファイルをzipに圧縮する場合は以下のサンプルのように記述します。
import zipfile with zipfile.ZipFile('sample.zip', 'w')as zf: zf.write('data/sample1.txt') zf.write('data/sample2.txt') zf.write('data/sample3.txt')
既存のZIPファイルにファイルを追加する
また、zipfileで開いたファイルは新たにファイルを追加することが可能です。先述の通りファイルオープンモードに追記の'a'を指定することでコンテキスト内でファイルの追加が可能となります。
import zipfile with zipfile.ZipFile('sample.zip', 'a')as zf: zf.write('data/sample4.txt') zf.write('data/sample5.txt')
ディレクトリごと圧縮する
Pythonの標準モジュールosを使用するとディレクトリ配下のパスツリーを構築することができますので、先程のサンプルに流し込めばディレクトリごと圧縮することが可能です。ただし、標準モジュールのshutil(ファイル操作の機能を提供するモジュール)が同様の処理を提供しているため、こちらを使用する方が楽でしょう。
https://docs.python.org/ja/3/library/shutil.html#shutil.make_archive
import shutil shutil.make_archive('dir_sample', 'zip', root_dir='data/sample_dir')
make_archiveメソッドはパラメータとして以下を指定することができます。
shutil.make_archive(base_name, format, [root_dir, [base_dir]])
base_nameに作成するファイル名を指定しますが、拡張子は省略します。formatにはアーカイブフォーマットとして'zip'を指定しますが、zip以外にtarを指定することが可能です。また、任意でroot_dir、base_dirの指定が可能で、圧縮するディレクトリのルートとroot_dirからの相対パスを指定します。省略した場合はカレントディレクトリが指定されます。
zipファイルを解凍する
zipファイルを解凍したり、中身を参照する場合はファイルのオープンモードに'r'を指定します。
解凍する
オープンモードに'r'を指定して開き、extractallメソッドを実行します。
import zipfile with zipfile.ZipFile('sample.zip', 'r')as zf: zf.extractall('./output/')
引数に解凍先のパスを指定します。上のサンプルの場合、./output/ディレクトリ配下に解凍されます。
また、extractメソッドを使用して個別に解凍することも可能です。
import zipfile with zipfile.ZipFile('sample.zip', 'r')as zf: zf.extract('data/sample1.txt', './output/')
上のサンプルの場合、data/sample1.txtのみが./output/配下に解凍されます。また、ファイルの有無を確認したい場合は次に説明するフィアルリストの取得で可能です。
解凍しないで中身を確認する
オープンモードに'r'を指定して開き、namelistメソッドを使用するとファイルリストを参照することができます。
import zipfile with zipfile.ZipFile('sample.zip', 'r')as zf: print(zf.namelist()) # ['data/sample1.txt', 'data/sample2.txt', 'data/sample3.txt']
パスワード付きのzipを解凍する
extractall、extractで引数pwdでパスワードを指定するとパスワード付きzipfileを解凍することが可能です。パスワードは以下サンプルのようにbyteで指定します。
import zipfile with zipfile.ZipFile('sample.zip', 'r')as zf: zf.extractall('./output/', pwd=b'パスワード') with zipfile.ZipFile('sample.zip', 'r')as zf: zf.extract('data/sample1.txt', './output/', pwd=b'パスワード')
一方でPython3.7の時点で圧縮時のパスワードは残念ながらサポートされていません。
https://docs.python.org/3/library/zipfile.html#module-zipfile
代案として以下のサードパーティ製ライブラリを使用する手があります。
https://pypi.org/project/pyminizip/
もう一つ注意点ですが、暗号化zipfileの解凍はCライブラリではなくPythonで記述されているため「非常に遅い」です。大量に処理する場合はパフォーマンスに注意してください。
https://docs.python.org/3/library/zipfile.html#module-zipfile
zipfileの破損とcrc32
一通り使い方について説明しました。最後に業務で使用する場合の注意点について解説します。
システムでファイルを扱っていて問題になるのがファイルの破損です。大量のファイルを扱っているとクライアント側のエラー、サーバーサイドのディスクの損傷、転送時のエラーなど様々な理由でファイルの破損が発生しますので特に受託開発の場合は注意が必要です。
ZIPファイルの破損のチェック法ですが、ファイルヘッダのオフセット14番目以降にファイル内容のCRC-32が格納されいるためこれを利用します。もちろん自力で実装する必要はなく、Pythonのzipfile.ZipFileクラスにはこのCRC-32をチェックするメソッドがあります。
import zipfile import sys # zip作成 with zipfile.ZipFile('sample.zip', 'w')as zf: zf.write('data/sample1.txt') # zipの破壊 with open('sample.zip', 'rb+') as f: f.seek(40) f.write(0x80.to_bytes(1, byteorder=sys.byteorder)) # zipの検査 with zipfile.ZipFile('sample.zip', 'r')as zf: t = zf.testzip() print(t) # data/sample1.txt
上のサンプルでは圧縮ファイルに対してファイルのオフセット40番目を1バイト壊した状態で検査しています。testzipメソッドでは最初に破損を検知したファイル名が出力され、破損が検知されていることが確認できます。