Архивация файлов в Python: ZIP-архивы
В современных проектах часто возникает необходимость упаковывать файлы в архивы или извлекать данные из готовых ZIP-архивов. Стандартная библиотека Python предлагает для этого модуль zipfile, который позволяет создавать, читать и изменять ZIP-архивы без установки дополнительных пакетов. В статье рассматриваются основные приёмы работы, альтернативные подходы и типичные сложности.
Основной подход: модуль zipfile
Наиболее универсальное решение - использование класса ZipFile из модуля zipfile. Он поддерживает методы для добавления файлов, извлечения, а также работу с паролями и сжатием.
Как создать ZIP-архив и добавить в него несколько файлов?
import zipfile
with zipfile.ZipFile('example.zip', 'w') as zf:
zf.write('file1.txt')
zf.write('file2.txt')Python zip файл (работа с zip-архивами в python)
Архив создаётся в режиме 'w' (запись). Если файл уже существует, он будет перезаписан. Добавление выполняется методом write(), который принимает имя файла в текущей директории. Можно указать другое имя внутри архива с помощью параметра arcname.
Типичная ошибка: попытка добавить несуществующий файл приводит к исключению FileNotFoundError. Перед добавлением следует проверять наличие файла или использовать блок try/except.
Как извлечь все файлы из архива в текущую директорию?
with zipfile.ZipFile('example.zip', 'r') as zf:
zf.extractall()Zip архив python (работа с zip-архивами в python)
Метод extractall() распаковывает всё содержимое в текущую папку. Для указания другого пути используется параметр path.
Проблема: при извлечении из архива, созданного в другой операционной системе, могут нарушиться права доступа или возникнуть конфликты с кодировкой имён файлов. Рекомендуется использовать параметр members для выборочного извлечения или проверять имена через namelist().
Как защитить архив паролем?
with zipfile.ZipFile('protected.zip', 'w') as zf:
zf.setpassword(b'secret')
zf.write('data.txt')
Пароль задаётся методом setpassword() в виде байтовой строки. При извлечении пароль необходимо указать повторно для того же объекта.
Важно: модуль zipfile поддерживает только шифрование по стандарту ZIP 2.0 (с использованием PKZIP). Более современные методы (AES) не реализованы. Для надёжного шифрования лучше использовать сторонние библиотеки, например, pyzipper.
Альтернативные решения
Как быстро создать архив из целой директории без ручного перечисления файлов?
Использование shutil.make_archive
import shutil
shutil.make_archive('backup', 'zip', '/path/to/dir')
Функция make_archive автоматически упаковывает всё содержимое указанной папки. Результат - файл backup.zip в текущей директории. Это удобно для быстрого резервного копирования, но не позволяет тонко настраивать сжатие или исключать файлы.
Ограничение: функция не поддерживает добавление отдельных файлов, только целые директории. Если нужно добавить несколько разрозненных файлов, придётся предварительно скопировать их во временную папку.
Как создать архив в памяти без записи на диск?
Работа с BytesIO
from io import BytesIO
import zipfile
buf = BytesIO()
with zipfile.ZipFile(buf, 'w') as zf:
zf.writestr('file.txt', 'Hello, world!')
# buf содержит архив в виде байтов
Метод writestr() записывает строку или байты непосредственно в архив без создания временного файла. Такой приём полезен для отправки архива через HTTP или сохранения в базу данных.
Нюанс: после записи указатель буфера остаётся в конце. Для чтения архива из того же буфера нужно вызвать buf.seek(0).
Как работать с архивами других форматов (RAR, 7z) или с расширенными возможностями?
Сторонние библиотеки: patool и py7zr
import patoolib
patoolib.create_archive('example.rar', ('file1.txt', 'file2.txt'))
Библиотека patool предоставляет единый интерфейс для множества архиваторов (7z, rar, tar и др.). Для её работы требуется наличие соответствующих утилит в системе (например, 7-Zip).
import py7zr
with py7zr.SevenZipFile('example.7z', 'w') as z:
z.writeall('/path/to/dir')
py7zr позволяет создавать и извлекать 7z-архивы с поддержкой сжатия LZMA2 и шифрования AES. Это хорошая альтернатива, если требуется высокая степень сжатия.
Зависимости: обе библиотеки требуют установки через pip. Кроме того, patool вызывают внешние программы, что может быть нежелательно в изолированных средах.
Расширенные примеры работы с ZIP
Приведённые ниже примеры демонстрируют более тонкие настройки и нестандартные сценарии использования модуля zipfile.
1. Рекурсивное архивирование с сохранением структуры папок
import zipfile
import os
def zip_dir(dir_path, zip_path):
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk(dir_path):
for file in files:
full_path = os.path.join(root, file)
arcname = os.path.relpath(full_path, start=dir_path)
zf.write(full_path, arcname)
zip_dir('my_folder', 'output.zip')
Архив output.zip содержит все файлы из my_folder, сохраняя вложенную структуру.
Параметр zipfile.ZIP_DEFLATED включает сжатие (алгоритм Deflate). Без него файлы сохраняются без сжатия (ZIP_STORED).
2. Добавление файла с указанием степени сжатия
with zipfile.ZipFile('compressed.zip', 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
zf.write('big_file.dat')
Уровень сжатия от 0 (без сжатия) до 9 (максимальное). По умолчанию используется -1, что соответствует уровню по умолчанию (обычно 6).
3. Чтение содержимого архива без извлечения
with zipfile.ZipFile('example.zip', 'r') as zf:
for info in zf.infolist():
print(f'{info.filename} - {info.file_size} -> {info.compress_size}')
file1.txt - 1024 -> 512 subdir/file2.dat - 2048 -> 1000
Метод infolist() возвращает список объектов ZipInfo с метаданными: имя, размеры, дата, CRC.
4. Извлечение одного файла с сохранением его в поток
with zipfile.ZipFile('archive.zip', 'r') as zf:
data = zf.read('secret.txt')
# data теперь содержит байты файла
Метод read() извлекает файл в память. Это удобно, когда файл малого размера и не требуется сохранять на диск.
5. Обработка архива с кириллическими именами (кодировка CP866 или UTF-8)
import zipfile
with zipfile.ZipFile('cyrillic.zip', 'r') as zf:
# Попытка чтения с использованием CP866 (часто используется в Windows)
try:
names = [name.encode('cp437').decode('cp866') for name in zf.namelist()]
except:
# Если не получилось, пробуем UTF-8
names = [name.encode('cp437').decode('utf-8') for name in zf.namelist()]
print(names)
['документ.txt', 'отчёты/2023.pdf']
Проблема кодировки имён - частая сложность при работе с архивами, созданными в разных ОС. Приведённый способ не идеален, но часто помогает.
6. Создание многотомного (разделённого) архива
import zipfile
with zipfile.ZipFile('split.zip', 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as zf:
zf.write('large_file.bin')
# Сам модуль zipfile не умеет разбивать на тома, но allowZip64 позволяет работать с архивами >4 ГБ.
Для настоящего разделения на несколько файлов (томов) требуется внешняя утилита, например, 7-Zip. Python может только создать архив, который затем будет разбит.