Подтвердить что ты не робот

Как читать из zip файла в zip файле в Python?

У меня есть файл, который я хочу прочитать, который сам заархивирован в zip-архиве. Например, parent.zip содержит child.zip, который содержит child.txt. У меня проблемы с чтением child.zip. Кто-нибудь может исправить мой код?

Я предполагаю, что мне нужно создать child.zip как подобный файлу объект и затем открыть его со вторым экземпляром zipfile, но, будучи новичком в python, мой zipfile.ZipFile(zfile.open(name)) глупо. Это поднимает zipfile.BadZipfile: "Файл не является zip файлом" на (независимо проверенном) child.zip

import zipfile
with zipfile.ZipFile("parent.zip", "r") as zfile:
    for name in zfile.namelist():
        if re.search(r'\.zip$', name) is not None:
            # We have a zip within a zip
            with **zipfile.ZipFile(zfile.open(name))** as zfile2:
                    for name2 in zfile2.namelist():
                        # Now we can extract
                        logging.info( "Found internal internal file: " + name2)
                        print "Processing code goes here"
4b9b3361

Ответ 1

Когда вы используете .open() для экземпляра ZipFile вы действительно получаете дескриптор открытого файла. Однако, чтобы прочитать zip файл, классу ZipFile нужно немного больше. Он должен иметь возможность поиска по этому файлу, а объект, возвращаемый .open(), не .open() поиска в вашем случае. Только Python 3 (3.2 и выше) создает объект ZipExFile который поддерживает поиск (при условии, что основной дескриптор файла для внешнего zip файла доступен для поиска, и ничто не пытается записать в объект ZipFile).

Обходной путь - прочитать всю zip-запись в памяти с помощью .read(), сохранить ее в объекте BytesIO (BytesIO для BytesIO файл в памяти) и ZipFile в ZipFile:

from io import BytesIO

# ...
        zfiledata = BytesIO(zfile.read(name))
        with zipfile.ZipFile(zfiledata) as zfile2:

или, в контексте вашего примера:

import zipfile
from io import BytesIO

with zipfile.ZipFile("parent.zip", "r") as zfile:
    for name in zfile.namelist():
        if re.search(r'\.zip$', name) is not None:
            # We have a zip within a zip
            zfiledata = BytesIO(zfile.read(name))
            with zipfile.ZipFile(zfiledata) as zfile2:
                for name2 in zfile2.namelist():
                    # Now we can extract
                    logging.info( "Found internal internal file: " + name2)
                    print "Processing code goes here"

Ответ 2

Чтобы заставить это работать с python33 (под окнами, но это может быть нерелевантно), я должен был сделать:

 import zipfile, re, io
    with zipfile.ZipFile(file, 'r') as zfile:
        for name in zfile.namelist():
            if re.search(r'\.zip$', name) != None:
                zfiledata = io.BytesIO(zfile.read(name))
                with zipfile.ZipFile(zfiledata) as zfile2:
                    for name2 in zfile2.namelist():
                        print(name2)

cStringIO не существует, поэтому я использовал io.BytesIO

Ответ 3

Здесь появилась функция, с которой я пришел. (Скопировано из здесь.)

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

Ответ 4

Что делать, если в zip файле есть еще один zip файл, похожий на "куклу-накопитель", а мы не знаем, сколько в нем?