【Python】zipfile/shutilモジュールでファイル圧縮と解凍をする方法を徹底解説

スポンサーリンク
Python標準ライブラリ_zip_shutil_ファイルのアーカイブ Python

本サイトでは、Pythonで使える便利なライブラリを紹介しています。

さて、日頃の仕事業務において、「大量のデータファイルをまとめて整理したい」「容量の大きいサイズのデータを転送したい」といったことは頻繁にあるのではないでしょうか。

  • Windowsの標準機能「送る→圧縮(zip形式)フォルダ」
  • 専用のソフトを使う
  • WnodwsのコマンドやLinuxのShellなどを使う・・・

などのように、いくつかパターンがありますが、Pytnonのライブラリを活用するという手段もあります。


そこで今回は、ファイルやディレクトリを「圧縮」したり「解凍」するPythonライブラリを取り上げます。同機能を提供するライブラリとして「zipfileモジュール」「shutilモジュール」が用意されています。ともに、Pytnonの標準モジュールで別途インストールする必要なくインポートするだけで使うことができます。※

圧縮形式にはいくつか種類がありますが、本記事ではもっとも一般的な「Zip形式」による方法を紹介します。

(※ なお、両モジュールにはその他、数多くのメソッドやプロパティが用意されています。これだけは知っておきたいという機能を厳選して紹介しています。)


この記事を読むことで次のことが「できるわかる」ようになります。ぜひ、最後までお付き合いください。

この記事で学べること
  • ファイルやフォルダをZip形式にアーカイブ(まとめ)して、圧縮する方法
  • Zipファイルを解凍(中身全てまたは、個別ファイルを指定して)する方法
  • zipfileモジュールとshutilモジュールの使い分けについて

それでは、次項より具体的な活用方法を解説していきます。

スポンサーリンク

1. zipfileモジュール

はじめに、「zipfileモジュール」の概要について解説します。

このモジュールは、ZIPファイルを新規作成したり、ファイルを追加、又は取得(解凍)する機能を提供します。Pythonの標準ライブラリで、別途インストール不要でインポートして直ぐに使用することができます。

zipfile 公式ドキュメント:

https://docs.python.org/3/library/zipfile.html?highlight=zipfile#module-zipfile
zipfileモジュールの公式ドキュメント_rev0.1
図1. zipfileモジュール

<zipfileモジュールの構成>

モジュール構成は、おおきく次の4つの「クラス」からなります。<表1>

その内もっとも、重要なのが「ZipFileクラス」で、Zipファイルの作成とアーカイブの操作全般に関わるメソッドやプロパティを提しています。

zipfileモジュール.クラス名機能の概要
ZipFile クラスZipファイルの生成や読み書きをするための機能を提供するクラス
ZipFileオブジェクトを生成し、アーカイブ(圧縮・解凍)の基幹となるメソッドやプロパティを提供する
PyZipFile クラスZipFileクラスに加えて、Pythonライブラリを含めてアーカイブできる
ZipInfo クラスZIPファイルの属性情報を取得するための機能を提供するクラス
Path クラス標準ライブラリPathのサブクラス
ZIPファイルのパス情報の取得・設定ができる
<表1>zipfileモジュールを構成する主なクラス

<ファイルの圧縮・解凍の概要>

ファイルの圧縮・解凍の手順は、次のとおりです。(図2)

まずは、「ZipFileオブジェクト」を取得します()。ZIPファイルの新規作成もしくは、既存のものに対する解凍圧縮の操作(, )は全て「ZipFileオブジェクト」を経由して行われます。

zipfileモジュール_使い方概要説明_rev0.1
図2. ファイルの圧縮・解凍の手順(概要)

それでは、zipfileモジュールをつかったファイルの圧縮・解凍について具体的なオブジェクトやクラス、メソッドの使い方を説明していきます。

なお、ここで紹介しているものは、本ライブラリを使う上で最低限必要なものを厳選しています。 引数のオプションなど詳細については公式ドキュメントを参照して下さい。

1.1 「ZipFileオブジェクト」を取得する

先述したように、すべてのアーカイブ操作(解凍・圧縮)においては「ZipFileオブジェクト」を介して行われます。そのため、まずはZipFileオブジェクトを取得することからはじめます。

同オブジェクトは、ZipFileクラス を次の書式指定でコンストラクタ(初期化)して生成します。

ZipFileオブジェクト

zipfile.ZipFile(file, mode, cumpression)クラス


引数1: file :Zip圧縮するファイル名の指定

引数2: mode :ZipFileの読み書きモードを指定

引数3: compression :圧縮タイプを指定

戻り値: ZipFileオブジェクト

※同クラスにはこの他、省略可能な引数があります

第2引数:mode には、ZipFileオブジェクトのモード指定するための文字定数 (図3)を設定します。

圧縮や解凍するには、後述する配下のメソッド(write/extract)などで操作することになるのですが、そのためにはあらかじめ目的に応じたモードでオブジェクトを取得しておく必要があるのです。

書き込み(圧縮)する場合は2通りあって、新規ZipFileオブジェクトに対しては「‘w’」とし、既存のオブジェクトにファイルを追加していく用途には「‘a’」とします。逆に、読み取り (解凍)する場合には「”r”」(デフォルト値)とします。

zipfile.ZipFile_modeの指定オプション
図3. ZipFileクラスの引数:mode

一方、第3引数:compression には、圧縮タイプを指定します(図4)。通常のZIP形式の場合には「ZIP_DEFLATED」とし、圧縮は行わずにまとめる(アーカイブ)するだけであれば「ZIP_STORED」と指定します。

zipfile.ZipFile_compressionの指定オプション
図4. 引数compressionのオプション指定一覧

それ以外の圧縮タイプとしてBZIP2/ LZMA形式も選択できますが、今回は通常のZIP形式とした場合の使用例を以降で解説していきます。

また、第1引数:file には圧縮ファイル名を指定します。同名の圧縮ファイルが存在していなけば新規オブジェクトが作成されますが、既存のファイル名を指定すれば再度そのオブジェクトを操作の対象とすることができます。

1.2 「ZipFileオブジェクト」を圧縮する

1.1項で生成したZipFileオブジェクトに対して、ファイルを追加することでアーカイブや圧縮を行います。追加は同オブジェクト配下の「write()メソッド」を次の書式のように使います。

ただし、ZipFileオブジェクトは、書き込み(もしくは追記)モードで作成されている前提とします。

ZipFileオブジェクト(writeメソッド)

ZipFileオブジェクト.write(filename, arcname)メソッド


引数1: filename :追加したいファイル名(カレントディレクトリでない場合はパスも含めて指定する)

引数2: arcname :アーカイブ先でのファイル名とその相対パスを指定する

※同メソッドにはこのほかにも省略可能な引数があります

ここで具体的な使用例を紹介します。

カレントディレクトリに、「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)がアーカイブされています。

zipfile_writeメソッドのイメージ図
図5. <List1>の実行イメージ

次に作成した、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()メソッド が提供され、次の書式のようにして使います。

ZipFileオブジェクト extractall()

ZipFileオブジェクト.extractall(path, member, pwd)メソッド


引数1:path: 解凍先のパス(省略した場合は、カレントディレクトリに解凍されます)

引数2:member: アーカイブ内のコンテンツ一覧を指定します(namelist()メソッドの戻り値を指定)

引数3:pwd: パスワードを指定します

※すべての引数はオプショナルで省略可能です

解凍先のディレクトリ指定は 引数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)

zipfile_extractallのイメージ図
図6. <List3>の実行結果イメージ(extractallメソッド)

(2) 特定のコンテンツを指定して解凍する場合

「Zipファイルの特定のコンテンツを指定して解凍する場合」については extractall()メソッド を次の書式のようにして使います。

引数2: path: 解凍先のパス(省略した場合は、カレントディレクトリに解凍されます)

ZipFileオブジェクト extract()

ZipFileオブジェクト.extract(member, path, pwd)メソッド


引数1: member: 解凍したいファイル名もしくはZipInfoオブジェクト(後述)を指定する

引数2: path: 解凍先のパス(省略した場合は、カレントディレクトリに解凍されます) 省略可能(オプショナル)

引数3: pwd : パスワードを指定します 省略可能(オプショナル)

引数: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()メソッド で取得できます。

ZipInfoオブジェクトの取得

ZipFileオブジェクト.infoget(name)メソッド

引数:name :アーカイブメンバ名を指定する(ファイル名)

戻り値: ZipInfoオブジェクト


ZipFileオブジェクト.infolist()メソッド

戻り値: ZipInfoオブジェクトを要素とするリストを返す

2つめの infolist()メソッドは、アーカイブ内のすべてのコンテンツの情報をリスト形式で一度に取得します。

[<項目名1=値1 項目名2=値2 ・・・ >, <>, <>,・・・]

1組の<>がZipInfoオブジェクトの内容で、各項目間はスペースで区切られま

す。特定のオブジェクトを取得する場合は、通常の「リスト」のように[index]のようにインデックスで指定します。

ZipInfoオブジェクトには、主として次のようなプロパティや属性があります。

オブジェクトプロパティ/属性機能
ZipInfoオブジェクトfilenameファイル名の取得と設定
ZipInfoオブジェクトflag_bitsZipファイルのフラッグビット情報の取得
ZipInfoオブジェクトcompress_typeZipファイルの圧縮形式の取得
ZipInfoオブジェクトcompress_sizeZipファイルのサイズの取得
表2. ZipInfoオブジェクトの主な属性

具体的に、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メソッド圧縮したファイル名を取得する(複数ある場合はリスト形式で戻る)
表4 ZipFileオブジェクトの主なメソッド・プロパティ
スポンサーリンク

2. shutilモジュールによるアーカイブ

Python_基本文法_内包表記

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 フォルダの中身をまとめて圧縮する

shutilモジュール make_archive関数(1)

shutil.make_archive(base_name, format, root_dir)


引数1:base_name:作成するアーカイブファイルの名前を指定する。※注意点としては、拡張子は指定しないこと。

引数2:format:アーカイブ方式を指定する。ZIP圧縮であれば「zip」、tar..gz圧縮であれば「gztar」を指定するその他、bztar/xztarがある

引数3:root_dir:アーカイブ対象となるディレクトリを指定する。対象のディレクトリ以下に含まれるフォルダーやファイルがまとめて圧縮される

次の例はフォルダの中身をまとめて圧縮するコード例です。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そのものではなくその中身が圧縮対象になります。

shutil_make_archive_1
図4 target_folder内の複数ファイルを圧縮する例

2.1.2 フォルダごと圧縮する

shutilモジュール make_archive関数(2)

shutil.make_archive(base_name, format, root_dir, base_dir)


引数1:base_name:作成するアーカイブファイルの名前を指定する。※注意点としては、拡張子は指定しないこと。

引数2:format:アーカイブ方式を指定する。ZIP圧縮であれば「zip」、tar..gz圧縮であれば「gztar」その他、bztar/xztarがある

引数3:root_dir:アーカイブ対象となるディレクトリを指定する。対象のディレクトリ以下に含まれるフォルダーやファイルがまとめて圧縮される。

引数4:base_dir:アーカイブを開始するディレクトリを指定する。

では、フォルダーごと(図の例では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そのものが圧縮対象になります。

shutil_make_archive_2
図5 target_folderを圧縮する例

2.1.3 圧縮フォルダを解凍する

shutilモジュール unpack_archive関数

shutil.unpack_archive(filename, extract_dir)


引数1:filename:解凍したいアーカイブファイル(必要に応じてパスも付けて指定する)

引数2:extract_dir:解凍先ディレクトリ

Unpack_archiveの使用例になります。引数:filenameに解凍対象のファイル名(zip_arc.zip)を、

引数:extract_dirに解凍先のディレクトリ(unzip)を指定しています。指定した解凍先が存在しなければ自動で作成されます。

import shutil

shutil.unpack_archive('zip_arc.zip', 'unzip')

unpack_archive関数を使った解凍はシンプルですが、元になる圧縮ファイルの中身によって展開内容が異なります。図6はフォルダの中身を圧縮したアーカイブファイルを解凍した例です。

shutil_unpack_archive_1
図6 zip_arc.ziipをunzipに解凍する例1

図7はフォルダごと圧縮したアーカイブファイルを解凍した例です。

shutil_unpack_archive_2
図7 zip_arc.ziipをunzipに解凍する例2
スポンサーリンク

3. zipfile/shutilモジュールの使い分けについて

ここで、zipfileとshutilモジュールの得意・苦手を比較して使い分けましょう。簡単にまとめると次のようになります。


 【zipfileモジュール】

得意:ファイル毎の圧縮・解凍ができまた、細かい設定や文字化け対策ができる

苦手:フォルダごと一気に圧縮することができない。(ファイルを一つずつ圧縮する必要があるためコード量が増える)

 【shutilモジュール】

得意:フォルダごと一気にアーカイブできる。(Windowsのエクスプローラでの圧縮方法に近いイメージ)

苦手:zipfileモジュールのようなファイル毎に詳細設定ができない。

Windowsエクスプローラの用に効率的にアーカイブする場合はshutil、個別ファイル毎の処理や文字化け対策が必要になるのであればzipfileといった使い分けができるのではないでしょうか。

4. まとめ

いかがでしたでしょうか?

今回はPythonの標準ライブラリであるzipfile/shutilモジュールを使って、アーカイブファイルの作成をする方法についてまとめました。

➀ zipfileモジュールでファイルを圧縮・解凍をする方法

② shutilモジュールでファイル・フォルダを圧縮・解凍をする方法

➂ 両モジュールの違いと使い分けについて

Pythonを使って大量のファイルを管理整理行うなどの処理を自動化したいときには役立つ機能を提供してくれるモジュールだと思います。是非活用してみてください。

最後までお読み頂きありがとうございました。

タイトルとURLをコピーしました