Как создать zip-архив каталога?

Как я могу создать zip-архив структуры каталогов в Python?


person Martha Yi    schedule 06.12.2009    source источник
comment
Не используйте решение, предложенное в принятом ответе, а используйте тот, который ниже, используя make_archive из shutil (если вы хотите рекурсивно заархивировать один каталог).   -  person malana    schedule 09.10.2018


Ответы (26)


Как указывали другие, вы должны использовать zipfile. В документации рассказывается, какие функции доступны, но не объясняется, как их можно использовать для заархивирования всего каталога. Я думаю, что проще всего объяснить пример кода:

import os
import zipfile
    
def zipdir(path, ziph):
    # ziph is zipfile handle
    for root, dirs, files in os.walk(path):
        for file in files:
            ziph.write(os.path.join(root, file), 
                       os.path.relpath(os.path.join(root, file), 
                                       os.path.join(path, '..')))
      
zipf = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)
zipdir('tmp/', zipf)
zipf.close()
person Mark Byers    schedule 06.12.2009
comment
Я бы добавил второй аргумент к вызову записи, передав os.path.relpath(os.path.join(root, file), os.path.join(path, '..')). Это позволит вам заархивировать каталог из любого рабочего каталога без получения полных абсолютных путей в архиве. - person Reimund; 29.06.2013
comment
Когда я пытаюсь заархивировать папку и вывести полученный zip в ту же папку, происходит забавная рекурсия. :-) - person Sibbs Gambling; 23.03.2017
comment
shutil делает это очень просто, используя всего одну строку. Пожалуйста, проверьте ответ ниже .. - person droidlabour; 30.05.2017
comment
вас может заинтересовать выполнение ziph.write (os.path.join (путь, файл), arcname = file), чтобы имена файлов внутри архива не относились к жесткому диску - person Christophe Blin; 14.06.2017
comment
Я получаю. Сжатая (заархивированная) папка пуста в Windows 7 - person information_interchange; 22.01.2018
comment
Ах, я пропустил звонок .close()! - person information_interchange; 23.01.2018
comment
@RoachLord: file не является ключевым словом, даже если ваш редактор выделяет его как одно. - person Eric; 02.11.2018
comment
Также может использоваться с диспетчером контекста: with zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED) as zipf: zipdir('tmp/', zipfh) - person AndB; 11.11.2020
comment
Следующий ответ, если более простой, должен был быть принят - person Pierre; 18.03.2021
comment
Что ziph в этой функции? path - это каталог. Что делать, если у меня есть каталог, содержащий другие подкаталоги? Я добавил вопрос, связанный с этим. stackoverflow.com/questions/67119676/ - person icatalan; 16.04.2021
comment
Следующий ответ не обрабатывает ничего, связанного со сжатием, он просто сохраняет результаты в ZIP-архиве. Если вы ищете фактическое сжатие, правильный ответ - этот, а не следующий с shutil. - person José L. Patiño; 11.07.2021
comment
Хочу добавить, что эта опция позволяет создавать zip-архив с любым расширением файла. т.е. zipfile.ZipFile('Python.meta_zip', 'w') В моей работе мы должны сгруппировать каталог метаданных с файлами в нем и файлом, с которым они связаны. Нам не нужно сжатие, мы просто хотим, чтобы все они были в одном месте, когда мы загружаем их в облако. - person Tanner Davis; 14.07.2021

Самый простой способ - использовать shutil.make_archive. Он поддерживает форматы zip и tar.

import shutil
shutil.make_archive(output_filename, 'zip', dir_name)

Если вам нужно сделать что-то более сложное, чем заархивировать весь каталог (например, пропустить определенные файлы), вам нужно будет покопаться в _ 3_, как предлагали другие.

person crdavis    schedule 03.09.2014
comment
shutil является частью стандартной библиотеки Python. Это должен быть главный ответ - person AlexG; 28.04.2017
comment
Это наиболее краткий ответ здесь, а также имеет то преимущество, что все подкаталоги и файлы добавляются в архив напрямую, а не все, что находится в папке верхнего уровня (что приводит к избыточному уровню в структуре папок при распаковке). - person aitch-hat; 22.06.2017
comment
@cmcginty, не могли бы вы уточнить, какой из аспектов не является потокобезопасным? Будет ли запуск нескольких потоков во время одного вызова вызывать сбой интерпретатора? - person std''OrgnlDave; 05.11.2017
comment
@ std''OrgnlDave Нет, сбой не произойдет. Проблема, которую я видел, заключается в том, что файлы из разных потоков будут скопированы в один и тот же zip-файл. См. bugs.python.org/issue30511. - person cmcginty; 06.11.2017
comment
Имейте в виду, что до Python 3.4 shutil.make_archive не поддерживает ZIP64 и не сможет создавать файлы ZIP размером более 2 ГБ. - person azdev; 19.01.2018
comment
Несмотря на то, что он не является потокобезопасным, он все равно должен быть безопасным, если используется разными потоками в разных каталогах, верно? - person Teekin; 26.05.2018
comment
@Teekin Нет. Если вы посмотрите отчет об ошибках (bugs.python.org/issue30511), вы увидите, что shutil.make_archive использует os.chdir(). Судя по тому, что я читаю о os.chdir(), он действует глобально. - person Sam Malayek; 25.07.2018
comment
@azdev, какое решение для shutil.make_archive для файлов более 2Гб? У меня была эта ошибка ( - person Jane; 03.09.2018
comment
@Jane См. Принятый ответ или один из других ответов на этой странице, в котором вместо shutil используется библиотека zipfile. - person azdev; 04.09.2018
comment
shutil работает очень и очень медленно. Так что это не лучший вариант для каталогов с большими файлами. - person Tedo Vrbanec; 15.10.2018
comment
@azdev Для python версий от 3.6 до 3.7 безопасна ли работа shutil для файлов размером более 2 ГБ? - person Sukanya Pai; 17.01.2020
comment
Если я хочу заархивировать каталог вместо содержимого каталога - есть ли для этого возможность? Также: не можете найти эквивалент --remove-files? - person SwissNavy; 15.04.2020
comment
@SwissNavy см. base_dir вариант - person mckbrd; 26.06.2020
comment
Могу засвидетельствовать, что это лучший ответ - person Cyborg_Patrick; 21.09.2020
comment
Спасибо. Быстрый способ архивирования файлов. - person Usman Liaqat; 22.10.2020
comment
Я пытаюсь автоматизировать заархивирование моего рабочего каталога, который составляет ~ 200 МБ в качестве ежедневной резервной копии, этот код продолжал работать и составлял +84 ГБ и выдавал ошибку, потому что на моем рабочем компьютере закончилось хранилище! - person KiDo; 07.11.2020
comment
Обратите внимание, что это НЕ безопасно для потоков! Это явный os.chdir, который может привести к заархивированию неправильного каталога. Это привело к тому, что мои zip-архивы стали гигантскими, а на диске закончилось место. - person xjcl; 01.03.2021
comment
фактический пример с использованием как root_dir, так и base_dir см .: docs.python.org/3/library/ - person pangyuteng; 04.03.2021
comment
Это небезопасно, это не подходит для каталогов с огромными файлами. - person Shubham_geo; 10.03.2021
comment
Почему этот ответ находится так далеко на странице? - person Cheng; 15.03.2021
comment
Это неверный ответ. Ничего не сжимает, плюс медленнее. принятый ответ правильный и должен быть справочным. - person José L. Patiño; 11.07.2021

Чтобы добавить содержимое mydirectory в новый zip-файл, включая все файлы и подкаталоги:

import os
import zipfile

zf = zipfile.ZipFile("myzipfile.zip", "w")
for dirname, subdirs, files in os.walk("mydirectory"):
    zf.write(dirname)
    for filename in files:
        zf.write(os.path.join(dirname, filename))
zf.close()
person Ben James    schedule 06.12.2009
comment
Для меня этот код выдает ошибку ниже TypeError: invalid file: ‹zipfile.ZipFile [closed]› - person Nishad Up; 23.08.2017
comment
Разве вы не можете использовать with вместо того, чтобы в конце позвонить самому себе close()? - person ArtOfWarfare; 12.01.2018
comment
пример: `с zipfile.ZipFile (myzipfile.zip, w) как zf: pass` - person Subramanya Rao; 02.02.2021

Как я могу создать zip-архив структуры каталогов в Python?

В скрипте Python

В Python 2.7+ shutil имеет функцию make_archive.

from shutil import make_archive
make_archive(
  'zipfile_name', 
  'zip',           # the archive format - or tar, bztar, gztar 
  root_dir=None,   # root for archive - current working dir if None
  base_dir=None)   # start archiving from here - cwd if None too

Здесь заархивированный архив будет называться zipfile_name.zip. Если base_dir находится дальше от root_dir, он исключает файлы не в base_dir, но по-прежнему архивирует файлы в родительских каталогах до root_dir.

У меня была проблема с тестированием этого на Cygwin с 2.7 - ему нужен аргумент root_dir для cwd:

make_archive('zipfile_name', 'zip', root_dir='.')

Использование Python из оболочки

Вы можете сделать это с помощью Python из оболочки, также используя модуль zipfile:

$ python -m zipfile -c zipname sourcedir

Где zipname - это имя файла назначения, который вы хотите (добавьте .zip, если хотите, он не сделает это автоматически), а sourcedir - это путь к каталогу.

Архивирование Python (или просто не нужен родительский каталог):

Если вы пытаетесь заархивировать пакет python с __init__.py и __main__.py, и вам не нужен родительский каталог, это

$ python -m zipfile -c zipname sourcedir/*

А также

$ python zipname

запустит пакет. (Обратите внимание, что вы не можете запускать подпакеты в качестве точки входа из заархивированного архива.)

Архивирование приложения Python:

Если у вас установлен python3.5 + и вы хотите заархивировать пакет Python, используйте zipapp < / а>:

$ python -m zipapp myapp
$ python myapp.pyz
person Aaron Hall    schedule 31.03.2016

Эта функция будет рекурсивно архивировать дерево каталогов, сжимая файлы и записывая правильные относительные имена файлов в архив. Записи в архиве такие же, как созданные zip -r output.zip source_dir.

import os
import zipfile
def make_zipfile(output_filename, source_dir):
    relroot = os.path.abspath(os.path.join(source_dir, os.pardir))
    with zipfile.ZipFile(output_filename, "w", zipfile.ZIP_DEFLATED) as zip:
        for root, dirs, files in os.walk(source_dir):
            # add directory (needed for empty dirs)
            zip.write(root, os.path.relpath(root, relroot))
            for file in files:
                filename = os.path.join(root, file)
                if os.path.isfile(filename): # regular files only
                    arcname = os.path.join(os.path.relpath(root, relroot), file)
                    zip.write(filename, arcname)
person George V. Reilly    schedule 13.06.2013
comment
Это должен быть принятый ответ. - person Florian; 12.07.2021

Используйте shutil, который является частью стандартной библиотеки Python. Использовать shutil очень просто (см. Код ниже):

  • 1-й аргумент: имя результирующего файла zip / tar,
  • 2-й аргумент: zip / tar,
  • 3-й аргумент: dir_name

Код:

import shutil
shutil.make_archive('/home/user/Desktop/Filename','zip','/home/username/Desktop/Directory')
person vadiraj jahagirdar    schedule 12.10.2018
comment
Со всеми приведенными здесь примерами shutil.make_archvie все они создают пустые корневые папки, ведущие к папке, которую вы действительно хотите заархивировать. Я не хочу, чтобы мой архивный файл был разархивирован с помощью / home / user / Desktop, чтобы все могли видеть, где изначально была интересующая папка. Как мне просто заархивировать / Directory и оставить все следы родительских папок? - person kravb; 13.02.2021
comment
Об этом сказано уже 3 раза. И это определенно не лучший ответ. - person José L. Patiño; 11.07.2021

Современный Python (3.6+), использующий модуль pathlib для краткой ООП-подобной обработки пути и pathlib.Path.rglob() для рекурсивного подстановки. Насколько я могу судить, это эквивалентно ответу Джорджа В. Рейли: zip-архивы со сжатием, самый верхний элемент - это каталог, хранит пустые каталоги, использует относительные пути.

from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile

from os import PathLike
from typing import Union


def zip_dir(zip_name: str, source_dir: Union[str, PathLike]):
    src_path = Path(source_dir).expanduser().resolve(strict=True)
    with ZipFile(zip_name, 'w', ZIP_DEFLATED) as zf:
        for file in src_path.rglob('*'):
            zf.write(file, file.relative_to(src_path.parent))

Примечание: как указывают необязательные подсказки типа, zip_name не может быть объектом Path (будет исправлено в версии 3.6.2 + ).

person monk-time    schedule 31.03.2017
comment
Фантастика! Лаконично! Современный! - person ingyhere; 27.04.2020
comment
Это должен быть принятый ответ. - person Philippe Remy; 09.07.2021

Чтобы добавить сжатие к полученному zip-файлу, ознакомьтесь с эту ссылку.

Вам нужно изменить:

zip = zipfile.ZipFile('Python.zip', 'w')

to

zip = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)
person E Smith    schedule 27.04.2013

Я внес некоторые изменения в код, предоставленный Марком Байерсом. Функция ниже также добавит пустые каталоги, если они у вас есть. Примеры должны прояснить, какой путь добавлен в zip.

#!/usr/bin/env python
import os
import zipfile

def addDirToZip(zipHandle, path, basePath=""):
    """
    Adding directory given by \a path to opened zip file \a zipHandle

    @param basePath path that will be removed from \a path when adding to archive

    Examples:
        # add whole "dir" to "test.zip" (when you open "test.zip" you will see only "dir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir')
        zipHandle.close()

        # add contents of "dir" to "test.zip" (when you open "test.zip" you will see only it's contents)
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir', 'dir')
        zipHandle.close()

        # add contents of "dir/subdir" to "test.zip" (when you open "test.zip" you will see only contents of "subdir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir', 'dir/subdir')
        zipHandle.close()

        # add whole "dir/subdir" to "test.zip" (when you open "test.zip" you will see only "subdir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir', 'dir')
        zipHandle.close()

        # add whole "dir/subdir" with full path to "test.zip" (when you open "test.zip" you will see only "dir" and inside it only "subdir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir/subdir')
        zipHandle.close()

        # add whole "dir" and "otherDir" (with full path) to "test.zip" (when you open "test.zip" you will see only "dir" and "otherDir")
        zipHandle = zipfile.ZipFile('test.zip', 'w')
        addDirToZip(zipHandle, 'dir')
        addDirToZip(zipHandle, 'otherDir')
        zipHandle.close()
    """
    basePath = basePath.rstrip("\\/") + ""
    basePath = basePath.rstrip("\\/")
    for root, dirs, files in os.walk(path):
        # add dir itself (needed for empty dirs
        zipHandle.write(os.path.join(root, "."))
        # add files
        for file in files:
            filePath = os.path.join(root, file)
            inZipPath = filePath.replace(basePath, "", 1).lstrip("\\/")
            #print filePath + " , " + inZipPath
            zipHandle.write(filePath, inZipPath)

Выше представлена ​​простая функция, которая должна работать в простых случаях. Вы можете найти более элегантный класс в моем Gist: https://gist.github.com/Eccenux/17526123107ca0ac28e6 < / а>

person Nux    schedule 10.06.2013
comment
Ошибка: zipHandle.write (os.path.join (root,.)) Не принимает во внимание basePath. - person Petter; 30.08.2014
comment
Да, наверное, ты прав. Позже я немного улучшил это ;-) gist.github.com/Eccenux/17526123107ca0ac28e6 - person Nux; 30.08.2014

У меня есть еще один пример кода, который может помочь, используя python3, pathlib и zipfile. Он должен работать в любой ОС.

from pathlib import Path
import zipfile
from datetime import datetime

DATE_FORMAT = '%y%m%d'


def date_str():
    """returns the today string year, month, day"""
    return '{}'.format(datetime.now().strftime(DATE_FORMAT))


def zip_name(path):
    """returns the zip filename as string"""
    cur_dir = Path(path).resolve()
    parent_dir = cur_dir.parents[0]
    zip_filename = '{}/{}_{}.zip'.format(parent_dir, cur_dir.name, date_str())
    p_zip = Path(zip_filename)
    n = 1
    while p_zip.exists():
        zip_filename = ('{}/{}_{}_{}.zip'.format(parent_dir, cur_dir.name,
                                             date_str(), n))
        p_zip = Path(zip_filename)
        n += 1
    return zip_filename


def all_files(path):
    """iterator returns all files and folders from path as absolute path string
    """
    for child in Path(path).iterdir():
        yield str(child)
        if child.is_dir():
            for grand_child in all_files(str(child)):
                yield str(Path(grand_child))


def zip_dir(path):
    """generate a zip"""
    zip_filename = zip_name(path)
    zip_file = zipfile.ZipFile(zip_filename, 'w')
    print('create:', zip_filename)
    for file in all_files(path):
        print('adding... ', file)
        zip_file.write(file)
    zip_file.close()


if __name__ == '__main__':
    zip_dir('.')
    print('end!')
person duncanmonty    schedule 02.10.2015

Для краткого способа сохранить иерархию папок в родительском каталоге для архивации:

import glob
import zipfile

with zipfile.ZipFile(fp_zip, "w", zipfile.ZIP_DEFLATED) as zipf:
    for fp in glob(os.path.join(parent, "**/*")):
        base = os.path.commonpath([parent, fp])
        zipf.write(fp, arcname=fp.replace(base, ""))

Если хотите, вы можете изменить это, чтобы использовать pathlib для подстановки файлов.

person ryanjdillon    schedule 09.07.2019

Вы, вероятно, захотите взглянуть на модуль zipfile; есть документация на http://docs.python.org/library/zipfile.html .

Вы также можете захотеть os.walk() для индексации структуры каталогов.

person me_and    schedule 06.12.2009

Вот вариант ответа Nux, который подходит мне:

def WriteDirectoryToZipFile( zipHandle, srcPath, zipLocalPath = "", zipOperation = zipfile.ZIP_DEFLATED ):
    basePath = os.path.split( srcPath )[ 0 ]
    for root, dirs, files in os.walk( srcPath ):
        p = os.path.join( zipLocalPath, root [ ( len( basePath ) + 1 ) : ] )
        # add dir
        zipHandle.write( root, p, zipOperation )
        # add files
        for f in files:
            filePath = os.path.join( root, f )
            fileInZipPath = os.path.join( p, f )
            zipHandle.write( filePath, fileInZipPath, zipOperation )
person M Katz    schedule 30.07.2014

Попробуйте следующее. У меня сработало.

import zipfile, os
zipf = "compress.zip"  
def main():
    directory = r"Filepath"
    toZip(directory)
def toZip(directory):
    zippedHelp = zipfile.ZipFile(zipf, "w", compression=zipfile.ZIP_DEFLATED )

    list = os.listdir(directory)
    for file_list in list:
        file_name = os.path.join(directory,file_list)

        if os.path.isfile(file_name):
            print file_name
            zippedHelp.write(file_name)
        else:
            addFolderToZip(zippedHelp,file_list,directory)
            print "---------------Directory Found-----------------------"
    zippedHelp.close()

def addFolderToZip(zippedHelp,folder,directory):
    path=os.path.join(directory,folder)
    print path
    file_list=os.listdir(path)
    for file_name in file_list:
        file_path=os.path.join(path,file_name)
        if os.path.isfile(file_path):
            zippedHelp.write(file_path)
        elif os.path.isdir(file_name):
            print "------------------sub directory found--------------------"
            addFolderToZip(zippedHelp,file_name,path)


if __name__=="__main__":
    main()
person Chandra    schedule 23.04.2015

Если вам нужна такая функциональность, как папка сжатия любого обычного графического файлового менеджера, вы можете использовать следующий код, он использует zipfile. Используя этот код, у вас будет zip-файл с путем в качестве его корневой папки.

import os
import zipfile

def zipdir(path, ziph):
    # Iterate all the directories and files
    for root, dirs, files in os.walk(path):
        # Create a prefix variable with the folder structure inside the path folder. 
        # So if a file is at the path directory will be at the root directory of the zip file
        # so the prefix will be empty. If the file belongs to a containing folder of path folder 
        # then the prefix will be that folder.
        if root.replace(path,'') == '':
                prefix = ''
        else:
                # Keep the folder structure after the path folder, append a '/' at the end 
                # and remome the first character, if it is a '/' in order to have a path like 
                # folder1/folder2/file.txt
                prefix = root.replace(path, '') + '/'
                if (prefix[0] == '/'):
                        prefix = prefix[1:]
        for filename in files:
                actual_file_path = root + '/' + filename
                zipped_file_path = prefix + filename
                zipf.write( actual_file_path, zipped_file_path)


zipf = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)
zipdir('/tmp/justtest/', zipf)
zipf.close()
person VGe0rge    schedule 21.03.2016

Для большей гибкости, например выберите каталог / файл по имени используйте:

import os
import zipfile

def zipall(ob, path, rel=""):
    basename = os.path.basename(path)
    if os.path.isdir(path):
        if rel == "":
            rel = basename
        ob.write(path, os.path.join(rel))
        for root, dirs, files in os.walk(path):
            for d in dirs:
                zipall(ob, os.path.join(root, d), os.path.join(rel, d))
            for f in files:
                ob.write(os.path.join(root, f), os.path.join(rel, f))
            break
    elif os.path.isfile(path):
        ob.write(path, os.path.join(rel, basename))
    else:
        pass

Для файлового дерева:

.
├── dir
│   ├── dir2
│   │   └── file2.txt
│   ├── dir3
│   │   └── file3.txt
│   └── file.txt
├── dir4
│   ├── dir5
│   └── file4.txt
├── listdir.zip
├── main.py
├── root.txt
└── selective.zip

Вы можете, например, выберите только dir4 и root.txt:

cwd = os.getcwd()
files = [os.path.join(cwd, f) for f in ['dir4', 'root.txt']]

with zipfile.ZipFile("selective.zip", "w" ) as myzip:
    for f in files:
        zipall(myzip, f)

Или просто listdir в каталог вызова скрипта и добавьте все оттуда:

with zipfile.ZipFile("listdir.zip", "w" ) as myzip:
    for f in os.listdir():
        if f == "listdir.zip":
            # Creating a listdir.zip in the same directory
            # will include listdir.zip inside itself, beware of this
            continue
        zipall(myzip, f)
person pbn    schedule 25.09.2018
comment
Это застегивается, но не сжимается. - person Alex; 12.12.2018

Допустим, вы хотите заархивировать все папки (подкаталоги) в текущем каталоге.

for root, dirs, files in os.walk("."):
    for sub_dir in dirs:
        zip_you_want = sub_dir+".zip"
        zip_process = zipfile.ZipFile(zip_you_want, "w", zipfile.ZIP_DEFLATED)
        zip_process.write(file_you_want_to_include)
        zip_process.close()

        print("Successfully zipped directory: {sub_dir}".format(sub_dir=sub_dir))
person Sushilinux    schedule 09.04.2019

Здесь так много ответов, и я надеюсь, что смогу внести свой вклад в свою собственную версию, основанную на исходном ответе (кстати), но с более графической перспективой, также использующей контекст для каждой zipfile настройки и сортировки os.walk(), чтобы иметь упорядоченный вывод.

Имея эти папки и их файлы (среди других папок), я хотел создать .zip для каждой cap_ папки:

$ tree -d
.
├── cap_01
|    ├── 0101000001.json
|    ├── 0101000002.json
|    ├── 0101000003.json
|
├── cap_02
|    ├── 0201000001.json
|    ├── 0201000002.json
|    ├── 0201001003.json
|
├── cap_03
|    ├── 0301000001.json
|    ├── 0301000002.json
|    ├── 0301000003.json
| 
├── docs
|    ├── map.txt
|    ├── main_data.xml
|
├── core_files
     ├── core_master
     ├── core_slave

Вот что я применил, с комментариями для лучшего понимания процесса.

$ cat zip_cap_dirs.py 
""" Zip 'cap_*' directories. """           
import os                                                                       
import zipfile as zf                                                            


for root, dirs, files in sorted(os.walk('.')):                                                                                               
    if 'cap_' in root:                                                          
        print(f"Compressing: {root}")                                           
        # Defining .zip name, according to Capítulo.                            
        cap_dir_zip = '{}.zip'.format(root)                                     
        # Opening zipfile context for current root dir.                         
        with zf.ZipFile(cap_dir_zip, 'w', zf.ZIP_DEFLATED) as new_zip:          
            # Iterating over os.walk list of files for the current root dir.    
            for f in files:                                                     
                # Defining relative path to files from current root dir.        
                f_path = os.path.join(root, f)                                  
                # Writing the file on the .zip file of the context              
                new_zip.write(f_path) 

По сути, для каждой итерации над os.walk(path) я открываю контекст для установки zipfile, а затем повторяю итерацию по files, который представляет собой list файлов из каталога root, формируя относительный путь для каждого файла на основе текущего каталога root, добавляя в запущенный zipfile контекст.

И результат представлен так:

$ python3 zip_cap_dirs.py
Compressing: ./cap_01
Compressing: ./cap_02
Compressing: ./cap_03

Чтобы увидеть содержимое каждого .zip каталога, вы можете использовать команду less:

$ less cap_01.zip

Archive:  cap_01.zip
 Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
--------  ------  ------- ---- ---------- ----- --------  ----
  22017  Defl:N     2471  89% 2019-09-05 08:05 7a3b5ec6  cap_01/0101000001.json
  21998  Defl:N     2471  89% 2019-09-05 08:05 155bece7  cap_01/0101000002.json
  23236  Defl:N     2573  89% 2019-09-05 08:05 55fced20  cap_01/0101000003.json
--------          ------- ---                           -------
  67251             7515  89%                            3 files
person ivanleoncz    schedule 05.09.2019

Заархивируйте файл или дерево (каталог и его подкаталоги).

from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED

def make_zip(tree_path, zip_path, mode='w', skip_empty_dir=False):
    with ZipFile(zip_path, mode=mode, compression=ZIP_DEFLATED) as zf:
        paths = [Path(tree_path)]
        while paths:
            p = paths.pop()
            if p.is_dir():
                paths.extend(p.iterdir())
                if skip_empty_dir:
                    continue
            zf.write(p)

Чтобы добавить к существующему архиву, передайте mode='a', чтобы создать новый архив mode='w' (значение по умолчанию в приведенном выше). Допустим, вы хотите связать 3 разных дерева каталогов в одном архиве.

make_zip(path_to_tree1, path_to_arch, mode='w')
make_zip(path_to_tree2, path_to_arch, mode='a')
make_zip(path_to_file3, path_to_arch, mode='a')
person Michael Ekoka    schedule 17.09.2020

Решение с использованием pathlib.Path, которое не зависит от используемой ОС:

import zipfile
from pathlib import Path

def zip_dir(path: Path, zip_file_path: Path):
    """Zip all contents of path to zip_file"""
    files_to_zip = [
        file for file in path.glob('*') if file.is_file()]
    with zipfile.ZipFile(
        zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zip_f:
        for file in files_to_zip:
            print(file.name)
            zip_f.write(file, file.name)

current_dir = Path.cwd()  
zip_dir = current_dir / "test"
tools.zip_dir(
    zip_dir, current_dir / 'Zipped_dir.zip')
person Alex    schedule 05.01.2021

Вот современный подход с использованием pathlib и диспетчера контекста. Помещает файлы непосредственно в zip-архив, а не во вложенную папку.

def zip_dir(filename: str, dir_to_zip: pathlib.Path):
    with zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
        # Use glob instead of iterdir(), to cover all subdirectories.
        for directory in dir_to_zip.glob('**'):
            for file in directory.iterdir():
                if not file.is_file():
                    continue
                # Strip the first component, so we don't create an uneeded subdirectory
                # containing everything.
                zip_path = pathlib.Path(*file.parts[1:])
                # Use a string, since zipfile doesn't support pathlib  directly.
                zipf.write(str(file), str(zip_path))
person Turtles Are Cute    schedule 14.12.2016

Я подготовил функцию, объединив решение Марка Байерса с комментариями Реймунда и Мортена Зилмеров (относительный путь и включая пустые каталоги). Рекомендуется использовать with при создании файла ZipFile.

Функция также подготавливает имя zip-файла по умолчанию с именем заархивированного каталога и расширением .zip. Следовательно, он работает только с одним аргументом: исходный каталог, который нужно заархивировать.

import os
import zipfile

def zip_dir(path_dir, path_file_zip=''):
if not path_file_zip:
    path_file_zip = os.path.join(
        os.path.dirname(path_dir), os.path.basename(path_dir)+'.zip')
with zipfile.ZipFile(path_file_zip, 'wb', zipfile.ZIP_DEFLATED) as zip_file:
    for root, dirs, files in os.walk(path_dir):
        for file_or_dir in files + dirs:
            zip_file.write(
                os.path.join(root, file_or_dir),
                os.path.relpath(os.path.join(root, file_or_dir),
                                os.path.join(path_dir, os.path.pardir)))
person Gürol Canbek    schedule 24.12.2016

Что ж, после прочтения предложений я придумал очень похожий способ, который работает с 2.7.x без создания "забавных" имен каталогов (абсолютные имена), и создаст только указанную папку внутри zip.

Или на всякий случай, если вам нужно, чтобы ваш zip-архив содержал внутри папку с содержимым выбранного каталога.

def zipDir( path, ziph ) :
 """
 Inserts directory (path) into zipfile instance (ziph)
 """
 for root, dirs, files in os.walk( path ) :
  for file in files :
   ziph.write( os.path.join( root, file ) , os.path.basename( os.path.normpath( path ) ) + "\\" + file )

def makeZip( pathToFolder ) :
 """
 Creates a zip file with the specified folder
 """
 zipf = zipfile.ZipFile( pathToFolder + 'file.zip', 'w', zipfile.ZIP_DEFLATED )
 zipDir( pathToFolder, zipf )
 zipf.close()
 print( "Zip file saved to: " + pathToFolder)

makeZip( "c:\\path\\to\\folder\\to\\insert\\into\\zipfile" )
person Xedret    schedule 15.08.2018

Функция для создания zip-файла.

def CREATEZIPFILE(zipname, path):
    #function to create a zip file
    #Parameters: zipname - name of the zip file; path - name of folder/file to be put in zip file

    zipf = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)
    zipf.setpassword(b"password") #if you want to set password to zipfile

    #checks if the path is file or directory
    if os.path.isdir(path):
        for files in os.listdir(path):
            zipf.write(os.path.join(path, files), files)

    elif os.path.isfile(path):
        zipf.write(os.path.join(path), path)
    zipf.close()
person sushh    schedule 19.11.2018
comment
пожалуйста, объясните на примере, чтобы я мог исправить свой ответ - person sushh; 29.01.2019
comment
Однако в настоящее время zipfile не может создать зашифрованный файл (из docs.python.org/3.9/library /zipfile.html) - person Georg; 14.05.2020

Для всех, кто углубляется в этот вопрос и пытается заархивировать тот же каталог, в котором находится их программа, и получает как очень глубокие древовидные структуры, так и заканчивает рекурсией из-за самого zip-файла, попробуйте это.

Это комбинация ответа Марка и некоторых дополнительных проверок, чтобы убедиться, что нет рекурсивного архивирования самого zip-файла и нет излишне глубоких структуры папок.

import os
import zipfile

def zipdir(path, ziph, ignored_directories, ignored_files):
    # ziph is zipfile handle
    for root, dirs, files in os.walk(path):
      for file in files:
        if not any(ignored_dir in root for ignored_dir in ignored_directories):
          if not any(ignored_fname in file for ignored_fname in ignored_files):
            ziph.write(os.path.join(root, file))

# current working directory
this_dir = os.path.dirname(os.path.abspath(__file__))

# the directory within the working directory the zip will be created in (build/archives).
zip_dest_dir = os.path.join('build', 'archives')

# verify zip_dest_dir exists: if not, create it
if not os.path.isdir(zip_dest_dir):
    os.makedirs(zip_dest_dir, exist_ok=True)

# leave zip_dest_dir blank (or set dist_dir = this_dir) if you want the zip file in the working directory (same directory as the script)
dest_dir = os.path.join(this_dir, zip_dest_dir)

# name the zip file: remember the file extension
zip_filename = 'zipped_directory.zip'

# zip file's path
zip_path = os.path.join(dest_dir, zip_filename)

# create the zipfile handle: you can change ZIP_STORED to any other compression algorithm of your choice, like ZIP_DEFLATED, if you need actual compression
zipf = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_STORED)

# ignored files and directories: I personally wanted to ignore the "build" directory, alongside with "node_modules", so those would be listed here.
ignored_dirs = []

# ignore any specific files: in my case, I was ignoring the script itself, so I'd include 'deploy.py' here
ignored_files = [zip_filename]

# zip directory contents
zipdir('.', zipf, ignored_dirs, ignored_files)
zipf.close()

Результирующий zip-файл должен включать только каталоги, начинающиеся с рабочего каталога: поэтому нет Users / user / Desktop / code /.../ working_directory /.../ и т. Д. вид файловой структуры.

person 499602D2    schedule 28.07.2020

Очевидным способом было бы пойти с shutil, как сказано во втором верхнем ответе, но если вы по какой-то причине все еще хотите использовать ZipFile, и если у вас возникают проблемы с этим (например, ERR 13 в Windows и т. Д.) , Вы можете использовать это исправление:

import os
import zipfile

def retrieve_file_paths(dirName):
  filePaths = []
  for root, directories, files in os.walk(dirName):
    for filename in files:
        filePath = os.path.join(root, filename)
        filePaths.append(filePath)
  return filePaths
 
def main(dir_name, output_filename):
  filePaths = retrieve_file_paths(dir_name)
   
  zip_file = zipfile.ZipFile(output_filename+'.zip', 'w')
  with zip_file:
    for file in filePaths:
      zip_file.write(file)

main("my_dir", "my_dir_archived")

Этот рекурсивно выполняет итерацию по каждой подпапке / файлу в данной папке и записывает их в zip-файл вместо попытки напрямую заархивировать папку.

person GuruPrasaathM    schedule 10.03.2021