Skip to content

Zip et dézipage de fichiers avec zipfile et shutil en Python

En Python, vous pouvez compresser et décompresser des fichiers, c’est-à-dire compresser des fichiers dans un fichier ZIP et extraire un fichier ZIP avec le module zipfile.

De plus, vous pouvez facilement compresser un répertoire (dossier) et décompresser un fichier ZIP avec make_archive() et unpack_archive() du module shutdown.

Les deux sont inclus dans la bibliothèque standard, aucune installation supplémentaire n’est donc requise.

Cet article décrit le contenu suivant.

  • Compressez un répertoire (dossier) :shutil.make_archive()
  • Décompressez un fichier :shutil.unpack_archive()
  • Bases du module zipfile :ZipFile objects
  • Compresser des fichiers individuels dans un fichier ZIP
  • Ajouter d’autres fichiers à un fichier ZIP existant
  • Vérifier la liste des fichiers dans un fichier ZIP
  • Extraire des fichiers individuels d’un fichier ZIP
  • Lire des fichiers dans un fichier ZIP
  • ZIP avec mots de passe (chiffrement et déchiffrement)

Compressez un répertoire (dossier) :shutil.make_archive()

Vous pouvez compresser un répertoire (dossier), c’est-à-dire créer un fichier ZIP à partir d’un répertoire avec shutdown.make_archive().

Le premier paramètre nom_base est le chemin sans extension du fichier ZIP à créer, le deuxième paramètre format est le format de l’archive (‘zip’, ‘tar’, ‘gztar’, ‘bztar’, ‘xztar’), et le troisième paramètre root_dir est le chemin du répertoire à compresser.

Par exemple, supposons qu’il existe un répertoire dir_zip avec la structure suivante dans le répertoire actuel.

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

Compressez ce répertoire dans un fichier ZIP archive_shutil.zip dans le répertoire courant.

import shutil

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

Dans ce cas, le répertoire spécifié dir_zip lui-même n’est pas inclus dans archive_shutil.zip.

Si vous souhaitez inclure le répertoire lui-même, spécifiez le chemin du répertoire supérieur du répertoire cible dans le troisième paramètre root_dir et le chemin relatif du répertoire cible depuis root_dir dans le quatrième paramètre base_dir.

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

Voir la section suivante pour le résultat de la décompression.

Décompressez un fichier :shutil.unpack_archive()

Vous pouvez décompresser un fichier, c’est-à-dire extraire tout le contenu d’un fichier ZIP avec shutdown.unpack_archive().

Le premier paramètre filename est le chemin du fichier ZIP et le deuxième paramètre extract_dir est le chemin du répertoire cible où l’archive est extraite.

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

Il est extrait comme suit :

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

Bien que la documentation ne le précise pas, il semble créer un nouveau répertoire même si extract_dir n’existe pas (confirmé dans Python 3.9.9).

Le fichier ZIP créé par shutdown.make_archive() avec base_dir est extrait comme suit :

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

Bases du module zipfile :ZipFile objects

Le module zipfile fournit la classe ZipFile pour créer, lire, écrire, ajouter et répertorier un fichier ZIP.

Les objets ZipFile sont créés en spécifiant le premier fichier de paramètre (chemin d’un fichier ZIP) et le second mode de paramètre (lire ‘r’, écrire ‘w’, ajouter ‘a’, etc.) au constructeur zipfile.ZipFile().

L’objet ZipFile doit être fermé avec la méthode close(), mais si vous utilisez l’instruction with, il est automatiquement fermé lorsque le bloc est terminé.

L’utilisation est similaire à la lecture et à l’écriture de fichiers avec la fonction intégrée open(), comme la spécification du mode et l’utilisation de l’instruction with.

Des exemples spécifiques sont décrits dans les sections suivantes.

Compresser des fichiers individuels dans un fichier ZIP

Pour compresser des fichiers individuels dans un fichier ZIP, créez un nouvel objet ZipFile et ajoutez les fichiers que vous souhaitez compresser avec la méthode write().

Avec zipfile.ZipFile(), spécifiez le chemin d’un fichier ZIP nouvellement créé comme premier fichier de paramètres, et définissez le deuxième mode de paramètre sur ‘w’ (écriture).

En mode écriture, vous pouvez également spécifier la méthode et le niveau de compression avec les paramètres compression et compresslevel.

La compression de la méthode de compression est la suivante ; BZIP2 et LZMA ont un taux de compression plus élevé, mais la compression prend plus de temps.

  • zipfile.ZIP_STORED : aucune compression (par défaut)
  • zipfile.ZIP_DEFLATED : Compression ZIP habituelle
  • zipfile.ZIP_BZIP2 : compression BZIP2
  • fichier zip.ZIP_LZMA : compression LZMA

Pour ZIP_DEFLATED, le niveau de compression compresslevel correspond au niveau de zlib.compressobj(). La valeur par défaut est -1 (Z_DEFAULT_COMPRESSION).

level est le niveau de compression – un entier de 0 à 9 ou -1. Une valeur de 1 (Z_BEST_SPEED) est la plus rapide et produit le moins de compression, tandis qu’une valeur de 9 (Z_BEST_COMPRESSION) est la plus lente et produit le plus. 0 (Z_NO_COMPRESSION) correspond à aucune compression. La valeur par défaut est -1 (Z_DEFAULT_COMPRESSION). Z_DEFAULT_COMPRESSION représente un compromis par défaut entre vitesse et compression (actuellement équivalent au niveau 6).
zlib.compressobj() — Compression compatible avec gzip — Documentation Python 3.10.2

La méthode write() de l’objet ZipFile écrit le fichier nommé le premier paramètre filename dans un fichier ZIP, en lui donnant le nom de l’archive (= nom dans ZIP) second paramètre arcname. Si arcname est omis, filename est utilisé comme nom d’archive. Vous pouvez spécifier une structure de répertoires pour 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')

Vous pouvez également sélectionner une méthode et un niveau de compression pour chaque fichier en spécifiant compress_type et compresslevel dans la méthode write().

Ajouter d’autres fichiers à un fichier ZIP existant

Pour ajouter d’autres fichiers à un fichier ZIP existant, avec zipfile.ZipFile(), définissez le premier fichier de paramètres sur le chemin du fichier ZIP existant et le second mode de paramètre sur ‘a’ (ajouter).

Ajouter des fichiers existants

Vous pouvez ajouter des fichiers existants avec la méthode write() de l’objet ZipFile.

Voici un exemple d’ajout d’un autre_fichier.txt dans le répertoire courant. L’argument arcname est omis.

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

Créer et ajouter un nouveau fichier

Vous pouvez également créer un nouveau fichier et l’ajouter. Utilisez la méthode open() de l’objet ZipFile avec le mode d’ajout (‘a’).

Spécifiez le chemin du fichier nouvellement créé dans ZIP comme premier paramètre et définissez le mode du deuxième paramètre sur « w ».

Vous pouvez écrire le contenu avec la méthode write() de l’objet fichier ouvert.

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’argument de write() doit être spécifié en tant qu’octets et non en tant que chaîne. Pour écrire un texte, utilisez b’…’, ou convertissez-le avec la méthode encode() de str.

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

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

Un exemple de lecture d’un fichier au format ZIP avec open() de l’objet ZipFile est décrit plus loin.

Vérifier la liste des fichiers dans un fichier ZIP

Pour vérifier le contenu d’un fichier ZIP existant, créez un objet ZipFile avec le premier fichier de paramètres comme chemin du fichier ZIP existant et le second mode de paramètre comme ‘r’ (lecture). mode peut être omis puisque la valeur par défaut est ‘r’.

Vous pouvez obtenir une liste des éléments archivés avec la méthode namelist() de l’objet 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']

Comme vous pouvez le voir dans le résultat ci-dessus, les ZIP créés avec shutdown.make_archive() répertorient également les répertoires individuellement. Il en était de même pour les ZIP compressés avec la fonction standard du Finder sur Mac.

Vous pouvez exclure des répertoires avec des compréhensions de liste.

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']

Pour décompresser un fichier ZIP, créez un objet ZipFile en mode lecture (‘r’, par défaut).

Si vous souhaitez extraire uniquement des fichiers spécifiques, utilisez la méthode extract().

Le premier membre de paramètre est le nom du fichier à extraire (y compris le répertoire dans le fichier zip), et le deuxième chemin de paramètre est le chemin d’accès au répertoire vers lequel extraire.

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')

Si vous souhaitez extraire tous les fichiers, utilisez la méthode extractall(). Spécifiez le chemin du répertoire vers lequel effectuer l’extraction comme premier chemin d’argument.

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

Dans les deux cas, si path est omis, les fichiers sont extraits dans le répertoire courant. Bien que la documentation ne le précise pas, il semble créer un nouveau répertoire même si le chemin est inexistant (confirmé dans Python 3.9.9).

Lire des fichiers dans un fichier ZIP

Vous pouvez directement lire les fichiers dans un fichier ZIP.

Créez un objet ZipFile en mode lecture (par défaut) et ouvrez le fichier à l’intérieur avec la méthode open().

Le premier argument de open() est le nom d’un fichier dans le ZIP (il peut inclure le répertoire). Le deuxième mode d’argument peut être omis puisque la valeur par défaut est ‘r’ (lecture).

Le contenu peut être lu avec la méthode read() de l’objet fichier ouvert. Une chaîne d’octets bytes est renvoyée, qui peut être convertie en une chaîne str avec la méthode 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'>

En plus de read(), readline() et readlines() peuvent être utilisés ainsi que l’objet fichier ouvert avec la fonction intégrée open().

ZIP avec mots de passe (chiffrement et déchiffrement)

Le module zipfile peut décrypter les ZIP avec des mots de passe (ZIP cryptés), mais il ne peut pas crypter les ZIP.

Il prend en charge le décryptage des fichiers cryptés dans les archives ZIP, mais il ne peut actuellement pas créer de fichier crypté. Le déchiffrement est extrêmement lent car il est implémenté en Python natif plutôt qu’en C.
zipfile — Travailler avec des archives ZIP — Documentation Python 3.10.2

De plus, AES n’est pas pris en charge.

Le module zipfile de la bibliothèque standard Python ne prend en charge que les fichiers zip cryptés CRC32 (voir ici : http://hg.python.org/cpython/file/71adf21421d9/Lib/zipfile.py#l420 ).
zip – Python décompresser le fichier crypté AES-128

Ni make_archive() ni unpack_archive() ne prennent en charge le chiffrement et le déchiffrement.

pyzipper

Le pyzipper introduit dans Stack Overflow ci-dessus prend en charge le cryptage et le décryptage AES, et peut être utilisé de la même manière que zipfile.

Pour créer un fichier ZIP avec un mot de passe, spécifiez encryption=pyzipper.WZ_AES avec pyzipper.AESZipFile() et définissez le mot de passe avec la méthode setpassword(). Notez que vous devez spécifier le mot de passe avec la chaîne d’octets bytes.

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')

Voici un exemple de décompression d’un fichier ZIP avec un mot de passe.

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

Bien sûr, si le mot de passe est erroné, il ne peut pas être déchiffré.

# 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'

Le module zipfile vous permet également de spécifier un mot de passe, mais comme mentionné ci-dessus, il ne prend pas en charge 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

Exécuter la commande avec subprocess.run()

Vous pouvez également utiliser subprocess.run() si zipfile ou pyzipper ne fonctionne pas, mais la commande peut quand même le gérer.

Utilisez la commande 7z de 7-zip (installation requise) comme exemple.

import subprocess

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

Équivalent aux commandes suivantes. -x est l’expansion. Notez que -p et -o ne nécessitent pas d’espaces.

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