2012-08-13 27 views
8

Sto cercando un modo per decomprimere i file zip annidati in python. Ad esempio, si consideri la seguente struttura (nomi ipotetici per la facilità):Unzip file zip annidati in python

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

... ecc. Sto cercando di accedere ai file di testo che si trovano nel secondo zip. Certamente non voglio estrarre tutto, dato che i numeri di taglio farebbero crashare il computer (ci sono diverse centinaia di zip nel primo strato e quasi 10.000 nel secondo strato (per zip)).

Ho giocato con il modulo 'zipfile' - Sono in grado di aprire il primo livello di file zip. Es .:

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

Tuttavia, questo restituisce un'istanza "ZipExtFile" (non è un file o un'istanza file zip) - e non può quindi andare avanti e aprire questo particolare tipo di dati. Che non posso fare questo:

data = next_layer_zip.open(data.txt) 

Posso tuttavia "leggere" questo file file zip con:

next_layer_zip.read() 

Ma questo è del tutto inutile! (cioè può solo leggere dati compressi/goobledigook).

Qualcuno ha qualche idea su come potrei andare su questo (senza utilizzare ZipFile.extract) ??

Mi sono imbattuto in questo, http://pypi.python.org/pypi/zip_open/ - che sembra fare esattamente quello che voglio, ma non sembra funzionare per me. (continua a ricevere "[Errno 2] Nessun file o directory di questo tipo:" per i file che sto cercando di elaborare, usando quel modulo).

Qualsiasi idea sarebbe molto apprezzata !! Grazie in anticipo

risposta

8

Sfortunatamente i file zip di decompressione richiedono un accesso casuale all'archivio e i metodi ZipFile (per non parlare dell'algoritmo DEFLATE stesso) forniscono solo flussi. È quindi impossibile decomprimere i file zip annidati senza estrarli.

5

ZipFile ha bisogno di un oggetto simile a un file, quindi è possibile utilizzare StringIO per trasformare i dati letti dallo zip annidato in tale oggetto. L'avvertenza è che caricherete in memoria la zip interna completa (ancora compressa).

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

Ecco una funzione che ho trovato.

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) 

Ecco come ho provato:

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

per quelli di voi che usano 3.3, per risparmiare tempo 'TypeError: argomento stringa previsto, ottenuto 'byte' relativo alla riga' memory_zip.write (parent_zip.open (child_zip_path) .read()) 'non è sicuro del soluzione – user25064

0

Per chi cerca una funzione che estrae un file zip nidificato (qualsiasi livello di nidificazione) e pulisce i file zip originale:

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)