Распаковка директории с помощью 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.
Есть ли быстрый способ распаковать файл и сохранить структуру каталогов?
Ответы
Ответ 1
Экстра и экстрактивные методы великолепны, если вы находитесь на 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 файла, когда будете перебирать элемент списка), но вы получите эту идею.
Ответ 2
Не доверять извлечение() или extractall().
Эти методы слепо извлекают файлы на пути, указанные в именах файлов. Но имена файлов ZIP могут быть вообще любыми, включая опасные строки типа "x/../../../etc/passwd". Извлеките такие файлы, и вы могли бы просто скомпрометировать весь ваш сервер.
Возможно, это следует рассматривать как отчетное отверстие безопасности в модуле zipfile Python, но любое количество zip-dearchivers демонстрирует то же самое поведение в прошлом. Чтобы безопасно распаковать ZIP файл со структурой папок, вам нужна углубленная проверка каждого пути к файлу.
Ответ 3
Я попробовал это и могу воспроизвести его. Выделенный метод, как было предложено другими ответами, решает проблему не. Это похоже на ошибку в модуле 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
; если это каталог, создайте каталог. В противном случае извлеките файл.
Ответ 4
Я знаю, что может быть немного поздно это сказать, но Джефф прав.
Это так же просто, как:
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)
Ответ 5
Там очень простой способ, если вы используете 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)
Ответ 6
Похоже, вы пытаетесь запустить unzip для извлечения zip.
Было бы лучше использовать модуль python zipfile
и, следовательно, сделать извлечение в python.
import zipfile
def extract(zipfilepath, extractiondir):
zip = zipfile.ZipFile(zipfilepath)
zip.extractall(path=extractiondir)
Ответ 7
Фильтровать список элементов для исключения папок
Все, что вам нужно сделать, это отфильтровать записи namelist()
, заканчивающиеся на /
, и проблема решена:
z.extractall(dest, filter(lambda f: not f.endswith('/'), z.namelist()))
NJoy!
Ответ 8
Если мне нравится, вам нужно извлечь полный 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()
Ответ 9
Обратите внимание, что 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-конфигурациях.