Question

Comment puis-je créer une archive zip d'une structure de répertoire en Python?

Était-ce utile?

La solution

Comme d'autres l'ont souligné, vous devez utiliser zipfile. La documentation vous indique quelles fonctions sont disponibles, mais n'explique pas vraiment comment vous pouvez les utiliser pour zip un répertoire entier. Je pense qu'il est plus facile d'expliquer avec quelques exemples de code:

#!/usr/bin/env python
import os
import zipfile

def zipdir(path, ziph):
    # ziph is zipfile handle
    for root, dirs, files in os.walk(path):
        for file in files:
            ziph.write(os.path.join(root, file))

if __name__ == '__main__':
    zipf = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)
    zipdir('tmp/', zipf)
    zipf.close()

Adapté de: http://www.devshed.com/c/ a / python / python-décompressés /

Autres conseils

Le plus simple est d'utiliser shutil.make_archive. Il prend en charge les formats zip et de goudron.

import shutil
shutil.make_archive(output_filename, 'zip', dir_name)

Si vous avez besoin de faire quelque chose de plus compliqué que passer comme un éclair tout le répertoire (comme sauter certains fichiers), alors vous aurez besoin de creuser dans la zipfile module comme d'autres l'ont suggéré.

Pour ajouter le contenu de mydirectory à un nouveau fichier zip, y compris tous les fichiers et sous-répertoires:

import os
import zipfile

zf = zipfile.ZipFile("myzipfile.zip", "w")
for dirname, subdirs, files in os.walk("mydirectory"):
    zf.write(dirname)
    for filename in files:
        zf.write(os.path.join(dirname, filename))
zf.close()
  

Comment puis-je créer une archive zip d'une structure de répertoire en Python?

Dans un script Python

En Python 2.7+, shutil a une fonction make_archive.

from shutil import make_archive
make_archive(
  'zipfile_name', 
  'zip',           # the archive format - or tar, bztar, gztar 
  root_dir=None,   # root for archive - current working dir if None
  base_dir=None)   # start archiving from here - cwd if None too

Voici l'archive compressée sera nommé zipfile_name.zip. Si base_dir est plus bas de root_dir il exclut les fichiers pas dans le base_dir, mais toujours archiver les fichiers dans les répertoires parents jusqu'à la root_dir.

J'ai eu un problème sur ce test Cygwin avec 2,7 - il veut un argument root_dir, pour CWD:

make_archive('zipfile_name', 'zip', root_dir='.')

Utilisation Python à partir du shell

Vous pouvez le faire avec Python à partir du shell en utilisant également le module zipfile:

$ python -m zipfile -c zipname sourcedir

zipname est le nom du fichier de destination que vous voulez (ajouter .zip si vous le voulez, il ne le fera pas automatiquement) et SourceDir est le chemin vers le répertoire.

Python remonte la fermeture éclair (ou tout simplement ne veulent pas dir parent):

Si vous essayez de zip un paquet python avec un __init__.py et __main__.py, et vous ne voulez pas le répertoire parent, il est

$ python -m zipfile -c zipname sourcedir/*

$ python zipname

irait à l'emballage. (Notez que vous ne pouvez pas exécuter comme sous-paquets le point d'entrée d'une archive compressée.)

Compresser une application Python:

Si vous avez python3.5 +, et que vous voulez spécifiquement zip un package Python, utilisez zipapp :

$ python -m zipapp myapp
$ python myapp.pyz

Cette fonction récursive zip une arborescence de répertoires, comprimant les fichiers, et l'enregistrement des noms de fichiers relatifs corrects dans l'archive. Les entrées d'archives sont les mêmes que celles générées par zip -r output.zip source_dir.

import os
import zipfile
def make_zipfile(output_filename, source_dir):
    relroot = os.path.abspath(os.path.join(source_dir, os.pardir))
    with zipfile.ZipFile(output_filename, "w", zipfile.ZIP_DEFLATED) as zip:
        for root, dirs, files in os.walk(source_dir):
            # add directory (needed for empty dirs)
            zip.write(root, os.path.relpath(root, relroot))
            for file in files:
                filename = os.path.join(root, file)
                if os.path.isfile(filename): # regular files only
                    arcname = os.path.join(os.path.relpath(root, relroot), file)
                    zip.write(filename, arcname)

Utilisation shutil, qui fait partie du jeu de bibliothèque standard de python. L'utilisation shutil est si simple (voir le code ci-dessous):

  • 1er arg: Nom du fichier zip / tar résultant,
  • 2 arg: zip / tar,
  • 3 arg: dir_name

Code:

import shutil
shutil.make_archive('/home/user/Desktop/Filename','zip','/home/username/Desktop/Directory')

Pour ajouter une compression au fichier zip résultant, consultez ce lien .

Vous devez changer:

zip = zipfile.ZipFile('Python.zip', 'w')

à

zip = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)

Je l'ai apporté quelques modifications au code données par Mark Byers . Ci-dessous fbsql_affected_rows ajoute également des répertoires vides si vous les avez. Les exemples devraient le rendre plus clair ce qui est le chemin ajouté au zip.

#!/usr/bin/env python
import os
import zipfile

def addDirToZip(zipHandle, path, basePath=""):
    """
    Adding directory given by \a path to opened zip file \a zipHandle

    @param basePath path that will be removed from \a path when adding to archive

    Examples:
        # add whole "dir" to "test.zip" (when you open "test.zip" you will see only "dir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir')
        zipHandle.close()

        # add contents of "dir" to "test.zip" (when you open "test.zip" you will see only it's contents)
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir', 'dir')
        zipHandle.close()

        # add contents of "dir/subdir" to "test.zip" (when you open "test.zip" you will see only contents of "subdir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir', 'dir/subdir')
        zipHandle.close()

        # add whole "dir/subdir" to "test.zip" (when you open "test.zip" you will see only "subdir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir', 'dir')
        zipHandle.close()

        # add whole "dir/subdir" with full path to "test.zip" (when you open "test.zip" you will see only "dir" and inside it only "subdir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir')
        zipHandle.close()

        # add whole "dir" and "otherDir" (with full path) to "test.zip" (when you open "test.zip" you will see only "dir" and "otherDir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir')
        addDirToZip(zipHandle, 'otherDir')
        zipHandle.close()
    """
    basePath = basePath.rstrip("\\/") + ""
    basePath = basePath.rstrip("\\/")
    for root, dirs, files in os.walk(path):
        # add dir itself (needed for empty dirs
        zipHandle.write(os.path.join(root, "."))
        # add files
        for file in files:
            filePath = os.path.join(root, file)
            inZipPath = filePath.replace(basePath, "", 1).lstrip("\\/")
            #print filePath + " , " + inZipPath
            zipHandle.write(filePath, inZipPath)

Ci-dessus est une fonction simple qui devrait fonctionner pour les cas simples. Vous pouvez trouver la classe plus élégante dans mon Gist: https://gist.github.com/Eccenux/17526123107ca0ac28e6

Vous voulez probablement regarder le module zipfile; il y a la documentation http://docs.python.org/library/zipfile.html .

Vous pouvez également os.walk() indexer la structure de répertoire.

J'ai un autre exemple de code qui peut aider, en utilisant python3, pathlib et zipfile. Il devrait fonctionner dans tous les OS.

from pathlib import Path
import zipfile
from datetime import datetime

DATE_FORMAT = '%y%m%d'


def date_str():
    """returns the today string year, month, day"""
    return '{}'.format(datetime.now().strftime(DATE_FORMAT))


def zip_name(path):
    """returns the zip filename as string"""
    cur_dir = Path(path).resolve()
    parent_dir = cur_dir.parents[0]
    zip_filename = '{}/{}_{}.zip'.format(parent_dir, cur_dir.name, date_str())
    p_zip = Path(zip_filename)
    n = 1
    while p_zip.exists():
        zip_filename = ('{}/{}_{}_{}.zip'.format(parent_dir, cur_dir.name,
                                             date_str(), n))
        p_zip = Path(zip_filename)
        n += 1
    return zip_filename


def all_files(path):
    """iterator returns all files and folders from path as absolute path string
    """
    for child in Path(path).iterdir():
        yield str(child)
        if child.is_dir():
            for grand_child in all_files(str(child)):
                yield str(Path(grand_child))


def zip_dir(path):
    """generate a zip"""
    zip_filename = zip_name(path)
    zip_file = zipfile.ZipFile(zip_filename, 'w')
    print('create:', zip_filename)
    for file in all_files(path):
        print('adding... ', file)
        zip_file.write(file)
    zip_file.close()


if __name__ == '__main__':
    zip_dir('.')
    print('end!')

Voici une variation sur la réponse donnée par Nux qui fonctionne pour moi:

def WriteDirectoryToZipFile( zipHandle, srcPath, zipLocalPath = "", zipOperation = zipfile.ZIP_DEFLATED ):
    basePath = os.path.split( srcPath )[ 0 ]
    for root, dirs, files in os.walk( srcPath ):
        p = os.path.join( zipLocalPath, root [ ( len( basePath ) + 1 ) : ] )
        # add dir
        zipHandle.write( root, p, zipOperation )
        # add files
        for f in files:
            filePath = os.path.join( root, f )
            fileInZipPath = os.path.join( p, f )
            zipHandle.write( filePath, fileInZipPath, zipOperation )

Essayez le dessous d'un .it a fonctionné pour moi .

import zipfile, os
zipf = "compress.zip"  
def main():
    directory = r"Filepath"
    toZip(directory)
def toZip(directory):
    zippedHelp = zipfile.ZipFile(zipf, "w", compression=zipfile.ZIP_DEFLATED )

    list = os.listdir(directory)
    for file_list in list:
        file_name = os.path.join(directory,file_list)

        if os.path.isfile(file_name):
            print file_name
            zippedHelp.write(file_name)
        else:
            addFolderToZip(zippedHelp,file_list,directory)
            print "---------------Directory Found-----------------------"
    zippedHelp.close()

def addFolderToZip(zippedHelp,folder,directory):
    path=os.path.join(directory,folder)
    print path
    file_list=os.listdir(path)
    for file_name in file_list:
        file_path=os.path.join(path,file_name)
        if os.path.isfile(file_path):
            zippedHelp.write(file_path)
        elif os.path.isdir(file_name):
            print "------------------sub directory found--------------------"
            addFolderToZip(zippedHelp,file_name,path)


if __name__=="__main__":
    main()

Si vous voulez une fonctionnalité comme le dossier de tout compress gestionnaire de fichiers graphique commune, vous pouvez utiliser le code suivant, il utilise le zipfile module. En utilisant ce code, vous aurez le fichier zip avec le chemin que son dossier racine.

import os
import zipfile

def zipdir(path, ziph):
    # Iterate all the directories and files
    for root, dirs, files in os.walk(path):
        # Create a prefix variable with the folder structure inside the path folder. 
        # So if a file is at the path directory will be at the root directory of the zip file
        # so the prefix will be empty. If the file belongs to a containing folder of path folder 
        # then the prefix will be that folder.
        if root.replace(path,'') == '':
                prefix = ''
        else:
                # Keep the folder structure after the path folder, append a '/' at the end 
                # and remome the first character, if it is a '/' in order to have a path like 
                # folder1/folder2/file.txt
                prefix = root.replace(path, '') + '/'
                if (prefix[0] == '/'):
                        prefix = prefix[1:]
        for filename in files:
                actual_file_path = root + '/' + filename
                zipped_file_path = prefix + filename
                zipf.write( actual_file_path, zipped_file_path)


zipf = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)
zipdir('/tmp/justtest/', zipf)
zipf.close()

python moderne (3.6+) en utilisant le module pathlib pour OOP concise manipulation -comme des chemins, et pathlib.Path.rglob() récursive englobement. Pour autant que je peux dire, cela équivaut à George V. La réponse de Reilly: zips avec la compression, l'élément supérieur est un répertoire, garde dirs vides, utilise des chemins relatifs.

from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile

from os import PathLike
from typing import Union


def zip_dir(zip_name: str, source_dir: Union[str, PathLike]):
    src_path = Path(source_dir).expanduser().resolve(strict=True)
    with ZipFile(zip_name, 'w', ZIP_DEFLATED) as zf:
        for file in src_path.rglob('*'):
            zf.write(file, file.relative_to(src_path.parent))

Note: comme des notes de type en option indiquent, zip_name ne peut pas être un objet Path ( serait fixé à 3,6. 2+ ).

Pour donner plus de flexibilité, par exemple Sélectionner le répertoire / fichier par l'utilisation de nom:

import os
import zipfile

def zipall(ob, path, rel=""):
    basename = os.path.basename(path)
    if os.path.isdir(path):
        if rel == "":
            rel = basename
        ob.write(path, os.path.join(rel))
        for root, dirs, files in os.walk(path):
            for d in dirs:
                zipall(ob, os.path.join(root, d), os.path.join(rel, d))
            for f in files:
                ob.write(os.path.join(root, f), os.path.join(rel, f))
            break
    elif os.path.isfile(path):
        ob.write(path, os.path.join(rel, basename))
    else:
        pass

Pour un arbre de fichier:

.
├── dir
│   ├── dir2
│   │   └── file2.txt
│   ├── dir3
│   │   └── file3.txt
│   └── file.txt
├── dir4
│   ├── dir5
│   └── file4.txt
├── listdir.zip
├── main.py
├── root.txt
└── selective.zip

Vous pouvez par exemple sélectionnez uniquement dir4 et root.txt:

cwd = os.getcwd()
files = [os.path.join(cwd, f) for f in ['dir4', 'root.txt']]

with zipfile.ZipFile("selective.zip", "w" ) as myzip:
    for f in files:
        zipall(myzip, f)

Ou tout simplement listdir dans le répertoire d'appel de script et ajouter tout de là:

with zipfile.ZipFile("listdir.zip", "w" ) as myzip:
    for f in os.listdir():
        if f == "listdir.zip":
            # Creating a listdir.zip in the same directory
            # will include listdir.zip inside itself, beware of this
            continue
        zipall(myzip, f)

Dites que vous voulez Zip tous les dossiers (sous-répertoires) dans le répertoire courant.

for root, dirs, files in os.walk("."):
    for sub_dir in dirs:
        zip_you_want = sub_dir+".zip"
        zip_process = zipfile.ZipFile(zip_you_want, "w", zipfile.ZIP_DEFLATED)
        zip_process.write(file_you_want_to_include)
        zip_process.close()

        print("Successfully zipped directory: {sub_dir}".format(sub_dir=sub_dir))

Voici une approche moderne, en utilisant pathlib, et un gestionnaire de contexte. Place les fichiers directement dans le zip, plutôt que dans un sous-dossier.

def zip_dir(filename: str, dir_to_zip: pathlib.Path):
    with zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
        # Use glob instead of iterdir(), to cover all subdirectories.
        for directory in dir_to_zip.glob('**'):
            for file in directory.iterdir():
                if not file.is_file():
                    continue
                # Strip the first component, so we don't create an uneeded subdirectory
                # containing everything.
                zip_path = pathlib.Path(*file.parts[1:])
                # Use a string, since zipfile doesn't support pathlib  directly.
                zipf.write(str(file), str(zip_path))

J'ai préparé une fonction en consolidant la solution de Mark Byers avec commentaires et Morten Reimund pour Zilmer (chemin relatif et y compris les répertoires vides). En tant que meilleure pratique, with est utilisé dans la construction de fichiers ZipFile.

La fonction prépare également un nom de fichier zip par défaut avec le nom du répertoire compressé et extension « .zip ». Par conséquent, il fonctionne avec un seul argument:. Le répertoire source à fermeture éclair

import os
import zipfile

def zip_dir(path_dir, path_file_zip=''):
if not path_file_zip:
    path_file_zip = os.path.join(
        os.path.dirname(path_dir), os.path.basename(path_dir)+'.zip')
with zipfile.ZipFile(path_file_zip, 'wb', zipfile.ZIP_DEFLATED) as zip_file:
    for root, dirs, files in os.walk(path_dir):
        for file_or_dir in files + dirs:
            zip_file.write(
                os.path.join(root, file_or_dir),
                os.path.relpath(os.path.join(root, file_or_dir),
                                os.path.join(path_dir, os.path.pardir)))
# import required python modules
# You have to install zipfile package using pip install

import os,zipfile

# Change the directory where you want your new zip file to be

os.chdir('Type your destination')

# Create a new zipfile ( I called it myfile )

zf = zipfile.ZipFile('myfile.zip','w')

# os.walk gives a directory tree. Access the files using a for loop

for dirnames,folders,files in os.walk('Type your directory'):
    zf.write('Type your Directory')
    for file in files:
        zf.write(os.path.join('Type your directory',file))

Eh bien, après avoir lu les suggestions que je suis venu avec une façon très similaire qui fonctionne avec 2.7.x sans créer des noms de répertoire « drôles » (nom-comme absolu), et ne fera que créer le dossier spécifié dans le zip.

Ou juste au cas où vous avez besoin de votre zip pour contenir un dossier dans le contenu du répertoire sélectionné.

def zipDir( path, ziph ) :
 """
 Inserts directory (path) into zipfile instance (ziph)
 """
 for root, dirs, files in os.walk( path ) :
  for file in files :
   ziph.write( os.path.join( root, file ) , os.path.basename( os.path.normpath( path ) ) + "\\" + file )

def makeZip( pathToFolder ) :
 """
 Creates a zip file with the specified folder
 """
 zipf = zipfile.ZipFile( pathToFolder + 'file.zip', 'w', zipfile.ZIP_DEFLATED )
 zipDir( pathToFolder, zipf )
 zipf.close()
 print( "Zip file saved to: " + pathToFolder)

makeZip( "c:\\path\\to\\folder\\to\\insert\\into\\zipfile" )

Fonction pour créer un fichier zip.

def CREATEZIPFILE(zipname, path):
    #function to create a zip file
    #Parameters: zipname - name of the zip file; path - name of folder/file to be put in zip file

    zipf = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)
    zipf.setpassword(b"password") #if you want to set password to zipfile

    #checks if the path is file or directory
    if os.path.isdir(path):
        for files in os.listdir(path):
            zipf.write(os.path.join(path, files), files)

    elif os.path.isfile(path):
        zipf.write(os.path.join(path), path)
    zipf.close()

Pour une façon concise de conserver la hiérarchie des dossiers dans le répertoire parent à archiver:

import glob
import zipfile

with zipfile.ZipFile(fp_zip, "w", zipfile.ZIP_DEFLATED) as zipf:
    for fp in glob(os.path.join(parent, "**/*")):
        base = os.path.commonpath([parent, fp])
        zipf.write(fp, arcname=fp.replace(base, ""))

Si vous voulez, vous pouvez changer cette option pour utiliser href="https://stackoverflow.com/a/2186565/943773"> pathlib .

Tant de réponses ici, et j'espère que je pourrais contribuer avec ma propre version, qui est basée sur la réponse originale (en passant), mais avec une perspective plus graphique, en utilisant également un contexte pour chaque configuration de zipfile et le tri os.walk(), afin d'avoir une sortie ordonnée.

Avoir ces dossiers et fichiers les (entre autres dossiers), je voulais créer un .zip pour chaque dossier cap_:

$ tree -d
.
├── cap_01
|    ├── 0101000001.json
|    ├── 0101000002.json
|    ├── 0101000003.json
|
├── cap_02
|    ├── 0201000001.json
|    ├── 0201000002.json
|    ├── 0201001003.json
|
├── cap_03
|    ├── 0301000001.json
|    ├── 0301000002.json
|    ├── 0301000003.json
| 
├── docs
|    ├── map.txt
|    ├── main_data.xml
|
├── core_files
     ├── core_master
     ├── core_slave

Voici ce que j'ai appliqué, avec des commentaires pour une meilleure compréhension du processus.

$ cat zip_cap_dirs.py 
""" Zip 'cap_*' directories. """           
import os                                                                       
import zipfile as zf                                                            


for root, dirs, files in sorted(os.walk('.')):                                                                                               
    if 'cap_' in root:                                                          
        print(f"Compressing: {root}")                                           
        # Defining .zip name, according to Capítulo.                            
        cap_dir_zip = '{}.zip'.format(root)                                     
        # Opening zipfile context for current root dir.                         
        with zf.ZipFile(cap_dir_zip, 'w', zf.ZIP_DEFLATED) as new_zip:          
            # Iterating over os.walk list of files for the current root dir.    
            for f in files:                                                     
                # Defining relative path to files from current root dir.        
                f_path = os.path.join(root, f)                                  
                # Writing the file on the .zip file of the context              
                new_zip.write(f_path) 

En fait, pour chaque itération sur os.walk(path), j'ouvre un contexte pour la configuration de zipfile et ensuite, itérer itérer sur files, qui est un list de fichiers à partir du répertoire de root, formant le chemin relatif pour chaque fichier en fonction de la root actuelle répertoire, annexant au contexte zipfile qui est en cours d'exécution.

Et la sortie est présenté comme ceci:

$ python3 zip_cap_dirs.py
Compressing: ./cap_01
Compressing: ./cap_02
Compressing: ./cap_03

Pour voir le contenu de chaque répertoire .zip, vous pouvez utiliser la commande less:

$ less cap_01.zip

Archive:  cap_01.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
  22017  Defl:N     2471  89% 2019-09-05 08:05 7a3b5ec6  cap_01/0101000001.json
  21998  Defl:N     2471  89% 2019-09-05 08:05 155bece7  cap_01/0101000002.json
  23236  Defl:N     2573  89% 2019-09-05 08:05 55fced20  cap_01/0101000003.json
--------          ------- ---                           -------
  67251             7515  89%                            3 files
scroll top