Cómo extraer el archivo zip de forma recursiva en Python

Tengo un archivo zip que contiene tres archivos zip de esta manera:

zipfile.zip\ dirA.zip\ a dirB.zip\ b dirC.zip\ c 

Quiero extraer todos los archivos zip internos que están dentro del archivo zip en directorios con estos nombres (dirA, dirB, dirC).
Básicamente, quiero terminar con el siguiente esquema:

 output\ dirA\ a dirB\ b dirC\ c 

He probado lo siguiente:

 import os, re from zipfile import ZipFile os.makedirs(directory) # where directory is "\output" with ZipFile(self.archive_name, "r") as archive: for id, files in data.items(): if files: print("Creating", id) dirpath = os.path.join(directory, id) os.mkdir(dirpath) for file in files: match = pattern.match(filename) new = match.group(2) new_filename = os.path.join(dirpath, new) content = archive.open(file).read() with open(new_filename, "wb") as outfile: outfile.write(content) 

Pero solo extrae el archivo zip y termino con:

 output\ dirA\ dirA.zip dirB\ dirB.zip dirC\ dirC.zip 

Cualquier sugerencia que incluya segmentos de código será muy apreciada porque he probado muchas cosas diferentes y he leído la documentación sin éxito.

Al extraer el archivo zip, desearía escribir los archivos zip internos en la memoria en lugar de ellos en el disco. Para hacer esto, he usado BytesIO .

Echa un vistazo a este código:

 import os import io import zipfile def extract(filename): z = zipfile.ZipFile(filename) for f in z.namelist(): # get directory name from file dirname = os.path.splitext(f)[0] # create new directory os.mkdir(dirname) # read inner zip file into bytes buffer content = io.BytesIO(z.read(f)) zip_file = zipfile.ZipFile(content) for i in zip_file.namelist(): zip_file.extract(i, dirname) 

Si ejecuta el extract("zipfile.zip") con zipfile.zip como:

 zipfile.zip/ dirA.zip/ a dirB.zip/ b dirC.zip/ c 

La salida debe ser:

 dirA/ a dirB/ b dirC/ c 

Para una función que extrae un archivo zip nested (cualquier nivel de anidamiento) y limpia los archivos zip originales:

 import zipfile, re, os def extract_nested_zip(zippedFile, toFolder): """ Extract a zip file including any nested zip files Delete the zip file(s) after extraction """ with zipfile.ZipFile(zippedFile, 'r') as zfile: zfile.extractall(path=toFolder) os.remove(zippedFile) for root, dirs, files in os.walk(toFolder): for filename in files: if re.search(r'\.zip$', filename): fileSpec = os.path.join(root, filename) extract_nested_zip(fileSpec, root) 

Probé algunas de las otras soluciones pero no pude hacer que funcionaran “en su lugar”. Publicaré mi solución para manejar la versión “en el lugar”. Nota: elimina los archivos zip y los ‘reemplaza’ con directorios con nombres idénticos , así que haga una copia de seguridad de sus archivos zip si desea mantenerlos.

La estrategia es simple. Descomprima todos los archivos zip en el directorio (y subdirectorios) y enjuague y repita hasta que no quede ningún archivo zip. El enjuague y la repetición son necesarios si los archivos zip contienen archivos zip.

 import os import io import zipfile import re def unzip_directory(directory): """" This function unzips (and then deletes) all zip files in a directory """ for root, dirs, files in os.walk(directory): for filename in files: if re.search(r'\.zip$', filename): to_path = os.path.join(root, filename.split('.zip')[0]) zipped_file = os.path.join(root, filename) if not os.path.exists(to_path): os.makedirs(to_path) with zipfile.ZipFile(zipped_file, 'r') as zfile: zfile.extractall(path=to_path) # deletes zip file os.remove(zipped_file) def exists_zip(directory): """ This function returns T/F whether any .zip file exists within the directory, recursively """ is_zip = False for root, dirs, files in os.walk(directory): for filename in files: if re.search(r'\.zip$', filename): is_zip = True return is_zip def unzip_directory_recursively(directory, max_iter=1000): print("Does the directory path exist? ", os.path.exists(directory)) """ Calls unzip_directory until all contained zip files (and new ones from previous calls) are unzipped """ iterate = 0 while exists_zip(directory) and iterate < max_iter: unzip_directory(directory) iterate += 1 pre = "Did not " if iterate < max_iter else "Did" print(pre, "time out based on max_iter limit of", max_iter, ". Took iterations:", iterate) 

Suponiendo que se haga una copia de seguridad de sus archivos zip, puede hacer que todo esto funcione llamando a unzip_directory_recursively(your_directory) .