Skip to content

Zip e decompressione di file con zipfile e shutil in Python

Python

In Python, puoi comprimere e decomprimere file, ovvero comprimere i file in un file ZIP ed estrarre un file ZIP con il modulo zipfile.

Inoltre, puoi facilmente comporre una directory (cartella) e decomprimere un file ZIP con make_archive() e unpack_archive() del modulo shutil.

Entrambi sono inclusi nella libreria standard, quindi non è richiesta alcuna installazione aggiuntiva.

In questo articolo vengono descritti i seguenti contenuti.

  • Comprimere una directory (cartella):shutil.make_archive()
  • Decomprimi un file:shutil.unpack_archive()
  • Nozioni di base sul modulo zipfile:ZipFile objects
  • Comprimi i singoli file in un file ZIP
  • Aggiungi altri file a un file ZIP esistente
  • Controlla l’elenco dei file in un file ZIP
  • Estrai singoli file da un file ZIP
  • Leggi i file in un file ZIP
  • ZIP con password (crittografia e decrittografia)

Comprimere una directory (cartella):shutil.make_archive()

È possibile comporre una directory (cartella), ovvero creare un file ZIP da una directory con shutil.make_archive().

Il primo parametro nome_base è il percorso senza estensione del file ZIP da creare formato il secondo parametro formato è il archivio (“zip”, ‘tar’, ‘gztar’, ‘bztar’, ‘xztar’) e il terzo parametro root_dir è il percorso della directory da comprimere.

Ad esempio, supponiamo che ci sia una directory dir_zip con la seguente struttura nella directory corrente.

dir_zip
├── dir_sub
│   └── file_sub.txt
└── file.txt

Comprimere questa directory in un file ZIP archive_shutil.zip nella directory corrente.

import shutil

shutil.make_archive('archive_shutil', format='zip', root_dir='dir_zip')

In questo caso, la directory specificata dir_zip non è inclusa in archive_shutil.zip.

Se si desidera includere la directory stessa, specificare il percorso della directory superiore della directory di destinazione nel terzo parametro root_dir e il percorso relativo alla directory di destinazione da root_dir nel quarto parametro base_dir.

shutil.make_archive('archive_shutil_base', format='zip',
                    root_dir='.', base_dir='dir_zip')

Vedere la sezione successiva per il risultato della decompressione.

Decomprimi un file:shutil.unpack_archive()

Puoi decomprimere un file, cioè estrarre tutto il contenuto di un file ZIP con shutil.unpack_archive().

Il primo parametro filename è il percorso del file ZIP e il secondo archivio parametro extract_dir è il percorso della directory di destinazione in cui viene estratto l’archivio.

shutil.unpack_archive('archive_shutil.zip', 'dir_out')

Si estrae come segue:

dir_out
├── dir_sub
│   └── file_sub.txt
└── file.txt

Dopo la documentazione non lo specifichi, sembra creare una nuova directory anche se extract_dir è inesistente (confermato in Python 3.9.9).

Il file ZIP creato da shutil.make_archive() con base_dir viene estratto come segue:

shutil.unpack_archive('archive_shutil_base.zip', 'dir_out_base')
dir_out_base
└── dir_zip
    ├── dir_sub
    │   └── file_sub.txt
    └── file.txt

Nozioni di base sul modulo zipfile:ZipFile objects

Il modulo zipfile fornisce la classe ZipFile per creare, leggere, scrivere, aggiungere ed elencare un file ZIP.

Gli oggetti ZipFile vengono creati specificando il primo file di parametro (percorso di un file ZIP) e la seconda modalità di parametro (leggi ‘r’, scrivi ‘w’, aggiungi ‘a’, ecc.) al costruttore zipfile.ZipFile() .

L’oggetto ZipFile deve essere chiuso con il metodo close(), ma se si utilizza l’istruzione with, viene chiuso automaticamente al termine del blocco.

L’utilizzo è simile alla lettura e alla scrittura di file con la funzione integrata open(), come la specifica della modalità e l’utilizzo dell’istruzione with.

Esempi specifici sono descritti nelle sezioni seguenti.

Comprimi i singoli file in un file ZIP

Per comprimere singoli file in un file ZIP, creare un nuovo oggetto ZipFile e aggiungere i file che si desidera comprimere con il metodo write().

Con zipfile.ZipFile(), specificare il percorso di un file ZIP appena creato come primo file di parametro e impostare la modalità del secondo parametro su ‘w’ (scrittura).

In modalità di scrittura, puoi anche specificare il metodo di compressione e il livello con i parametri compression e compresslevel.

La compressione del metodo di compressione è la seguente; BZIP2 e LZMA hanno un rapporto di compressione più elevato, ma la compressione richiede più tempo.

  • zipfile.ZIP_STORED: nessuna compressione (impostazione predefinita)
  • zipfile.ZIP_DEFLATED: normale compressione ZIP
  • zipfile.ZIP_BZIP2: compressione BZIP2
  • zipfile.ZIP_LZMA: compressione LZMA

Per ZIP_DEFLATED, il livello di compressione compresslevel corrisponde al livello di zlib.compressobj(). Il valore predefinito è -1 (Z_DEFAULT_COMPRESSION).

level è il livello di compressione: un numero intero compreso tra 0 e 9 o -1. Un valore di 1 (Z_BEST_SPEED) è il più veloce e produce la compressione minima, mentre un valore di 9 (Z_BEST_COMPRESSION) è il più lento e produce di più. 0 (Z_NO_COMPRESSION) non è compressione. Il valore predefinito è -1 (Z_DEFAULT_COMPRESSION). Z_DEFAULT_COMPRESSION rappresenta un compromesso predefinito tra velocità e compressione (attualmente equivalente al livello 6).
zlib.compressobj() — Compressione compatibile con gzip — Documentazione Python 3.10.2

Il metodo dell’oggetto ZipFile scrive il file chiamato il primo parametro nomefile in un file ZIP, assegnandogli il nome archivio (= nome in ZIP) secondo parametro nomearco. Se arcname viene omesso, filename viene utilizzato come nome dell’archivio. È possibile specificare una struttura di directory per arcname.

import zipfile

with zipfile.ZipFile('archive_zipfile.zip', 'w',
                     compression=zipfile.ZIP_DEFLATED,
                     compresslevel=9) as zf:
    zf.write('dir_zip/file.txt', arcname='file.txt')
    zf.write('dir_zip/dir_sub/file_sub.txt', arcname='dir_sub/file_sub.txt')

Puoi anche selezionare un metodo di compressione e un livello per ogni file specificando compress_type e compresslevel nel metodo write().

Aggiungi altri file a un file ZIP esistente

Per aggiungere un file ZIP esistente, con zipfile.ZipFile(), impostare il primo file di parametro sul percorso del file ZIP esistente e la seconda modalità su’a’ (aggiungere).

Aggiungi file esistente

È possibile aggiungere file esistenti con il metodo write() dell’oggetto ZipFile.

Quello che segue è un esempio di aggiunta di un altro_file.txt nella directory corrente. L’argomento arcname viene omesso.

with zipfile.ZipFile('archive_zipfile.zip', 'a') as zf:
    zf.write('another_file.txt')

Crea e aggiungi un nuovo file

Puoi anche creare un nuovo file e aggiungerlo. Utilizzare il metodo open() dell’oggetto ZipFile con la modalità append (‘a’).

Specificare il percorso del file appena creato in ZIP come primo parametro e impostare la modalità del secondo parametro su ‘w’.

Puoi scrivere il contenuto con il metodo write() dell’oggetto file aperto.

with zipfile.ZipFile('archive_zipfile.zip', 'a') as zf:
    with zf.open('dir_sub/new_file.txt', 'w') as f:
        f.write(b'text in new file')

L’argomento di write() dovrebbe essere specificato come byte, non str. Per scrivere un testo, usa b’…’, o convertilo con il metodo encode() di str.

print(type(b'text'))
# <class 'bytes'>

print(type('text'.encode('utf-8')))
# <class 'bytes'>

Un esempio di lettura di un file in ZIP con open() dell’oggetto ZipFile è più avanti avanti.

Controlla l’elenco dei file in un file ZIP

Per controllare il contenuto di un file ZIP esistente, creare un oggetto ZipFile con il primo file di parametro come percorso del file ZIP esistente e la seconda modalità di parametro come ‘r’ (lettura). modalità può essere omesso poiché l’impostazione predefinita è ‘r’.

È possibile ottenere un elenco di elementi archiviati con il metodo namelist() dell’oggetto ZipFile.

with zipfile.ZipFile('archive_zipfile.zip') as zf:
    print(zf.namelist())
# ['file.txt', 'dir_sub/file_sub.txt', 'another_file.txt', 'dir_sub/new_file.txt']

with zipfile.ZipFile('archive_shutil.zip') as zf:
    print(zf.namelist())
# ['dir_sub/', 'file.txt', 'dir_sub/file_sub.txt']

Come puoi vedere dal risultato sopra, gli ZIP creati con shutil.make_archive() elencano anche le directory individualmente. Lo stesso vale per gli ZIP compressi con la funzione standard di Finder su Mac.

È possibile eseguire directory con comprensioni di elenchi.

with zipfile.ZipFile('archive_shutil.zip') as zf:
    print([x for x in zf.namelist() if not x.endswith('/')])
# ['file.txt', 'dir_sub/file_sub.txt']

Per decomprimere un file ZIP, creare un oggetto ZipFile in modalità di lettura (‘r’, semplificato).

Se vuoi estrarre solo file specifici, usa il metodo extract().

Il primo membro del parametro è il nome del file da estrarre (inclusa la directory nel file zip) e il secondo il percorso del parametro è il percorso della directory in cui.

with zipfile.ZipFile('archive_zipfile.zip') as zf:
    zf.extract('file.txt', 'dir_out_extract')
    zf.extract('dir_sub/file_sub.txt', 'dir_out_extract')

Se vuoi estrarre tutti i file, usa il metodo extractall(). Specificare il percorso della directory in cui viene estratto il primo percorso dell’argomento.

with zipfile.ZipFile('archive_zipfile.zip') as zf:
    zf.extractall('dir_out_extractall')

In entrambi i casi, se il percorso viene omesso, i file vengono estratti nella directory corrente. Nonostante la documentazione non lo specifichi, sembra creare una nuova directory anche se il percorso è inesistente (confermato in Python 3.9.9).

Leggi i file in un file ZIP

Puoi leggere direttamente i file in un file ZIP.

Crea un oggetto ZipFile in modalità di lettura (impostazione predefinita) e apri il file all’interno con il metodo open().

Il primo argomento di open() è il nome di un file nello ZIP (può includere la directory). La seconda modalità può essere omessa poiché il valore predefinito è ‘r’ (lettura).

Il contenuto può essere letto con il metodo read() dell’oggetto file aperto. Viene restituita una stringa byte byte, che può essere convertita in una stringa str con il metodo decode().

with zipfile.ZipFile('archive_zipfile.zip') as zf:
    with zf.open('dir_sub/new_file.txt') as f:
        b = f.read()

print(b)
# b'text in new file'

print(type(b))
# <class 'bytes'>

s = b.decode('utf-8')
print(s)
# text in new file

print(type(s))
# <class 'str'>

Oltre a read(), si possono usare readline() e readlines() così come l’oggetto file aperto con la funzione incorporata open().

ZIP con password (crittografia e decrittografia)

Il modulo zipfile può decrittografare gli ZIP con password (ZIP crittografati), ma non può crittografare gli ZIP.

Supporta la decrittografia dei file crittografati negli archivi ZIP, ma al momento non è in grado di creare un file crittografato. La decrittazione è estremamente lenta poiché è in Python nativo anziché in C. zipfile
— Lavora con archivi implementa ZIP — Documentazione Python 3.10.2

Inoltre, AES non è supportato.

Il modulo zipfile della libreria standard Python supporta solo file zip crittografati CRC32 (vedi qui: http://hg.python.org/cpython/file/71adf21421d9/Lib/zipfile.py#l420 ).
zip – Python decomprime il file crittografato AES-128 – Stack Overflow

Né make_archive() né unpack_archive() supporta la funzionalità e la decrittografia.

cerniera lampo

Il pyzipper introdotto in Stack Overflow sopra supporta la richiesta e la decrittografia A e può essere utilizzato più o meno allo stesso modo di zipfile.

Per creare un file ZIP con una password, specificare encryption=pyzipper.WZ_AES con pyzipper.AESZipFile() e impostare la password con il metodo setpassword(). Si noti che è necessario specificare la password con i byte della stringa di byte.

import pyzipper

with pyzipper.AESZipFile('archive_with_pass.zip', 'w',
                         encryption=pyzipper.WZ_AES) as zf:
    zf.setpassword(b'password')
    zf.write('dir_zip/file.txt', arcname='file.txt')
    zf.write('dir_zip/dir_sub/file_sub.txt', arcname='dir_sub/file_sub.txt')

Quello che segue è un esempio di decompressione di un file ZIP con una password.

with pyzipper.AESZipFile('archive_with_pass.zip') as zf:
    zf.setpassword(b'password')
    zf.extractall('dir_out_pyzipper')

Naturalmente, se la password è sbagliata, non può essere decifrata.

# with pyzipper.AESZipFile('archive_with_pass.zip') as zf:
#     zf.setpassword(b'wrong_password')
#     zf.extractall('dir_out_pass')
# RuntimeError: Bad password for file 'file.txt'

Il modulo zipfile consente anche di specificare una password, ma come accennato in precedenza, non supporta AES.

# with zipfile.ZipFile('archive_with_pass.zip') as zf:
#     zf.setpassword(b'password')
#     zf.extractall('dir_out_pass')
# NotImplementedError: That compression method is not supported

Esegui il comando con subprocess.run()

Puoi anche utilizzare zipfile o pyzipper non utilizzare, ma il comando può comunque.

Usa il comando 7z di 7-zip (installazione richiesta) come esempio.

import subprocess

subprocess.run(['7z', 'x', 'archive_with_pass.zip', '-ppassword', '-odir_out_7z'])

Equivalente ai seguenti comandi. -x è l’espansione. Si noti che -pe -o non lasciare spazi.

$ 7z x archive_with_pass.zip -ppassword -odir_out_pass_7z'