web-dev-qa-db-ja.com

PythonのZipファイル内のZipファイルから読み取る方法は?

読み取りたいファイルがあり、それ自体がZipアーカイブ内に圧縮されています。たとえば、parent.Zipには、child.txtを含むchild.Zipが含まれています。子供を読むのに問題があります。誰かが私のコードを修正できますか?

Child.Zipをファイルのようなオブジェクトとして作成し、zipfileの2番目のインスタンスで開く必要があると想定していますが、python my zipfile.ZipFile(zfile.open( name))はばかげています。それはzipfile.BadZipfileを発生させます:(独立して検証された)child.Zipで「ファイルは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"
34

ZipFileインスタンスで.open()呼び出しを使用すると、実際に開いているファイルハンドルが取得されます。ただし、read Zipファイルにするには、ZipFileクラスにもう少し必要があります。そのファイルに対してseekが可能である必要があり、.open()によって返されたオブジェクトはあなたの場合シークできません。 Python 3(3.2以降)のみが、シークをサポートするZipExFileオブジェクトを生成します(外側のZipファイルの基になるファイルハンドルがシーク可能であり、何も書き込もうとしていない場合) ZipFileオブジェクト)。

回避策は、.read()を使用してZipエントリ全体をメモリに読み取り、BytesIOオブジェクト(isシーク可能なメモリ内ファイル)に格納してフィードすることです。それを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"
46
Martijn Pieters

これをpython33で動作させるには(Windowsの下では問題にならないかもしれません)私はしなければなりませんでした:

 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を使用しました

11
zlr

これが私が思いついた機能です。 ( here からコピー)

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()
1
Matt Faus