Распаковка структуры каталогов с помощью Python - PullRequest
29 голосов
/ 12 марта 2009

У меня есть 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 ]

23 голосов
/ 12 марта 2009

Методы extract и extractall хороши, если вы используете Python 2.6. Сейчас я должен использовать 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-файла при переборе списка имен), но вы поняли .

16 голосов
/ 13 марта 2009

Не доверяйте extract () или extractall ().

Эти методы слепо извлекают файлы по путям, указанным в их именах файлов. Но имена файлов ZIP могут быть любыми, включая опасные строки, такие как «x /../../../ etc / passwd». Извлеките такие файлы, и вы могли бы просто поставить под угрозу весь ваш сервер.

Возможно, это следует рассматривать как отчетную дыру в безопасности в модуле zipfile в Python, но любое количество архиваторов zip демонстрировало точно такое же поведение в прошлом. Чтобы безопасно разархивировать ZIP-файл со структурой папок, необходимо тщательно проверить каждый путь к файлу.

8 голосов
/ 12 марта 2009

Я попробовал это, и могу воспроизвести это. Метод извлечения всех, как предлагают другие ответы, не решает проблему. Мне кажется, что это ошибка в модуле zipfile (возможно, только для Windows?), Если только я не понимаю, как структурированы zip-файлы.

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; если это каталог, создайте каталог. В противном случае распакуйте файл.

6 голосов
/ 25 июня 2011

Я знаю, что может быть немного поздно сказать это, но Джефф прав. Это так просто, как:

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)
3 голосов
/ 12 марта 2009

Существует очень простой способ использования 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)
2 голосов
/ 19 марта 2015

Если, как и я, вам нужно извлечь полный 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()
2 голосов
/ 26 августа 2012

Фильтр списка имен для исключения папок

Все, что вам нужно сделать, это отфильтровать записи namelist(), заканчивающиеся на /, и проблема будет решена:

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

NJoy!

2 голосов
/ 12 марта 2009

Звучит так, будто вы пытаетесь запустить unzip, чтобы извлечь zip.

Было бы лучше использовать модуль python zipfile и, следовательно, выполнять извлечение в python.

import zipfile

def extract(zipfilepath, extractiondir):
    zip = zipfile.ZipFile(zipfilepath)
    zip.extractall(path=extractiondir)
1 голос
/ 27 февраля 2010

Обратите внимание, что 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 реальными именами файлов относительно текущего каталога. Затем просто скопируйте все это и вставьте в командную оболочку, и он создаст новый архив, готовый к работе с Python 2.6. - это команда zip, которая удаляет эти записи каталога без разархивирования, но IIRC странно себя ведет в разных средах оболочки или в конфигурациях zip.

...