使用python解压缩目录结构

时间:2009-03-12 18:44:46

标签: python unzip

我有一个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。

是否有快速解压缩文件和维护目录结构的方法?

9 个答案:

答案 0 :(得分:23)

如果你使用的是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文件的内容),但是你得到了想法。

答案 1 :(得分:16)

不要信任extract()或extractall()。

这些方法盲目地将文件提取到文件名中给出的路径。但ZIP文件名可以是任何东西,包括危险的字符串,如“x /../../../ etc / passwd”。提取此类文件,您可能只是破坏了整个服务器。

也许这应该被认为是Python的zipfile模块中的一个可报告的安全漏洞,但是过去任何数量的zip-dearchivers都表现出完全相同的行为。要安全地取消归档具有文件夹结构的ZIP文件,您需要深入检查每个文件路径。

答案 2 :(得分:8)

我试过了,可以重现它。如其他答案所示,extractall方法解决问题。这看起来像zipfile模块中的一个错误(也许仅限于Windows?),除非我误解了zipfiles的结构。

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,其中包含文件 testb。这显然是后续尝试提取test.log失败的原因; testa\testb是一个文件,而不是文件夹。

编辑#1:如果你只提取文件,那么它可以工作:

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

编辑#2:杰夫的代码是要走的路;遍历namelist;如果是目录,请创建目录。否则,解压缩文件。

答案 3 :(得分:6)

我知道说这可能有点迟,但杰夫是对的。 它很简单:

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)

答案 4 :(得分:3)

如果您使用的是Python 2.6,那么这是一种非常简单的方法:extractall方法。

但是,由于zipfile模块完全在Python中实现而没有任何C扩展,因此您可以将其从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)

答案 5 :(得分:2)

听起来你正试图解压缩来解压缩。

最好使用python zipfile模块,因此在python中进行提取。

import zipfile

def extract(zipfilepath, extractiondir):
    zip = zipfile.ZipFile(zipfilepath)
    zip.extractall(path=extractiondir)

答案 6 :(得分:2)

过滤名称列表以排除文件夹

您只需过滤掉以namelist()结尾的/条目,问题就解决了:

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

的nJoy!

答案 7 :(得分:2)

如果像我一样,你必须提取一个完整的zip存档与旧的Python版本(在我的情况下,2.4)这里是我想出的(基于杰夫的答案):

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

答案 8 :(得分:1)

请注意,zip文件可以包含目录和文件的条目。使用zip命令创建存档时,请传递-D选项以禁用向存档明确添加目录条目。当Python 2.6的ZipFile.extractall方法在目录条目中运行时,它似乎在其位置创建了文件。由于归档条目不一定按顺序排列,这会导致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替换为相对于当前目录的真实文件名。然后只需复制整个内容并将其粘贴到命令shell中,它将创建一个新的存档,可以随时使用Python 2.6。 一个zip命令,它将删除这些目录条目而不解压缩,但是IIRC在不同的shell环境或zip配置中表现得很奇怪。