本サイトでは、Pythonで使える便利なライブラリを紹介しています。
さて、日頃の仕事業務において、「大量のデータファイルをまとめて整理したい」「容量の大きいサイズのデータを転送したい」といったことは頻繁にあるのではないでしょうか。
などのように、いくつかパターンがありますが、Pytnonのライブラリを活用するという手段もあります。
そこで今回は、ファイルやディレクトリを「圧縮」したり「解凍」するPythonライブラリを取り上げます。同機能を提供するライブラリとして「zipfileモジュール」「shutilモジュール」が用意されています。ともに、Pytnonの標準モジュールで別途インストールする必要なくインポートするだけで使うことができます。※
圧縮形式にはいくつか種類がありますが、本記事ではもっとも一般的な「Zip形式」による方法を紹介します。
(※ なお、両モジュールにはその他、数多くのメソッドやプロパティが用意されています。これだけは知っておきたいという機能を厳選して紹介しています。)
この記事を読むことで次のことが「できる・わかる」ようになります。ぜひ、最後までお付き合いください。
それでは、次項より具体的な活用方法を解説していきます。
1. zipfileモジュール
はじめに、「zipfileモジュール」の概要について解説します。
このモジュールは、ZIPファイルを新規作成したり、ファイルを追加、又は取得(解凍)する機能を提供します。Pythonの標準ライブラリで、別途インストール不要でインポートして直ぐに使用することができます。
zipfile 公式ドキュメント:
https://docs.python.org/3/library/zipfile.html?highlight=zipfile#module-zipfile
<zipfileモジュールの構成>
モジュール構成は、おおきく次の4つの「クラス」からなります。<表1>
その内もっとも、重要なのが「ZipFileクラス」で、Zipファイルの作成とアーカイブの操作全般に関わるメソッドやプロパティを提供しています。
zipfileモジュール.クラス名 | 機能の概要 |
---|---|
ZipFile クラス | Zipファイルの生成や読み書きをするための機能を提供するクラス ZipFileオブジェクトを生成し、アーカイブ(圧縮・解凍)の基幹となるメソッドやプロパティを提供する |
PyZipFile クラス | ZipFileクラスに加えて、Pythonライブラリを含めてアーカイブできる |
ZipInfo クラス | ZIPファイルの属性情報を取得するための機能を提供するクラス |
Path クラス | 標準ライブラリPathのサブクラス ZIPファイルのパス情報の取得・設定ができる |
<ファイルの圧縮・解凍の概要>
ファイルの圧縮・解凍の手順は、次のとおりです。(図2)
まずは、「ZipFileオブジェクト」を取得します(➀)。ZIPファイルの新規作成もしくは、既存のものに対する解凍圧縮の操作(➁, ➂)は全て「ZipFileオブジェクト」を経由して行われます。
それでは、zipfileモジュールをつかったファイルの圧縮・解凍について具体的なオブジェクトやクラス、メソッドの使い方を説明していきます。
1.1 「ZipFileオブジェクト」を取得する
先述したように、すべてのアーカイブ操作(解凍・圧縮)においては「ZipFileオブジェクト」を介して行われます。そのため、まずはZipFileオブジェクトを取得することからはじめます。
同オブジェクトは、ZipFileクラス を次の書式指定でコンストラクタ(初期化)して生成します。
第2引数:mode には、ZipFileオブジェクトのモード指定するための文字定数 (図3)を設定します。
圧縮や解凍するには、後述する配下のメソッド(write/extract)などで操作することになるのですが、そのためにはあらかじめ目的に応じたモードでオブジェクトを取得しておく必要があるのです。
書き込み(圧縮)する場合は2通りあって、新規ZipFileオブジェクトに対しては「‘w’」とし、既存のオブジェクトにファイルを追加していく用途には「‘a’」とします。逆に、読み取り (解凍)する場合には「”r”」(デフォルト値)とします。
一方、第3引数:compression には、圧縮タイプを指定します(図4)。通常のZIP形式の場合には「ZIP_DEFLATED」とし、圧縮は行わずにまとめる(アーカイブ)するだけであれば「ZIP_STORED」と指定します。
それ以外の圧縮タイプとしてBZIP2/ LZMA形式も選択できますが、今回は通常のZIP形式とした場合の使用例を以降で解説していきます。
また、第1引数:file には圧縮ファイル名を指定します。同名の圧縮ファイルが存在していなけば新規オブジェクトが作成されますが、既存のファイル名を指定すれば再度そのオブジェクトを操作の対象とすることができます。
1.2 「ZipFileオブジェクト」を圧縮する
1.1項で生成したZipFileオブジェクトに対して、ファイルを追加することでアーカイブや圧縮を行います。追加は同オブジェクト配下の「write()メソッド」を次の書式のように使います。
ただし、ZipFileオブジェクトは、書き込み(もしくは追記)モードで作成されている前提とします。
ここで具体的な使用例を紹介します。
カレントディレクトリに、「zip_arc.zip」というZipファイルを作り、画像ファイル(testimage.jpg)を圧縮してみます。<List1>
import zipfile
FILE = testImage1.jpg
#------------------------------------------------------------------------------
# 新規のZipFileオブジェクトを書込みモードで開く
with zipfile.ZipFIle('zip_arc.zip', 'w', zipfile.ZIP_DEFLATED) as zipf:
zipf.write(FILE, testImage1.jpg)
このコードでは、「with…asステートメント」とともにZipFileオブジェクトを生成しています。こうすることで、ZipFileオブジェクトの後処理を省略することができ「Closeメソッド」なしにオブジェクトを閉じてくれます。(通常のFileオブジェクトと同じ要領です。)
実際の圧縮は、7行目の write()メソッド で行っています。第1,2引数ともに同じファイル名で書き込んでいます。
<List1>の実行イメージは次の図5のとおりです。カレントディレクトリにzip_arc.zipファイルが作成され、画像(jpg)がアーカイブされています。
次に作成した、zip_arc.zipファイルにさらに画像ファイルを追加する例を示します。<List2>ここでは、同じディレクトリに2枚のjpgファイルを用意しました。
import zipfile
#------------------------------------------------------------------------------
# 既存のZipFileオブジェクトを追記モードで開く
with zipfile.ZipFIle('zip_arc.zip', 'a', zipfile.ZIP_DEFLATED) as zipf:
zipf.write(testImage2, testImage2.jpg) # 既存のZipFileオブジェクトに追加する
zipf.write(testImage3, testImage3.jpg) # 既存のZipFileオブジェクトに追加する
<List1>とほとんど同じですが、5行目の「ZipFileクラス」の 第2引数:mode は今度は追記モードを示す‘a’を指定してオブジェクトを開いています。
そして、続く6,7行目のように「write()メソッド」で1つずつ画像を追記していきます。このように一度に複数のデータを追加するのではなく、データごとにメソッドを実行していきます。
次項では、Zipファイルを解凍する方法について解説します。
1.3 「ZipFileオブジェクト」を解凍する
さて、圧縮のあとはZipファイルの解凍する手順となります。
解凍方法は、2つあって一つは「一度にすべてのコンテンツを解凍する場合」、そして「特定のコンテンツだけを解凍する場合」です。
(1) 一度に全てのコンテンツを解凍する場合
まずは一般的な「一度にZipファイルすべてのコンテンツを解凍する場合」についてです。 ZipFileオブジェクトには、extractall()メソッド が提供され、次の書式のようにして使います。
解凍先のディレクトリ指定は 引数1:path で行います。省略した場合は、カレントディレクトリに作成されます。
「extractall()メソッド」の使用例として、さきほどの<List2 >で作成した、3つの画像ファイルを含む「zip_arc.zipファイル」解凍してみます。<List3>
import zipfile
#------------------------------------------------------------------------------
with zipfile.ZipFIle('zip_arc.zip', 'r') as zipf: # 引数compressionは省略してOK
zipf.extractall('unzip') # 解凍先フォルダを指定
<List3>では、4行目でZipFileオブジェクトを読込みモード(‘r’)で開いている点がポイントです。そして、5行目の「extractall()」の 引数:path にフォルダ名(unzip)だけを指定して解凍を実行します。
実行後、イメージは次のとおりです。unzipフォルダがつくられ、<List2>で固めた3つのファイルが展開されていることが確認できます。(図6)
(2) 特定のコンテンツを指定して解凍する場合
「Zipファイルの特定のコンテンツを指定して解凍する場合」については extractall()メソッド を次の書式のようにして使います。
引数2: path: 解凍先のパス(省略した場合は、カレントディレクトリに解凍されます)
引数:member にて、解凍したい特定のコンテンツ情報を渡します。コンテンツ情報は、ファイル名もしくは、「ZipInfoオブジェクト」(後述)を指定します。その他は、「extractall()メソッド」と同様の取り扱いとなります。
「extract ()メソッド」の使用例として、3つの画像ファイルを含む「zip_arc.zipファイル」から、特定のファイルを指定して解凍してみます。<List4>
import zipfile
with zipfile.ZipFile('test/photo_arc.zip') as zipf:
zinfos = zipf.infolist() # ZipInfoオブジェクトを取得(詳細は後述)
zipf.extract(zinfos[2], 'unzip') # ZipInfoオブジェクトを個別に指定し解凍する
<List4>では、3行目のZipFileクラスの 第2引数:mode を省略していますが、オプショナル引数で省略すると‘r’(読み取り)モードでオブジェクトを開くことになります。
4行目の infolist()メソッド(後述)でアーカイブ情報を取得して、extract ()メソッドに渡して2番目のコンテンツを指定して解凍しています。
1.4 アーカイブ情報「ZipInfoオブジェクト」の取得
アーカイブ(圧縮)ファイル(ZipFileオブジェクト)が保有する各コンテンツの詳細情報(ファイル名、圧縮形式、サイズなど)は、配下の「ZipInfoオブジェクト」で管理されており各種メソッドやプロパティなどで属性の取得や設定が可能となります。また、先ほど解凍で解説した「extract()メソッド」の 引数:member に設定して、特定のコンテンツだけを取り出します。
ZipInfoオブジェクトは、次の infoget()メソッド, infolist()メソッド で取得できます。
2つめの infolist()メソッドは、アーカイブ内のすべてのコンテンツの情報をリスト形式で一度に取得します。
[<項目名1=値1 項目名2=値2 ・・・ >, <>, <>,・・・]
1組の<>がZipInfoオブジェクトの内容で、各項目間はスペースで区切られま
す。特定のオブジェクトを取得する場合は、通常の「リスト」のように[index]のようにインデックスで指定します。
ZipInfoオブジェクトには、主として次のようなプロパティや属性があります。
オブジェクト | プロパティ/属性 | 機能 |
---|---|---|
ZipInfoオブジェクト | filename | ファイル名の取得と設定 |
ZipInfoオブジェクト | flag_bits | Zipファイルのフラッグビット情報の取得 |
ZipInfoオブジェクト | compress_type | Zipファイルの圧縮形式の取得 |
ZipInfoオブジェクト | compress_size | Zipファイルのサイズの取得 |
具体的に、ZipInfoオブジェクトの中身をサンプルコードで確認してみます。Infolist()メソッドによる、オブジェクトの取得(リスト形式)とプロパティ(属性)により、ファイル名・圧縮形式、ファイルサイズを取得表示しています。<List5>
import zipfile
with zipfile.ZipFile('zip_arc.zip') as zipf:
print(zipf.infolist()) # ZipInfoオブジェクトを表示
# >>[<ZipInfo filename='img1.jpg' compress_type=deflate filemode='-rw-rw-rw-' file_size=7133compress_size=7106>, <ZipInfo ....<2つめのZipInfoオブジェクト><3つめのZipInfoオブジェクト> ]
zinfos = zf.infolist() # ZipInfoオブジェクトを取得
print(zinfos[0]) # 一つ目のZipファイルのZipInfoを選択して表示
# >><ZipInfo filename='img1.jpg' compress_type=deflate filemode='-rw-rw-rw-' file_size=7133 compress_size=7106>
#-------------------------------------------------------------------------------------------------
print(zinfos[0].filename) # ファイル名を取得 >> img1.jpg
print(zinfos[0].compress_type) # 圧縮形式を取得 >> 8
print(zinfos[0].compress_size) # 圧縮サイズを取得 >> 7106
次にZipInfoオブジェクトを使った応用事例を紹介します。<List6>
日本語(全角文字)を含んだファイルをエクスプローラなどで圧縮した場合、ファイル名が文字化けしてしまうことがあります。これは、文字コードに「cp437」という古いものを使って圧縮しているために起こる仕様の違いによる現象です。(通常、Windowsの文字コードは「cp932」が使われています。)
ZipInfoオブジェクトのflag_bits属性を確認することで、使われている文字コードを調べることができます。そして、文字コードが「cp437」であった場合には、「cp932」へ変換することで文字化けを防ぐことができます。
import zipfile
with zipfile.ZipFile('zip_arc.zip') as zipf:
for zinfo in zipf.infolist(): # ZipInfoオブジェクトを取得
if not zinfo.flag_bits & 0x800: # flag_bitsプロパティで文字コードを取得
# 文字コードが(cp437)だった場合はcp932へ変換する
# strオブジェクトのプロパティencode/decodeでcp932に変換
# 変換後のファイル名をfilenameプロパティで再度し直す
zinfo.filename = zinfo.filename.encode('cp437').decode('cp932')
zf.extract(zinfo, 'unzip')
それでは、コードの概要を解説します。
4行目のinfolist()メソッドで、アーカイブファイルのすべてのZipInfoオブジェクトを「For句」で順番に取得し以降の処理をコンテンツ数だけ繰り返します。
重要なのは、5行目で、オブジェクトのflag_bits属性で文字コードの識別子を取得・判定しています。「cp932」であれば、この識別子は12ビット目だけが1ととなります。“0x800”と論理積をとることで該当ビットの判定を行い、もし「cp932」以外の文字コードであるならば、「cp932」へ変換し直す処理を施します。
具体的には、9行目でfilenameプロパティで取得したファイル名(String型)に、Strオブジェクトのencode/decodeプロパティで「cp437」→「cp932」と変換処理を施します。一旦、cp437にエンコードしてから、cp932に変換し直す点がポイントです。
最後に11行目で、extract()メソッドの 引数:member にZipInfoオブジェクトを指定して個別ファイルごとに解凍を行います。
1.5 「ZipFileオブジェクト」その他の属性
ZipFileオブジェクトのその他の属性(メソッド)は以下のようなものがあります。他にも数多ありますので必要に応じて公式Documentを参照して下さい。
オブジェクト | メソッド・プロパティ | 機能 |
---|---|---|
ZipFileオブジェクト | closeメソッド | with構文を使用しないでZipFileオブジェクトを取得した場合は 必ず閉じる必要がある |
ZipFileオブジェクト | openメソッド | 既存のZipFileオブジェクト(Zipファイル)を開く |
ZipFileオブジェクト | namelistメソッド | 圧縮したファイル名を取得する(複数ある場合はリスト形式で戻る) |
2. shutilモジュールによるアーカイブ
shutilモジュールの使い方について解説します。
shutilはアーカイブに特化したモジュールというわけではなく、他にも高次なファイル操作を提供するクラスやメソッドが多数あります。
Python公式ドキュメント(shutil)は次の通りですので適宜参照ください。
Python公式ドキュメント(shutilモジュール)
https://docs.python.org/3/library/shutil.html?highlight=shutil#module-shutil
Pythonの標準ライブラリですのでインストールは必要なくインポートするだけで使用できます。
shutilモジュールは次のようにしてインポートします。
import shutil
特にエラーメッセージがでなければインポート成功です。
まず、shutilモジュールの使い方ですが、shutilのよる圧縮・解凍は先のzipfileモジュールによる手順とは異なり中間オブジェクト(ZipFileオブジェクト)の生成は不要で、関数の引数にアーカイブファイル名、対象フォルダを指定するだけで済みます。
それでは、shutilモジュールを用いたファイルの圧縮・解凍について具体的なオブジェクトやクラス、メソッドの使い方を説明していきます。
2.1 shutilモジュール
2.1.1 フォルダの中身をまとめて圧縮する
次の例はフォルダの中身をまとめて圧縮するコード例です。zipfileモジュールではまとめて一度に処理することはできませんがshutilモジュールのmake_archive関数を使うと一行で対応することができます。
引数:root_dirは対象のファイルが保存されているディレクトリを指定します。
図4の例では、引数:root_dirには、target_folderを指定してその中身のファイルを処理しています。
import shutil
# base_dirフォルダは省略した場合
# 引数root_dirには圧縮対象のファイルやフォルダを含むディレクトリを指定する
shutil.make_archive('zip_arc', 'zip', 'target_folder')
target_folderそのものではなくその中身が圧縮対象になります。
2.1.2 フォルダごと圧縮する
では、フォルダーごと(図の例ではtarget_folder)圧縮することはできないかというと勿論そのようなことはなく、もう一つ引数を追加(base_dir)することで対応できます。
次のコードと図5はその例になります。まず、引数:root_dirには対象のフォルダの親ディレクトリ(一つ上の階層のフォルダ)を指定します、そして追加した引数:base_dirに対象フォルダを指定します。
つまり、root_dirには対象フォルダの親ディレクトリである(Current_dir)、そしてbase_dirには対象フォルダである(target_folder)を指定します。
import shutil
#base_dirフォルダを指定した場合
# 引数root_dirには圧縮対象フォルダの親ディレクトリを指定
# 引数base_dirには圧縮対象フォルダを指定する
shutil.make_archive('zip_arc', 'zip', 'Current_dir'', 'target_folder')
target_folderそのものが圧縮対象になります。
2.1.3 圧縮フォルダを解凍する
Unpack_archiveの使用例になります。引数:filenameに解凍対象のファイル名(zip_arc.zip)を、
引数:extract_dirに解凍先のディレクトリ(unzip)を指定しています。指定した解凍先が存在しなければ自動で作成されます。
import shutil
shutil.unpack_archive('zip_arc.zip', 'unzip')
unpack_archive関数を使った解凍はシンプルですが、元になる圧縮ファイルの中身によって展開内容が異なります。図6はフォルダの中身を圧縮したアーカイブファイルを解凍した例です。
図7はフォルダごと圧縮したアーカイブファイルを解凍した例です。
3. zipfile/shutilモジュールの使い分けについて
ここで、zipfileとshutilモジュールの得意・苦手を比較して使い分けましょう。簡単にまとめると次のようになります。
【zipfileモジュール】
得意:ファイル毎の圧縮・解凍ができまた、細かい設定や文字化け対策ができる。
苦手:フォルダごと一気に圧縮することができない。(ファイルを一つずつ圧縮する必要があるためコード量が増える)
【shutilモジュール】
得意:フォルダごと一気にアーカイブできる。(Windowsのエクスプローラでの圧縮方法に近いイメージ)
苦手:zipfileモジュールのようなファイル毎に詳細設定ができない。
➡Windowsエクスプローラの用に効率的にアーカイブする場合はshutil、個別ファイル毎の処理や文字化け対策が必要になるのであればzipfileといった使い分けができるのではないでしょうか。
4. まとめ
いかがでしたでしょうか?
今回はPythonの標準ライブラリであるzipfile/shutilモジュールを使って、アーカイブファイルの作成をする方法についてまとめました。
➀ zipfileモジュールでファイルを圧縮・解凍をする方法
② shutilモジュールでファイル・フォルダを圧縮・解凍をする方法
➂ 両モジュールの違いと使い分けについて
Pythonを使って大量のファイルを管理整理行うなどの処理を自動化したいときには役立つ機能を提供してくれるモジュールだと思います。是非活用してみてください。
最後までお読み頂きありがとうございました。