概要(zipfile は「作る・入れる・取り出す」をひとまとめにする標準機能)
zipfile は ZIP アーカイブの作成・追加・一覧取得・解凍を行う標準ライブラリです。外部インストール不要で、ファイル単体からフォルダ丸ごとまで扱えます。重要なのは「モード(w/a/r/x)の選択」「圧縮方式と arcname の設計」「安全な解凍(パスの検証)」です。with 文で ZipFile を開いて閉じ忘れをなくし、pathlib と組み合わせると結合・作成・読み書きが安全にまとまります。
基本の操作(ここが重要)
ZIP を新規作成・追加・一覧・解凍
from zipfile import ZipFile, ZIP_DEFLATED
from pathlib import Path
# 新規作成(書き込みモード 'w'。既存があれば上書き)
with ZipFile("archive.zip", mode="w", compression=ZIP_DEFLATED) as z:
z.write("data/report.csv", arcname="report.csv") # 中の名前を制御(後述)
# 既存 ZIP に追加('a')
with ZipFile("archive.zip", mode="a", compression=ZIP_DEFLATED) as z:
z.write("images/logo.png", arcname="img/logo.png")
# 中身の一覧(ファイル名の列)
with ZipFile("archive.zip", mode="r") as z:
print(z.namelist())
# 全解凍(先に出力先フォルダを作る)
out = Path("extracted")
out.mkdir(parents=True, exist_ok=True)
with ZipFile("archive.zip") as z:
z.extractall(out)
Pythonモード選択の要点(w/a/r/x)
- w: 新規作成。既存があれば上書き。
- a: 追記。既存の内容は保持、追加分を入れる。
- r: 読み込みのみ。書き込み不可。
- x: 新規専用。既存なら失敗(安全に新規作成したいとき)。
圧縮の詳細と設計(方式・arcname・レベル)
圧縮方式と圧縮レベル
- ZIP_STORED: 無圧縮。高速だがサイズは減らない。
- ZIP_DEFLATED: 一般的な deflate。ほとんどの場面でこれ。
- ZIP_BZIP2 / ZIP_LZMA: より高圧縮(重い)。テキスト中心・サイズ重視なら検討。
from zipfile import ZipFile, ZIP_BZIP2
with ZipFile("docs.zip", "w", compression=ZIP_BZIP2, compresslevel=9) as z:
z.write("docs/manual.txt")
Pythoncompresslevel は 0〜9(方式に依存)。高いほど小さく・遅くなります。
arcname(ZIP 内のパス)で「見やすく安全に」
from zipfile import ZipFile
with ZipFile("bundle.zip", "w") as z:
# 絶対パスは入れない。必要な相対構造だけを格納
z.write("project/README.md", arcname="README.md")
z.write("project/config/app.yaml", arcname="config/app.yaml")
Python保存名(arcname)を制御すると、展開後の構成が分かりやすくなり、不要なローカルパス混入も防げます。
文字列データを直接入れる(writestr)
from zipfile import ZipFile
with ZipFile("conf.zip", "w") as z:
z.writestr("settings.ini", "name=alice\nretry=3\n")
Python生成物や小さなテキストをファイル化せずに格納できます。
ディレクトリ丸ごと圧縮(再帰・除外・相対化)
os.walk / pathlib で再帰して入れる
from zipfile import ZipFile, ZIP_DEFLATED
from pathlib import Path
root = Path("project")
with ZipFile("project.zip", "w", compression=ZIP_DEFLATED) as z:
for p in root.rglob("*"):
if p.is_file():
z.write(p, arcname=p.relative_to(root)) # ルートからの相対パス
Python除外ルールを挟む(例:ビルド生成物除外)
exclude = {".git", "__pycache__"}
with ZipFile("src.zip", "w") as z:
for p in Path("src").rglob("*"):
if any(part in exclude for part in p.parts): # 途中に該当フォルダがあれば除外
continue
if p.is_file():
z.write(p, arcname=p.relative_to("src"))
Python相対化は relative_to で確実に(絶対やカレント依存を避ける)。
解凍の安全策(ZIP スリップ対策・部分抽出)
ZIP スリップ(パストラバーサル)を防ぐ
悪意ある ZIP は「../」を含む格納名で、意図外の場所へ書き出す恐れがあります。抽出先を基準として検証します。
from zipfile import ZipFile
from pathlib import Path
def safe_extract(zpath, outdir):
outdir = Path(outdir).resolve()
with ZipFile(zpath) as z:
for name in z.namelist():
dest = (outdir / name).resolve()
if outdir not in dest.parents and dest != outdir:
raise ValueError(f"危険なパス: {name}")
z.extract(name, outdir)
safe_extract("archive.zip", "extracted")
Python部分的に取り出す(特定ファイルだけ)
from zipfile import ZipFile
with ZipFile("archive.zip") as z:
z.extract("config/app.yaml", "extracted")
data = z.read("README.md") # bytes を直接取得
Pythonパスワード付き ZIP の注意
zipfile は「復号(読み取り)」のパスワード指定は可能ですが、暗号化して「書き込み」は標準では対応が限定的です(外部ライブラリの検討)。読み取りは setpassword、または extract/read で pwd 引数を使います。
from zipfile import ZipFile
with ZipFile("secure.zip") as z:
z.setpassword(b"secret")
z.extractall("out")
Python実務パターン(バックアップ・配布パッケージ・インメモリ)
日付フォルダのバックアップを ZIP 化
from zipfile import ZipFile, ZIP_DEFLATED
from pathlib import Path
from datetime import date
src = Path("logs") / date.today().strftime("%Y/%m/%d")
out = Path("backup"); out.mkdir(parents=True, exist_ok=True)
zip_path = out / f"logs_{date.today().strftime('%Y%m%d')}.zip"
with ZipFile(zip_path, "w", ZIP_DEFLATED) as z:
for p in src.rglob("*.log"):
z.write(p, arcname=p.relative_to(src))
Python配布用に「必要ファイルだけ」束ねる
from zipfile import ZipFile
files = ["README.md", "LICENSE", "dist/app.exe"]
with ZipFile("release.zip", "w") as z:
for f in files:
z.write(f, arcname=Path(f).name) # ルート直下へ平坦化
Pythonインメモリで生成(HTTP 応答など)
from zipfile import ZipFile, ZIP_DEFLATED
from io import BytesIO
buf = BytesIO()
with ZipFile(buf, "w", ZIP_DEFLATED) as z:
z.writestr("hello.txt", "world")
# buf.getvalue() をレスポンスへ
Pythonファイルシステムを使わず、その場で ZIP バイト列を作れます。
よくある落とし穴の回避(上書き・巨大サイズ・メタデータ)
上書きと追記の混同を避ける
- w: 既存 ZIP を消して作り直す。
- a: 既存へ追加する。重複ファイル名が混在し得るため、設計か検証で回避。
巨大ファイル・大量ファイルの扱い
- allowZip64(デフォルト有効)で 2GB 超にも対応。
- 圧縮方式・レベルはサイズと速度のトレードオフ。テキスト中心なら高圧縮が効きやすい。バイナリ(PNG/JPEG など)はすでに圧縮済みで効果薄。
パーミッション・タイムスタンプなどのメタデータ
ZipInfo(infolist)から属性や時刻を参照可能。完全な復元が必要なら、格納時の方針を決める(例:arcname、展開先で chmod など)。
from zipfile import ZipFile
with ZipFile("archive.zip") as z:
for info in z.infolist():
print(info.filename, info.file_size, info.date_time)
Python例題で身につける(定番から一歩先まで)
例題1:フォルダ丸ごとを相対パスで圧縮
from zipfile import ZipFile, ZIP_DEFLATED
from pathlib import Path
root = Path("project")
with ZipFile("project.zip", "w", ZIP_DEFLATED) as z:
for p in root.rglob("*"):
if p.is_file():
z.write(p, arcname=p.relative_to(root))
Python例題2:追記で構成を整える(サブフォルダに入れる)
from zipfile import ZipFile, ZIP_DEFLATED
with ZipFile("bundle.zip", "a", ZIP_DEFLATED) as z:
z.write("docs/manual.pdf", arcname="docs/manual.pdf")
z.writestr("VERSION", "1.2.3")
Python例題3:安全抽出(ZIP スリップ対策)
from zipfile import ZipFile
from pathlib import Path
def safe_extract(zpath, outdir):
outdir = Path(outdir).resolve()
with ZipFile(zpath) as z:
for name in z.namelist():
dest = (outdir / name).resolve()
if outdir not in dest.parents and dest != outdir:
raise ValueError(f"危険なパス: {name}")
z.extract(name, outdir)
safe_extract("bundle.zip", "out")
Python例題4:インメモリ ZIP を作ってすぐ使う
from zipfile import ZipFile, ZIP_DEFLATED
from io import BytesIO
buf = BytesIO()
with ZipFile(buf, "w", ZIP_DEFLATED) as z:
z.writestr("hello.txt", "こんにちは")
data = buf.getvalue() # この bytes を送信・保存などに使う
Pythonまとめ
zipfile は「モード選択(w/a/r/x)」「圧縮方式とレベル」「arcname の設計」を押さえれば、配布・バックアップ・一括保存が短く安全に書けます。フォルダ丸ごと圧縮は relative_to で相対化、不要物は除外。解凍は ZIP スリップ対策で出力先配下に限定。with 文で確実にクローズし、pathlib と併用して結合・作成・検証を段階化すれば、現場で使える「壊れない ZIP 処理」になります。
