2012-08-13 31 views
8

Estoy buscando una forma de descomprimir archivos zip anidados en python. Por ejemplo, considere la siguiente estructura (nombres hipotéticos para facilidad):Descomprimir archivos zip anidados en python

  • Folder
    • ZipfileA.zip
      • ZipfileA1.zip
      • ZipfileA2.zip
    • ZipfileB .zip
      • ZipfileB1.zip
      • ZipfileB2.zip

... etc. Estoy intentando acceder a los archivos de texto que están dentro del segundo zip. Ciertamente no quiero extraer todo, ya que los números de cizallamiento bloquearían la computadora (hay varios cientos de cremalleras en la primera capa, y casi 10.000 en la segunda capa (por zip)).

He estado jugando con el módulo 'zipfile' - puedo abrir el primer nivel de archivos zip. Ej .:

zipfile_obj = zipfile.ZipFile("/Folder/ZipfileA.zip") 
next_layer_zip = zipfile_obj.open("ZipfileA1.zip") 

Sin embargo, esto devuelve una instancia de "ZipExtFile" (no un archivo zip o instancia) - y no puedo seguir y luego abrir este tipo de datos en particular. Que no puedo hacer esto:

data = next_layer_zip.open(data.txt) 

puedo sin embargo "leer" este archivo zip con:

next_layer_zip.read() 

Pero esto es totalmente inútil! (es decir, solo puede leer datos comprimidos/goobledigook).

¿Alguien tiene alguna idea sobre cómo podría ir sobre esto (sin usando ZipFile.extract) ??

Me encontré con esto, http://pypi.python.org/pypi/zip_open/ - que parece hacer exactamente lo que quiero, pero parece que no funciona para mí. (sigue obteniendo "[Errno 2] No existe ese archivo o directorio:" para los archivos que estoy tratando de procesar, usando ese módulo).

¡Cualquier idea sería muy apreciada! Gracias de antemano

Respuesta

8

Lamentablemente, la descompresión de archivos zip requiere acceso aleatorio al archivo, y los métodos ZipFile (sin mencionar el algoritmo DEFLATE en sí) solo proporcionan transmisiones. Por lo tanto, es imposible descomprimir archivos zip anidados sin extraerlos.

5

ZipFile necesita un objeto similar a un archivo, por lo que puede utilizar StringIO para convertir los datos leídos del zip anidado en dicho objeto. La advertencia es que va a cargar el zip interno completo (aún comprimido) en la memoria.

with zipfile.ZipFile('foo.zip') as z: 
    with z.open('nested.zip') as z2: 
     z2_filedata = cStringIO.StringIO(z2.read()) 
     with zipfile.ZipFile(z2_filedata) as nested_zip: 
      print nested_zip.open('data.txt').read() 
4

Aquí hay una función que se me ocurrió.

def extract_nested_zipfile(path, parent_zip=None): 
    """Returns a ZipFile specified by path, even if the path contains 
    intermediary ZipFiles. For example, /root/gparent.zip/parent.zip/child.zip 
    will return a ZipFile that represents child.zip 
    """ 

    def extract_inner_zipfile(parent_zip, child_zip_path): 
     """Returns a ZipFile specified by child_zip_path that exists inside 
     parent_zip. 
     """ 
     memory_zip = StringIO() 
     memory_zip.write(parent_zip.open(child_zip_path).read()) 
     return zipfile.ZipFile(memory_zip) 

    if ('.zip' + os.sep) in path: 
     (parent_zip_path, child_zip_path) = os.path.relpath(path).split(
      '.zip' + os.sep, 1) 
     parent_zip_path += '.zip' 

     if not parent_zip: 
      # This is the top-level, so read from disk 
      parent_zip = zipfile.ZipFile(parent_zip_path) 
     else: 
      # We're already in a zip, so pull it out and recurse 
      parent_zip = extract_inner_zipfile(parent_zip, parent_zip_path) 

     return extract_nested_zipfile(child_zip_path, parent_zip) 
    else: 
     if parent_zip: 
      return extract_inner_zipfile(parent_zip, path) 
     else: 
      # If there is no nesting, it's easy! 
      return zipfile.ZipFile(path) 

Así es como he comprobado:

echo hello world > hi.txt 
zip wrap1.zip hi.txt 
zip wrap2.zip wrap1.zip 
zip wrap3.zip wrap2.zip 

print extract_nested_zipfile('/Users/mattfaus/dev/dev-git/wrap1.zip').open('hi.txt').read() 
print extract_nested_zipfile('/Users/mattfaus/dev/dev-git/wrap2.zip/wrap1.zip').open('hi.txt').read() 
print extract_nested_zipfile('/Users/mattfaus/dev/dev-git/wrap3.zip/wrap2.zip/wrap1.zip').open('hi.txt').read() 
+0

para aquellos de ustedes que usan 3.3, para ahorrar algo de tiempo 'TypeError: string argument expected, got 'bytes'' relacionado con la línea' memory_zip.write (parent_zip.open (child_zip_path) .read()) 'no estoy seguro del solución alternativa – user25064

0

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

import zipfile, re, os 

def extract_nested_zip(zippedFile, toFolder): 
    """ Unzip a zip file and its contents, including 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) 
Cuestiones relacionadas