zipfile - Accede a un Archivio ZIP

Scopo: Legge e scrive file compressi ZIP

Il modulo zipfile può essere usato per manipolare file di archivio ZIP, il formato reso popolare dal programma per pc PKZIP.

Verificare file ZIP

La funzione is_zipfile() ritorna un booleano che indica se il nome di file passato come argomento faccia riferimento o meno a un archivio ZIP valido.

# zipfile_is_zipfile.py

import zipfile

for filename in [ 'LEGGIMI.txt', 'esempio.zip',
                  'cattivo_esempio.zip', 'nonqui.zip' ]:
    print('{:>20}  {}'.format(
        filename, zipfile.is_zipfile(filename)))

Se il file non esiste, is_zipfile() ritorna False.

$ python3  zipfile_is_zipfile.py

         LEGGIMI.txt  False
         esempio.zip  True
 cattivo_esempio.zip  False
          nonqui.zip  False

Leggere i Metadati da un Archivio

Si usi la classe Zipfile per lavorare direttamente con un archivio ZIP. Essa supporta sia metodi per leggere dati riguardo archivi esistenti che per modificare gli archivi aggiungendo file.

# zipfile_namelist.py

import zipfile

with zipfile.ZipFile('esempio.zip', 'r') as zf:
    print(zf.namelist())

Il metodo namelist() ritorna i nomi dei file di un archivio esistente.

$ python3 zipfile_namelist.py

['LEGGIMI.txt']

L'elenco di nomi è l'unica parte di informazioni disponibili dall'archivio. Per accedere a tutti i metadati circa il contenuto dello ZIP si usino i metodi infolist() oppure getinfo().

# zipfile_infolist.py

import datetime
import zipfile


def print_info(archive_name):
    with zipfile.ZipFile(archive_name) as zf:
        for info in zf.infolist():
            print(info.filename)
            print('  Commento    :', info.comment)
            mod_date = datetime.datetime(*info.date_time)
            print('  Modificato  :', mod_date)
            if info.create_system == 0:
                system = 'Windows'
            elif info.create_system == 3:
                system = 'Unix'
            else:
                system = 'SCONOSCIUTO'
            print('  Sistema      :', system)
            print('  Versione ZIP :', info.create_version)
            print('  Compressi    :', info.compress_size, 'byte')
            print('  Non compressi:', info.file_size, 'byte')
            print()

if __name__ == '__main__':
    print_info('esempio.zip')

Ci sono campi addizionali, oltre a quelli stampati qui, ma trascodificare i valori in qualcosa che abbia una qualsiasi utilità richiede una attenta lettura delle note dell'applicazione PKZIP con le specifiche del file ZIP.

$ python3 zipfile_infolist.py

LEGGIMI.txt
  Commento    : b''
  Modificato  : 2018-07-29 10:25:20
  Sistema      : Unix
  Versione ZIP : 63
  Compressi    : 56 byte
  Non compressi: 61 byte

Se il nome del membro di un archivio è noto in anticipo, è possibile ricavare un oggetto ZipInfo direttamente tramite getinfo().

# zipfile_getinfo.py

import zipfile

with zipfile.ZipFile('esempio.zip') as zf:
    for filename in ['LEGGIMI.txt', 'nonqui.txt']:
        try:
            info = zf.getinfo(filename)
        except KeyError:
            print('ERRORE: Non trovato {} nel file zip'.format(
                filename))
        else:
            print('{} è {} byte'.format(
                info.filename, info.file_size))

Se un membro di archivio non è presente, getinfo() solleva una eccezione.

$ python3 zipfile_getinfo.py

LEGGIMI.txt è 61 byte
ERRORE: Non trovato nonqui.txt nel file zip

Estrarre File da un Archivio

Per accedere ai dati di un membro di un archivio, si usi il metodo read(), passando il nome del membro.

# zipfile_read.py

import zipfile

with zipfile.ZipFile('esempio.zip') as zf:
    for filename in ['LEGGIMI.txt', 'nonqui.txt']:
        try:
            data = zf.read(filename)
        except KeyError:
            print('ERRORE: Non trovato {} nel file zip'.format(
                filename))
        else:
            print(filename, ':')
            print(data)
        print()

I dati, se necessario, sono automaticamente decompressi.

$ python3 zipfile_read.py

LEGGIMI.txt :
b'Gli esempi per i moduli zipfile e tarfile usano questo file.\n'

ERRORE: Non trovato nonqui.txt nel file zip

Creare Nuovi Archivi

Per creare un nuovo archivio, si istanzi ZipFile con modalità 'w'. Qualunque file esistente verrà troncato e verrà inizializzato un nuovo archivio. Per aggiungere file si usi il metodo write().

# zipfile_write.py

from zipfile_infolist import print_info
import zipfile

print('creazione archivio')
with zipfile.ZipFile('write.zip', mode='w') as zf:
    print('aggiungo LEGGIMI.txt')
    zf.write('LEGGIMI.txt')

print()
print_info('write.zip')

Nella modalità predefinita, i contenuti dell'archivio non sono compressi.

$ python3 zipfile_write.py

creazione archivio
aggiungo LEGGIMI.txt

LEGGIMI.txt
  Commento    : b''
  Modificato  : 2018-07-29 10:23:42
  Sistema      : Unix
  Versione ZIP : 20
  Compressi    : 61 byte
  Non compressi: 61 byte

Per aggiungere compressione, è richiesto il modulo zlib. Se zlib è disponibile, la modalità di compressione per i singoli file o per l'intero archivio può essere impostata usando zipfile.ZIP_DEFLATED. La modalità di compressione predefinita è zipfile_ZIP_STORED, che aggiunge file all'archivio senza comprimerli.

# zipfile_write_compression.py

from zipfile_infolist import print_info
import zipfile
try:
    import zlib
    compression = zipfile.ZIP_DEFLATED
except (ImportError, AttributeError):
    compression = zipfile.ZIP_STORED

modes = {
    zipfile.ZIP_DEFLATED: 'compresso',
    zipfile.ZIP_STORED: 'conservato',
}

print('creazione archivio')
with zipfile.ZipFile('write_compression.zip', mode='w') as zf:
    mode_name = modes[compression]
    print('aggiungo LEGGIMI.txt con modalità compressione', mode_name)
    zf.write('LEGGIMI.txt', compress_type=compression)

print()
print_info('write_compression.zip')

In questo caso, il membro dell'archivio è compresso.

$ python3 zipfile_write_compression.py

creazione archivio
aggiungo LEGGIMI.txt con modalità compressione compresso

LEGGIMI.txt
  Commento    : b''
  Modificato  : 2018-07-29 10:23:42
  Sistema      : Unix
  Versione ZIP : 20
  Compressi    : 56 byte
  Non compressi: 61 byte

Usare Nomi di Membri di Archivio Sostitutivi

Per aggiungere un file a un archivio usando un nome diverso da quello originale si usi l'argomento arcname per write().

# zipfile_write_arcname.py

from zipfile_infolist import print_info
import zipfile

with zipfile.ZipFile('write_arcname.zip', mode='w') as zf:
    zf.write('LEGGIMI.txt', arcname='NON_LEGGIMI.txt')

print_info('write_arcname.zip')

Non vi è traccia del nome file originale nell'archivio.

$ python3 zipfile_write_arcname.py

NON_LEGGIMI.txt
  Commento    : b''
  Modificato  : 2018-07-29 10:23:42
  Sistema      : Unix
  Versione ZIP : 20
  Compressi    : 61 byte
  Non compressi: 61 byte

Scrivere Dati da Sorgenti Diverse da File

Talvolta è necessario scrivere verso un archivio ZIP usando dati che non provengono da un file esistente. Piuttosto che scrivere dati a un file, quindi aggiungere lo stesso al file ZIP, si usi il metodo writestr() che aggiunge una stringa di byte all'archivio direttamente.

# zipfile_writestr.py

from zipfile_infolist import print_info
import zipfile

msg = 'Questi dati non esistono in un file.'
with zipfile.ZipFile('writestr.zip',
                     mode='w',
                     compression=zipfile.ZIP_DEFLATED,
                     ) as zf:
    zf.writestr('da_una_stringa.txt', msg)

print_info('writestr.zip')

with zipfile.ZipFile('writestr.zip', 'r') as zf:
    print(zf.read('da_una_stringa.txt'))

In questo caso, l'argomento compress_type di ZipFile è stato usato per comprimere i dati visto che writestr() non prevede un argomento che specifichi la compressione.

$ python3 zipfile_writestr.py

da_una_stringa.txt
  Commento    : b''
  Modificato  : 2021-06-03 09:16:42
  Sistema      : Unix
  Versione ZIP : 20
  Compressi    : 36 byte
  Non compressi: 36 byte

b'Questi dati non esistono in un file.'

Scrivere con una Istanza di ZipInfo

Normalmente la data di modifica viene calcolata quando un file o una stringa vengono aggiunti all'archivio. Una istanza di ZipInfo può essere passata a writestr() per definire la data di modifica e altri metadati.

# zipfile_writestr_zipinfo.py

import time
import zipfile
from zipfile_infolist import print_info

msg = b'Questi dati non esistono in un file.'

with zipfile.ZipFile('writestr_zipinfo.zip',
                     mode='w',
                     ) as zf:
    info = zipfile.ZipInfo('da_stringa.txt',
                           date_time=time.localtime(time.time()),
                           )
    info.compress_type = zipfile.ZIP_DEFLATED
    info.comment = b'I commenti vanno qui'
    info.create_system = 0
    zf.writestr(info, msg)

print_info('writestr_zipinfo.zip')

In questo esempio, l'orario modificato viene impostato all'orario corrente, i dati sono compressi, e viene usato un valore False per create_system. Viene anche associato un semplice commento al nuovo file.

$ python3 zipfile_writestr_zipinfo.py

da_stringa.txt
  Commento    : b'I commenti vanno qui'
  Modificato  : 2021-06-03 09:16:42
  Sistema      : Windows
  Versione ZIP : 20
  Compressi    : 36 byte
  Non compressi: 36 byte

Aggiungere File

Oltre alla creazione di nuovi archivi, è possibile aggiungere in un archivio esistente, oppure aggiungere un archivio a un file esistente (tipo un file a.exe per un archivio auto estraente). Per aprire un archivio per aggiungere un file si usi la modalità 'a'.

# zipfile_append.py

from zipfile_infolist import print_info
import zipfile

print('creazione archivio')
with zipfile.ZipFile('append.zip', mode='w') as zf:
    zf.write('LEGGIMI.txt')

print()
print_info('append.zip')

print("aggiungo all'archivio")
with zipfile.ZipFile('append.zip', mode='a') as zf:
    zf.write('LEGGIMI.txt', arcname='LEGGIMI2.txt')

print()
print_info('append.zip')

L'archivio risultante contiene due membri:

$ python3 zipfile_append.py

creazione archivio

LEGGIMI.txt
  Commento    : b''
  Modificato  : 2018-07-29 10:23:42
  Sistema      : Unix
  Versione ZIP : 20
  Compressi    : 61 byte
  Non compressi: 61 byte

aggiungo all'archivio

LEGGIMI.txt
  Commento    : b''
  Modificato  : 2018-07-29 10:23:42
  Sistema      : Unix
  Versione ZIP : 20
  Compressi    : 61 byte
  Non compressi: 61 byte

LEGGIMI2.txt
  Commento    : b''
  Modificato  : 2018-07-29 10:23:42
  Sistema      : Unix
  Versione ZIP : 20
  Compressi    : 61 byte
  Non compressi: 61 byte

Archivi ZIP Python

Python può importare moduli all'interno di un file ZIP usando zipimport, se questi archivi sono in sys.path. La classe PyZipFile può essere usata per costruire un modulo adatto per l'utilizzo con questa modalità. Il metodo supplementare writepy() dice a PyZipFile di cercare file con suffisso .py nella directory e aggiungere i corrispondenti file con estensione .pyo o .pyc all'archivio. Se nessuna delle forme compilate esiste, file creato e aggiunto un file .pyc.

# zipfile_pyzipfile.py

import sys
import zipfile

if __name__ == '__main__':
    with zipfile.PyZipFile('pyzipfile.zip', mode='w') as zf:
        zf.debug = 3
        print('Aggiungo file python')
        zf.writepy('./zipfile')
    for name in zf.namelist():
        print(name)

    print()
    sys.path.insert(0, 'pyzipfile.zip')
    import zipfile_pyzipfile
    print('Importati da:', zipfile_pyzipfile.__file__)

Con l'attributo di debut di PyZipFile impostato a 3, viene abilitato un debug verboso e il risultato viene prodotto mentre compile ogni file .py che trova.

$ python3 zipfile_pyzipfile.py

Aggiungo file python
Adding files from directory ./zipfile
Adding zipfile_append.pyc
Adding zipfile_getinfo.pyc
Adding zipfile_infolist.pyc
Adding zipfile_is_zipfile.pyc
Adding zipfile_namelist.pyc
Adding zipfile_pyzipfile.pyc
Adding zipfile_read.pyc
Adding zipfile_write.pyc
Adding zipfile_write_arcname.pyc
Adding zipfile_write_compression.pyc
Adding zipfile_writestr.pyc
Adding zipfile_writestr_zipinfo.pyc
zipfile_append.pyc
zipfile_getinfo.pyc
zipfile_infolist.pyc
zipfile_is_zipfile.pyc
zipfile_namelist.pyc
zipfile_pyzipfile.pyc
zipfile_read.pyc
zipfile_write.pyc
zipfile_write_arcname.pyc
zipfile_write_compression.pyc
zipfile_writestr.pyc
zipfile_writestr_zipinfo.pyc

Importati da: pyzipfile.zip/zipfile_pyzipfile.pyc

Limitazioni

Il modulo zipfile non supporta file ZIP con commenti aggiunti, o archivi multi disco. Supporta file ZIP più grandi di 4 GB che usano le estensioni ZIP64.

Vedere anche:

zipfile
La documentazione della libreria standard per questo modulo.
zlib
Libreria di compressione ZIP
tarfile
Legge e scrive file tar
zipimport
Importa moduli Python da archivi ZIP