2012-08-13 2 views
8

Я ищу способ распаковать вложенные zip-файлы в python. Например, рассмотрим следующую структуру (гипотетические имена для простоты):Unzip вложенные файлы zip в python

  • Папка
    • ZipfileA.zip
      • ZipfileA1.zip
      • ZipfileA2.zip
    • ZipfileB .zip
      • ZipfileB1.zip
      • ZipfileB2.zip

... и т.д.. Я пытаюсь получить доступ к текстовым файлам, которые находятся во втором zip-файле. Я, конечно, не хочу извлекать все, так как числа сдвига будут разбивать компьютер (в первом слое есть несколько сотен застежек, а во втором - почти 10 000).

Я играл с модулем zipfile - я могу открыть 1-й уровень zip-файлов. Например .:

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

Однако это возвращает «ZipExtFile» экземпляр (а не файл или ZipFile экземпляра) - и я не могу потом пойти и открыть этот конкретный тип данных. То, что я не могу это сделать:

data = next_layer_zip.open(data.txt) 

Я, однако, может «читать» этот файл архива с:

next_layer_zip.read() 

Но это совершенно бесполезно! (т. е. может считывать только сжатые данные/goobledigook).

Кто-нибудь есть какие-либо идеи о том, как я мог бы идти об этом (без с использованием ZipFile.extract) ??

Я натолкнулся на это, http://pypi.python.org/pypi/zip_open/ - который выглядит так, как я хочу, но он, похоже, не работает для меня. (продолжайте получать "[Errno 2] Нет такого файла или каталога:" для файлов, которые я пытаюсь обработать, используя этот модуль).

Любые идеи были бы высоко оценены! Заранее спасибо

ответ

8

К сожалению, для распаковки zip-файлов требуется произвольный доступ к архиву, а методы ZipFile (не говоря уже о самом алгоритме DEFLATE) предоставляют только потоки. Поэтому невозможно распаковать вложенные файлы zip без их извлечения.

5

ZipFile необходим файл-подобный объект, поэтому вы можете использовать StringIO для преобразования данных, которые вы читаете из вложенного почтового индекса, в такой объект. Предостережение заключается в том, что вы будете загружать полный (все еще сжатый) внутренний почтовый индекс в память.

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

Вот функция, с которой я пришел.

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) 

Вот как я тестировал:

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

для тех из вас, кто использует 3.3, чтобы сэкономить вам некоторое время 'TypeError: ожидаемый строковый аргумент, полученный 'bytes'', связанный с линией' memory_zip.write (parent_zip.open (child_zip_path) .read()) 'не уверен в обходной путь – user25064

0

Для тех, кто ищет функцию, которая извлекает вложенный файл почтового индекса (любого уровня вложенности) и очищает исходные файлы почтового индекса:

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) 
Смежные вопросы