web-dev-qa-db-ja.com

python

次のディレクトリ構造を含むZipファイルがあります。

dir1\dir2\dir3a
dir1\dir2\dir3b

私はそれを解凍してディレクトリ構造を維持しようとしていますが、エラーが発生します:

IOError: [Errno 2] No such file or directory: 'C:\\\projects\\\testFolder\\\subdir\\\unzip.exe'

ここで、testFolderは上記のdir1で、subdirはdir2です。

ファイルを解凍してディレクトリ構造を維持する簡単な方法はありますか?

29
Flyer1

Python 2.6を使用している場合、extractメソッドとextractallメソッドは最適です。ここではPython 2.5を使用する必要があるため、ディレクトリを作成するだけです。存在しない場合は、namelist()メソッドを使用してディレクトリのリストを取得できます。ディレクトリは常にスラッシュで終了します(Windowsでも)。例:

import os, zipfile

z = zipfile.ZipFile('myfile.Zip')
for f in z.namelist():
    if f.endswith('/'):
        os.makedirs(f)

あなたはおそらくそれをしたくないでしょうまさにそのように(つまり、おそらくあなたは名前リストを反復するときにZipファイルの内容を抽出したいと思うでしょう)が、あなたはアイデアを得ます。

23
Jeff

しないでください信頼するextract()またはextractall()。

これらのメソッドは、ファイル名で指定されたパスにファイルを盲目的に抽出します。ただし、Zipファイル名は、「x /../../../ etc/passwd」などの危険な文字列を含め、何でもかまいません。このようなファイルを抽出すると、サーバー全体が危険にさらされる可能性があります。

たぶん、これはPythonのzipfileモジュールの報告可能なセキュリティホールと考える必要がありますが、Zipデアーカイバの数はこれまでとまったく同じです。フォルダ構造のZipファイルを安全にアーカイブ解除するには、各ファイルパスの詳細なチェックが必要です。

16
bobince

試してみて再現できました。他の回答で示唆されているように、extractallメソッドはnotで問題を解決します。これは、zipfileモジュールのバグのようです(おそらくWindowsのみですか?)。ただし、zipfileの構造を誤解している場合を除きます。

_testa\
testa\testb\
testa\testb\test.log
> test.Zip

>>> from zipfile import ZipFile
>>> zipTest = ZipFile("C:\\...\\test.Zip")
>>> zipTest.extractall("C:\\...\\")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "...\zipfile.py", line 940, in extractall
  File "...\zipfile.py", line 928, in extract
  File "...\zipfile.py", line 965, in _extract_member
IOError: [Errno 2] No such file or directory: 'C:\\...\\testa\\testb\\test.log'
_

printdir()を実行すると、次のようになります(最初の列):

_>>> zipTest.printdir()
File Name
testa/testb/
testa/testb/test.log
_

次のように、最初のエントリのみを抽出しようとすると、

_>>> zipTest.extract("testa/testb/")
'C:\\...\\testa\\testb'
_

ディスク上では、これにより、フォルダtestaが作成され、内部にfiletestbが含まれます。これが、_test.log_を抽出する後続の試行が失敗する理由です。 _testa\testb_はフォルダではなくファイルです。

編集#1:ファイルだけを抽出すると、次のように機能します。

_>>> zipTest.extract("testa/testb/test.log")
'C:\\...\\testa\\testb\\test.log'
_

編集#2:Jeffのコードは進むべき道です。 namelistを繰り返します。ディレクトリの場合は、ディレクトリを作成します。それ以外の場合は、ファイルを抽出します。

8
DNS

これを言うのは少し遅れるかもしれませんが、ジェフは正しいです。それは次のように簡単です:

import os
from zipfile import ZipFile as Zip

def extractAll(zipName):
    z = Zip(zipName)
    for f in z.namelist():
        if f.endswith('/'):
            os.makedirs(f)
        else:
            z.extract(f)

if __name__ == '__main__':
    zipList = ['one.Zip', 'two.Zip', 'three.Zip']
    for Zip in zipList:
        extractAll(zipName)
6
ki113d

Python 2.6: extractall メソッドを使用している場合、非常に簡単な方法があります。

ただし、zipfileモジュールはCの拡張機能なしでPythonに完全に実装されているため、2.6インストールからコピーして、古いバージョンのPythonで使用できます。これは、機能を自分で再実装するよりも簡単だと思うかもしれませんが、関数自体は非常に短いです。

def extractall(self, path=None, members=None, pwd=None):
    """Extract all members from the archive to the current working
       directory. `path' specifies a different directory to extract to.
       `members' is optional and must be a subset of the list returned
       by namelist().
    """
    if members is None:
        members = self.namelist()

    for zipinfo in members:
        self.extract(zipinfo, path, pwd)
3
Eli Courtwright

名前リストをフィルターしてフォルダーを除外する

あなたがしなければならないのは、_/_で終わるnamelist()エントリを除外することだけであり、問​​題は解決されています。

_  z.extractall(dest, filter(lambda f: not f.endswith('/'), z.namelist()))
_

nJoy!

2
nickl-

解凍を実行してZipを抽出しようとしているようです。

python zipfile モジュールを使用して、Pythonで抽出することをお勧めします。

import zipfile

def extract(zipfilepath, extractiondir):
    Zip = zipfile.ZipFile(zipfilepath)
    Zip.extractall(path=extractiondir)
2
Douglas Leeder

私のように、古いPythonリリース(私の場合は2.4))で完全なZipアーカイブを抽出する必要があります(Jeffの回答に基づく):

import zipfile
import os

def unzip(source_file_path, destination_dir):
    destination_dir += '/'
    z = zipfile.ZipFile(source_file_path, 'r')
    for file in z.namelist():
        outfile_path = destination_dir + file
        if file.endswith('/'):
            os.makedirs(outfile_path)
        else:
            outfile = open(outfile_path, 'wb')
            outfile.write(z.read(file))
            outfile.close()
    z.close()
2
Apteryx

Zipファイルには、ディレクトリだけでなくファイルのエントリも含めることができます。 Zipコマンドでアーカイブを作成する場合、-Dオプションを渡して、ディレクトリエントリをアーカイブに明示的に追加しないようにします。 Python 2.6のZipFile.extractallメソッドがディレクトリエントリ全体で実行されると、その場所にfileが作成されるようです。アーカイブエントリは必ずしもこれにより、ZipFile.extractallは、ファイルのサブディレクトリにファイルを作成しようとするため、頻繁に失敗します。Pythonモジュール、単にそれを抽出し、-Dオプションを使用して再圧縮します。これを行うためにしばらく使用していた小さなスニペットを次に示します。

P=`pwd` && 
Z=`mktemp -d -t Zip` && 
pushd $Z && 
unzip $P/<busted>.Zip && 
Zip -r -D $P/<new>.Zip . && 
popd && 
rm -rf $Z

<busted>.Zip<new>.Zipを、現在のディレクトリを基準にした実際のファイル名に置き換えます。次に、全体をコピーしてコマンドシェルに貼り付けるだけで、新しいアーカイブが作成され、Python 2.6。ありますis a Zipコマンドは、解凍せずにこれらのディレクトリエントリを削除しますが、IIRCは異なるシェル環境またはZip構成で奇妙な動作をしました。

1
xdissent