Работа с архивами формата zip средствами Python
Введение в работу с zip-архивами в Python
Модуль zipfile является стандартным средством для работы с архивами формата ZIP. Он позволяет создавать, читать, извлекать и изменять архивы. Код ниже демонстрирует базовые операции.
Основной класс ZipFile предоставляет контекстный менеджер, что обеспечивает корректное закрытие архива. Рассмотрим создание и извлечение архива.
Чтение существующего архива
import zipfile
with zipfile.ZipFile('example.zip', 'r') as zf:
print(zf.namelist())
data = zf.read('file1.txt')
print(data.decode('utf-8'))
Python zip файл (работа с zip-архивами в python)
Создание архива
import zipfile
with zipfile.ZipFile('new_archive.zip', 'w') as zf:
zf.write('file1.txt')
zf.write('file2.txt', arcname='renamed.txt')
Zip архив python (работа с zip-архивами в python)
Параметр arcname позволяет задать имя внутри архива, отличное от исходного.
Распространенная ошибка: попытка создать архив с несуществующими файлами приводит к FileNotFoundError. Проверка наличия файлов с помощью os.path.exists() поможет избежать этого.
Как добавить файл в существующий архив?
Для добавления файлов используется режим 'a' (append).
import zipfile
with zipfile.ZipFile('existing.zip', 'a') as zf:
zf.write('new_file.txt')
Если архив уже содержит файл с таким же именем, он будет добавлен повторно. Чтобы избежать дублирования, можно проверять namelist() перед записью.
Как создать архив из строковых данных?
Метод writestr записывает строку или байты в архив под указанным именем.
import zipfile
with zipfile.ZipFile('string_archive.zip', 'w') as zf:
zf.writestr('hello.txt', 'Привет, мир!')
zf.writestr('data.txt', 'Some data')
Строки должны быть закодированы в байты. Для текста лучше указывать кодировку явно (например, 'utf-8').
Как извлечь только определённый файл?
Метод extract извлекает один файл по имени или ZipInfo.
with zipfile.ZipFile('archive.zip', 'r') as zf:
zf.extract('important.pdf', 'extracted/')
Если файл в архиве не найден, возникает KeyError. Проверить наличие можно через namelist().
Как защитить архив паролем?
ZipFile поддерживает шифрование с помощью пароля (метод ZipCrypto). Пароль задаётся при создании или открытии.
import zipfile
# Создание защищённого архива
with zipfile.ZipFile('protected.zip', 'w', compression=zipfile.ZIP_DEFLATED) as zf:
zf.setpassword(b'secret')
zf.writestr('private.txt', 'Секретные данные')
# Извлечение защищённого архива
with zipfile.ZipFile('protected.zip', 'r') as zf:
zf.setpassword(b'secret')
data = zf.read('private.txt')
print(data.decode())
Шифрование ZipCrypto не является стойким. Для более надёжной защиты рекомендуется использовать сторонние библиотеки, поддерживающие AES. Также при указании неправильного пароля возникает RuntimeError.
Как получить информацию о файлах в архиве без извлечения?
Метод infolist возвращает список объектов ZipInfo, содержащих размер, дату, сжатый размер, CRC и другие метаданные.
with zipfile.ZipFile('info.zip', 'r') as zf:
for info in zf.infolist():
print(f'{info.filename}: {info.file_size} байт -> {info.compress_size} байт')
При работе с большими архивами перебор всех файлов может быть медленным. Для выборочного доступа используется getinfo().
Как обработать архив, если файлы имеют нелатинские имена?
По умолчанию zipfile использует кодировку cp437 для имён файлов. Современные архивы могут использовать UTF-8, что устанавливается флагом в архиве. При чтении рекомендуется указывать метаданные encoding.
with zipfile.ZipFile('cyrillic.zip', 'r') as zf:
names = zf.namelist()
print(names)
Если имена отображаются кракозябрами, необходимо указать параметр metadata_encoding при открытии архива, но поддержка ограничена. Альтернатива - использовать библиотеку pyzipper с поддержкой Unicode.
Пример 1: Создание архива с разными методами сжатия
В zipfile доступны методы: ZIP_STORED (без сжатия), ZIP_DEFLATED (zlib), ZIP_BZIP2 (bzip2), ZIP_LZMA (lzma). Уровень сжатия для DEFLATED от 0 до 9, для BZIP2 от 1 до 9, для LZMA от 0 до 9.
import zipfile, os
with zipfile.ZipFile('methods.zip', 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
zf.write('bigfile.dat')
Файл размером 100MB сжат до 10MB (зависит от данных).
Для выбора метода при создании каждого отдельного файла используется параметр compress_type в write.
with zipfile.ZipFile('mixed.zip', 'w') as zf:
zf.write('uncompressed.txt', compress_type=zipfile.ZIP_STORED)
zf.write('compressed.txt', compress_type=zipfile.ZIP_DEFLATED)
Архив содержит файлы с разным сжатием. Проверить метод можно через ZipInfo.compress_type.
Пример 2: Извлечение с проверкой целостности
ZipFile может проверять CRC при извлечении. Если данные повреждены, будет ошибка. Обработка позволяет продолжить извлечение.
import zipfile
with zipfile.ZipFile('maybe_corrupt.zip', 'r') as zf:
for name in zf.namelist():
try:
zf.extract(name, 'out/')
except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
print(f'Проблема с файлом {name}: {e}')
Проблема с файлом broken.txt: Bad CRC-32
Пример 3: Работа с архивом в памяти с использованием BytesIO
Создание и чтение архива, который не сохраняется на диск. Полезно для передачи через сеть или временных операций.
import zipfile
from io import BytesIO
buf = BytesIO()
with zipfile.ZipFile(buf, 'w') as zf:
zf.writestr('note.txt', 'Этот архив в памяти')
zf.writestr('data.csv', 'a,b,c\n1,2,3')
buf.seek(0)
with zipfile.ZipFile(buf, 'r') as zf:
names = zf.namelist()
print(names)
note = zf.read('note.txt').decode('utf-8')
print(note)
['note.txt', 'data.csv'] Этот архив в памяти
Пример 4: Архив с комментарием и метаданными
Каждый объект ZipInfo содержит расширенные атрибуты, включая время модификации, права доступа, комментарии.
import zipfile, datetime
with zipfile.ZipFile('metadata.zip', 'w') as zf:
info = zipfile.ZipInfo('file.txt')
info.comment = b'Important file'
info.date_time = (2025, 3, 15, 10, 30, 0)
zf.writestr(info, 'Content')
При чтении:
with zipfile.ZipFile('metadata.zip', 'r') as zf:
info = zf.getinfo('file.txt')
print(info.comment, info.date_time)
Результат: b'Important file' (2025, 3, 15, 10, 30, 0)
Пример 5: Многократное добавление файлов в архив с проверкой дубликатов
При добавлении в существующий архив желательно избегать дублирования имён. Можно создать временный архив и перезаписать.
import zipfile, os
def add_to_zip(zip_name, file_name, new_name=None):
if not os.path.exists(zip_name):
with zipfile.ZipFile(zip_name, 'w') as zf:
zf.write(file_name, arcname=new_name)
else:
tmp_name = zip_name + '.tmp'
with zipfile.ZipFile(zip_name, 'r') as src, zipfile.ZipFile(tmp_name, 'w') as dst:
for item in src.infolist():
data = src.read(item.filename)
dst.writestr(item, data)
dst.write(file_name, arcname=new_name)
os.replace(tmp_name, zip_name)
add_to_zip('data.zip', 'newfile.txt')
Функция добавляет файл, копируя все предыдущие в новый архив. Это неэффективно для больших архивов, но избегает дубликатов.