Come creare un archivio zip di una directory in Python?
-
13-09-2019 - |
Domanda
Come posso creare un archivio zip di una struttura di directory in Python?
Soluzione
Come altri hanno fatto notare, è necessario utilizzare zipfile . La documentazione dice che cosa sono disponibili le funzioni, ma in realtà non spiega come si possono utilizzare per comprimere un'intera directory. Penso che sia più facile da spiegare con qualche esempio di codice:
#!/usr/bin/env python
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))
if __name__ == '__main__':
zipf = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)
zipdir('tmp/', zipf)
zipf.close()
Tratto da: http://www.devshed.com/c/ un / Python / Python-decompresso /
Altri suggerimenti
Il modo più semplice è quello di utilizzare shutil.make_archive
. Esso supporta entrambi i formati zip e tar.
import shutil
shutil.make_archive(output_filename, 'zip', dir_name)
Se avete bisogno di fare qualcosa di più complicato di zippare l'intera directory (come ad esempio saltare alcuni file), allora avrete bisogno di scavare nel modulo zipfile
come altri hanno suggerito.
Per aggiungere il contenuto di mydirectory
in un nuovo file zip, inclusi tutti i file e le sottodirectory:
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()
Come posso creare un archivio zip di una struttura di directory in Python?
In uno script Python
In Python 2.7+, shutil
ha una funzione 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
Ecco l'archivio zippato si chiamerà zipfile_name.zip
. Se base_dir
è più in basso da root_dir
si esclude i file non in base_dir
, ma ancora archiviare i file nei dirs genitore fino al root_dir
.
Ho avuto un problema prove su Cygwin con 2.7 - vuole un argomento root_dir, per CWD:
make_archive('zipfile_name', 'zip', root_dir='.')
Uso Python dal guscio
Si può fare questo con Python dalla shell anche utilizzando il modulo zipfile
:
$ python -m zipfile -c zipname sourcedir
Dove zipname
è il nome del file di destinazione che si desidera (aggiungere .zip
se lo vuoi, non lo farà automaticamente) e sourcedir è il percorso della directory.
zippare Python (o semplicemente non vogliono dir genitore):
Se si sta cercando di comprimere un pacchetto python con un __init__.py
e __main__.py
, e non si vuole dir genitore, è
$ python -m zipfile -c zipname sourcedir/*
E
$ python zipname
sarebbe eseguire il pacchetto. (Si noti che non è possibile eseguire sottopackage come il punto di ingresso da un archivio zip.)
Zippare un'applicazione Python:
Se avete python3.5 +, e in particolare vuole comprimere un pacchetto Python, utilizzare zipapp :
$ python -m zipapp myapp
$ python myapp.pyz
Questa funzione ricorsivamente comprimere un albero di directory, compressione i file, e la registrazione dei nomi di file corretti relativi nell'archivio. Le voci di archivio sono gli stessi di quelli generati dalle 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)
Usa shutil, che fa parte del pitone set libreria standard. Utilizzando shutil è così semplice (vedi codice qui sotto):
- 1 ° ARG: Nome del file zip / tar risultante,
- 2 ° arg: zip / catrame,
- 3 arg: dir_name
Codice:
import shutil
shutil.make_archive('/home/user/Desktop/Filename','zip','/home/username/Desktop/Directory')
Per aggiungere la compressione al file zip risultante, controlla questo link .
È necessario modificare:
zip = zipfile.ZipFile('Python.zip', 'w')
a
zip = zipfile.ZipFile('Python.zip', 'w', zipfile.ZIP_DEFLATED)
Ho fatto alcune modifiche al codice dato da Mark Byers . Sotto funzione aggiunge anche le directory vuote se li avete. Esempi dovrebbero renderlo più chiaro qual è il percorso aggiunto alla 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)
Sopra è una semplice funzione che dovrebbe funzionare per i casi semplici. È possibile trovare la classe più elegante nel mio Gist: https://gist.github.com/Eccenux/17526123107ca0ac28e6
Probabilmente si desidera guardare il modulo zipfile
; c'è la documentazione a http://docs.python.org/library/zipfile.html .
Si consiglia inoltre os.walk()
per indicizzare la struttura di directory.
Ho un altro esempio di codice che può aiutare, utilizzando python3, pathlib e zipfile. Dovrebbe funzionare in qualsiasi sistema operativo.
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!')
Qui è una variante sulla risposta data dal Nux che funziona per me:
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 )
provare il di sotto di un .it ha lavorato per me .
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()
Se si desidera una funzionalità come la cartella compressa di qualsiasi file manager grafico comune è possibile utilizzare il seguente codice, si utilizza il zipfile modulo. Utilizzando questo codice si avrà il file zip con il percorso come la sua cartella principale.
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()
Moderno Python (versione 3.6 e successive) utilizzando il pathlib
modulo per la programmazione orientata agli oggetti concisa manipolazione -come di percorsi e pathlib.Path.rglob()
ricorsiva globbing. Per quanto posso dire, questo è equivalente alla risposta di George V. Reilly: sfreccia con la compressione, l'elemento più in alto è una directory, mantiene dirs vuote, utilizza i percorsi relativi.
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))
Nota: come suggerimenti di tipo opzionale indicano, zip_name
non può essere un oggetto Path ( sarebbe stato fissato in 3.6. 2+ ).
Per dare maggiore flessibilità, per esempio Seleziona directory / file uso Nome:
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
Per un albero di file:
.
├── dir
│ ├── dir2
│ │ └── file2.txt
│ ├── dir3
│ │ └── file3.txt
│ └── file.txt
├── dir4
│ ├── dir5
│ └── file4.txt
├── listdir.zip
├── main.py
├── root.txt
└── selective.zip
È possibile per esempio selezionare solo dir4
e 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)
O semplicemente listdir
nella directory invocazione di script e aggiungere tutto da lì:
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)
Dire che si desidera Zip tutte le cartelle (directory sotto) nella directory corrente.
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))
Ecco un approccio moderno, utilizzando pathlib, e un gestore di contesto. Mette i file direttamente nel zip, piuttosto che in una sottocartella.
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))
Ho preparato una funzione consolidando soluzione Mark Byers' con i commenti Reimund e Morten ZILMER di (relativo percorso compreso directory vuote). Come best practice, with
viene utilizzato nella costruzione di file di ZipFile.
La funzione prepara anche un nome di file di default zip con il nome della directory zip e l'estensione '.zip'. Pertanto, funziona con un solo argomento:. La directory di origine da 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)))
# import required python modules
# You have to install zipfile package using pip install
import os,zipfile
# Change the directory where you want your new zip file to be
os.chdir('Type your destination')
# Create a new zipfile ( I called it myfile )
zf = zipfile.ZipFile('myfile.zip','w')
# os.walk gives a directory tree. Access the files using a for loop
for dirnames,folders,files in os.walk('Type your directory'):
zf.write('Type your Directory')
for file in files:
zf.write(os.path.join('Type your directory',file))
Bene, dopo aver letto i suggerimenti mi si avvicinò con un modo molto simile che funziona con 2.7.x senza creare i nomi delle directory "divertenti" (nomi di assoluto-simili), e creerà solo la cartella specificata all'interno della cerniera.
O solo in caso di bisogno il tuo zip per contenere una cartella all'interno con il contenuto della directory selezionata.
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" )
funzione per creare file 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()
Per un modo conciso per mantenere la gerarchia delle cartelle sotto la directory padre da archiviare:
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, ""))
Se si desidera, è possibile modificare questa opzione per utilizzare pathlib
per il file globbing .
Così molte risposte qui, e spero che potrei contribuire con la mia versione, che si basa sulla risposta originale (tra l'altro), ma con una prospettiva più grafica, anche utilizzando contesto per ogni configurazione zipfile
e ordinamento os.walk()
, al fine di avere un output ordinato.
Avendo queste cartelle e file (tra le altre cartelle), ho voluto creare un .zip
per ogni cartella 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
Ecco quello che ho fatto domanda, con i commenti per una migliore comprensione del processo.
$ 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)
In sostanza, per ogni iterazione sopra os.walk(path)
, sto aprendo un contesto per la configurazione zipfile
e poi, l'iterazione iterazione sopra files
, che è un list
di file dalla directory root
, formando il percorso relativo per ciascun file in base alla corrente root
directory, aggiungendo al contesto zipfile
che è in esecuzione.
E l'uscita si presenta in questo modo:
$ python3 zip_cap_dirs.py
Compressing: ./cap_01
Compressing: ./cap_02
Compressing: ./cap_03
Per visualizzare il contenuto di ogni directory .zip
, è possibile utilizzare il comando 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