Pregunta

¿Cómo puedo crear un archivo zip de una estructura de directorios en Python?

¿Fue útil?

Solución

Como otros han señalado, se debe utilizar archivo zip . La documentación le indica qué funciones están disponibles, pero en realidad no explica cómo se puede utilizar para comprimir un directorio completo. Creo que es más fácil de explicar con un código de ejemplo:

#!/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()

Adaptado de: http://www.devshed.com/c/ a / Python / Python-descomprimido /

Otros consejos

La forma más sencilla es utilizar shutil.make_archive . Es compatible con los formatos zip y alquitrán.

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

Si necesita hacer algo más complicado que comprimir todo el directorio (por ejemplo, saltar ciertos archivos), entonces usted tendrá que cavar en el módulo zipfile como han sugerido otros.

Para añadir el contenido de mydirectory a un nuevo archivo zip, incluyendo todos los archivos y subdirectorios:

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

¿Cómo crear un archivo zip de una estructura de directorios en Python?

En un script Python

En Python 2.7+, shutil tiene una función 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

A continuación, el archivo comprimido se llamará zipfile_name.zip. Si base_dir está más abajo de root_dir se excluye archivos no en el base_dir, pero todavía archivar los archivos en los directorios de los padres hasta la root_dir.

Yo tenía un problema en esta prueba con Cygwin 2.7 - que quiere un argumento ROOT_DIR, de la caquexia crónica:

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

Uso de Python de la cáscara

Usted puede hacer esto con el pitón de la cáscara también se utiliza el módulo zipfile:

$ python -m zipfile -c zipname sourcedir

Cuando zipname es el nombre del archivo de destino que desea (añadir .zip si lo desea, no lo hará automáticamente) y sourcedir es la ruta de acceso al directorio.

cerrando la cremallera de Python (o simplemente no quieren directorio padre):

Si usted está tratando de cerrar la cremallera de un paquete con un pitón __init__.py y __main__.py, y usted no quiere que el directorio padre, es

$ python -m zipfile -c zipname sourcedir/*

y

$ python zipname

se ejecute el paquete. (Tenga en cuenta que no se puede ejecutar sub-paquetes como el punto de entrada de un archivo comprimido.)

Comprimir una aplicación Python:

Si usted tiene python3.5 +, y, específicamente, quiere cerrar la cremallera de un paquete de Python, utilice zipapp :

$ python -m zipapp myapp
$ python myapp.pyz

Esta función recursiva cerrar la cremallera de un árbol de directorios, compresión los archivos, y el registro de los nombres de archivo relativas correctas en el archivo. Las entradas del archivo son los mismos que los generados por 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)

Uso shutil, que forma parte del conjunto de la biblioteca estándar de Python. Usando shutil es tan simple (ver código de abajo):

  • primero arg: Nombre del archivo zip / tar resultante,
  • 2º argumento: zip / tar,
  • tercera arg: nombre_dir

Código:

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

Para añadir compresión para el archivo zip resultante, echa un vistazo a este enlace .

Es necesario cambiar:

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

a

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

He hecho algunos cambios en el código dados por Mark Byers . A continuación se añade también la función de los directorios vacíos, si los tiene. Los ejemplos deben hacer más claro cuál es el camino añadido a la cremallera.

#!/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)

Por encima es una función simple que debe trabajar para casos sencillos. Puede encontrar la clase más elegante en mi Síntesis: https://gist.github.com/Eccenux/17526123107ca0ac28e6

Es probable que desea buscar en el módulo zipfile; no hay documentación en http://docs.python.org/library/zipfile.html .

También puede os.walk() al índice de la estructura de directorios.

Tengo otro ejemplo de código que puede ayudar, utilizando python3, pathlib y archivo zip. Debería funcionar en cualquier sistema operativo.

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

Esta es una variación de la respuesta dada por Nux que funciona para mí:

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 )

Pruebe el siguiente .it uno trabajó para mí .

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 desea una funcionalidad como la carpeta de compresión de cualquier administrador de archivos gráfico común que puede utilizar el código siguiente, se utiliza el archivo zip módulo. El uso de este código que tendrá el archivo zip con la ruta que su carpeta raíz.

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

Modern Python (3.6+) usando el módulo pathlib para OOP concisa -como el manejo de caminos, y pathlib.Path.rglob() para recursiva comodines. Por lo que yo puedo decir, esto es equivalente a la respuesta de George V. Reilly: cremalleras con la compresión, el elemento superior es un directorio, mantiene directorios vacíos, utiliza rutas relativas.

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

Nota: como pistas de tipo opcional indican, zip_name no puede ser un objeto Path ( se fija en 3,6. 2+ ).

Para dar más flexibilidad, por ejemplo, Seleccionar directorio / archivo mediante el uso nombre:

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

Para un árbol de archivos:

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

Se puede, por ejemplo, seleccionar sólo dir4 y 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)

O simplemente listdir en el directorio invocación guión y añadir todo desde allí:

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)

Supongamos que desea comprimir todas las carpetas (subdirectorios) en el directorio actual.

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

Esto es un enfoque moderno, utilizando pathlib, y un gestor de contexto. Pone los archivos directamente en el zip, en lugar de en una subcarpeta.

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

he preparado una función mediante la consolidación de solución de Mark Byers' con comentarios Reimund y Morten Zilmer de (ruta relativa e incluyendo los directorios vacíos). Como práctica recomendada, with se utiliza en la construcción de archivos de ZipFile.

La función también prepara un nombre de archivo zip defecto con el nombre de directorio con cremallera y extensión '.zip'. Por lo tanto, funciona sólo con un argumento:. El directorio de origen para ser comprimido

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

Bueno, después de leer las sugerencias que se me ocurrió una manera muy similar que funciona con 2.7.x sin crear nombres de directorio "gracioso" (nombres absoluta-like), y sólo va a crear la carpeta especificada en el interior de la cremallera.

O en caso de que necesita su código postal para contener una carpeta dentro de los contenidos del directorio seleccionado.

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

Función para crear archivo 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()

Para una manera concisa para conservar la jerarquía de carpetas bajo el directorio raíz para ser archivado:

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 lo desea, puede cambiarlo por el uso pathlib de archivo globbing .

Así que muchas respuestas aquí, y espero que podría contribuir con mi propia versión, que se basa en la respuesta original (por cierto), pero con una perspectiva más gráfica, también usando el contexto para cada configuración zipfile y clasificación os.walk(), con el fin de tener una salida ordenada.

Tener estas carpetas y los archivos (entre otras carpetas), que quería crear una carpeta para cada .zip 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

Esto es lo que he aplicado, con comentarios para una mejor comprensión del proceso.

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

Básicamente, para cada iteración sobre os.walk(path), estoy abriendo un contexto para la configuración zipfile y después, la iteración iteración sobre files, que es un list de archivos de directorio root, formando la ruta relativa para cada archivo basado en el actual root directorio, añadiendo al contexto zipfile que se está ejecutando.

Y la salida se presenta como sigue:

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

Para ver el contenido de cada directorio .zip, puede utilizar el comando 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top