PythonでZIPファイルを展開する(メモ)
Pythonではzipfileというモジュールを用いることによりZIP書庫の読み書きが行える。
ここでは既存のZIP書庫の展開をPythonで行う処理を扱う。
ZIP書庫を扱うオブジェクト
zipfile.ZipFileオブジェクトを用いる。
- 開くときにファイル名とモードを指定
- 閉じるときにメンバ関数close()を使用
- 読み書きが失敗すると例外IOErrorを出す
といった点においてファイルオブジェクトに似ている。
import zipfile import sys (中略) infile = [ZIPファイルの場所] try: f_zip = zipfile.ZipFile(infile, 'r') except IOError: print >> sys.stderr, 'IOError' sys.exit(1) try: # finally try: [読み書き処理...] except IOError: print >> sys.stderr, 'IOError' finally: f_zip.close()
ファイル/ディレクトリ名とタイムスタンプを処理する
zipfile.ZipFileオブジェクトのメンバ関数namelist()はZIPファイル中のファイルやディレクトリの一覧を格納するリスト(各要素は書庫内パスの文字列)を返し、infolist()はその一覧のファイル/ディレクトリの並びと同じ順番で個別の項目に関するタイムスタンプなどの情報を含むzipfile.ZipInfoオブジェクトのリストを返す。
これらをPythonの組み込み関数zip()で同時に処理すると便利。
for (item, info) in zip(f_zip.namelist(), f_zip.infolist()): [itemで個別のファイル/ディレクトリの書庫内パス、infoでその項目の情報を取り出す]
ディレクトリのタイムスタンプを復元するためには、全てのファイルを展開した後でディレクトリのタイムスタンプを変更することになる。あらかじめファイルとディレクトリを分けてから順番通りに処理する必要があるので面倒。
ファイルとディレクトリの展開
各書庫内パス(namelist()で得られるリストの個別の文字列)はファイルの場合とディレクトリの場合とがあるが、ディレクトリの場合は末尾が「/」になっているため、区別の役に立つ。
ディレクトリの場合はos.makedirs()などを用いて作成し、ファイルの場合は通常のファイルと同様open()で(バイナリの書き込みモードで)開いた後でzipfile.ZipFileオブジェクトのメンバ関数read()に書庫内パスを渡したものを書き込むことでZIPファイル内の個別のファイルの中身を取り出す。
ただし、Windows上の日本語ファイル名を含むZIPファイル*1を処理する場合、ZIPファイル内のファイル名エンコーディングはCP932(Windows上のShift_JIS)だが展開先のGNU/Linux上ではUTF-8となるため、作成するファイル/ディレクトリ名についてtry-except文によって各エンコーディングでのデコードを試行していく形となる。
下はディレクトリ作成部分。ファイルの作成部分も同様に処理することになるが、ここでは省略する。
prefix = [展開先ディレクトリ] for (item, info) in [ディレクトリ一覧の(名前, 情報)のタプルのリスト]: try: item_unicode = item.decode('utf-8') # UTF-8 except UnicodeDecodeError: try: item_unicode = item.decode('cp932') # Win上の日本語ディレクトリ名 except UnicodeDecodeError: item_unicode = item.decode('ascii') outpath = os.path.join(prefix, item_unicode.encode('utf-8')) try: os.makedirs(outpath) print '[OK] makedirs: %s' % outpath except OSError: print >> sys.stderr, [エラーメッセージ...]
とりあえず上のようにしてみたものの、これで良いのかは分からない。
タイムスタンプの復元
タイムスタンプはzipfile.ZipInfoオブジェクトのメンバdate_timeとして取り出すことができるが、その形式が特殊で、6つの値となる。*2
タイムスタンプを変更するためのos.utime()を用いるためには
- date_timeの各値を一度文字列の形にフォーマット付け(文字列に変換)する
- time.strptime()でその文字列を解析
- time.mktime()で形式変換
とする。
timestamp = time.mktime(time.strptime('%d/%02d/%02d %02d:%02d:%02d' % info.date_time, '%Y/%m/%d %H:%M:%S'))
(「PythonでZIPファイルを展開する(コード例)」に続く)
関連記事:
- urllibでダウンロードしたファイルのタイムスタンプをサーバ上の最終更新日時に合わせる
- コマンドラインオプションの解析を行う(概要、コード例、値の確認)
- コマンドラインオプションの解析を行う(エラー処理、ヘルプメッセージ)
- PythonでZIPファイルを展開する(コード例)
参考URL:
- Python ライブラリリファレンス: 12.4 zipfile -- ZIP アーカイブの処理
- Python ライブラリリファレンス: 14.2 time -- 時刻データへのアクセスと変換
- Python ライブラリリファレンス: 14.3 optparse -- より強力なコマンドラインオプション解析器
使用したバージョン:
- Python 2.5.2