Curso
Requisitos previos para trabajar con archivos ZIP
- Necesitas conocer el manejo de archivos en Python para entender cómo trabajar con archivos ZIP. Si no lo dominas, visita la sección File Handling de W3Schools para aprender.
- Conceptos de POO en Python
- Conceptos de Python como condicionales, bucles, funciones, clases, etc.
- Si no conoces Python, haz el curso gratuito de DataCamp Intro to Python for Data Science para aprender el lenguaje Python o lee la documentación oficial de Python.
Abre este enlace para descargar todas las carpetas ZIP que utilizo en las próximas secciones.
¿Qué es un archivo ZIP?
ZIP es un formato de archivo contenedor que admite compresión sin pérdida. Un archivo ZIP es un único archivo que contiene uno o varios archivos comprimidos.
¿Para qué sirven los archivos ZIP?
- Los archivos ZIP te ayudan a reunir en un solo lugar todos los archivos relacionados.
- Los archivos ZIP ayudan a reducir el tamaño de los datos.
- Los archivos ZIP se transfieren más rápido que archivos sueltos en muchas conexiones.
Módulo zipfile
Explora todos los métodos y clases del módulo zipfile con el método dir(). Mira el código para listar todas las clases y métodos del módulo zipfile.
import zipfile # importing the 'zipfile' module
print(dir(zipfile))
['BZIP2_VERSION', 'BadZipFile', 'BadZipfile', 'DEFAULT_VERSION', 'LZMACompressor', 'LZMADecompressor', 'LZMA_VERSION', 'LargeZipFile', 'MAX_EXTRACT_VERSION', 'PyZipFile', 'ZIP64_LIMIT', 'ZIP64_VERSION', 'ZIP_BZIP2', 'ZIP_DEFLATED', 'ZIP_FILECOUNT_LIMIT', 'ZIP_LZMA', 'ZIP_MAX_COMMENT', 'ZIP_STORED', 'ZipExtFile', 'ZipFile', 'ZipInfo', '_CD64_CREATE_VERSION', '_CD64_DIRECTORY_RECSIZE', '_CD64_DIRECTORY_SIZE', '_CD64_DISK_NUMBER', '_CD64_DISK_NUMBER_START', '_CD64_EXTRACT_VERSION', '_CD64_NUMBER_ENTRIES_THIS_DISK', '_CD64_NUMBER_ENTRIES_TOTAL', '_CD64_OFFSET_START_CENTDIR', '_CD64_SIGNATURE', '_CD_COMMENT_LENGTH', '_CD_COMPRESSED_SIZE', '_CD_COMPRESS_TYPE', '_CD_CRC', '_CD_CREATE_SYSTEM', '_CD_CREATE_VERSION', '_CD_DATE', '_CD_DISK_NUMBER_START', '_CD_EXTERNAL_FILE_ATTRIBUTES', '_CD_EXTRACT_SYSTEM', '_CD_EXTRACT_VERSION', '_CD_EXTRA_FIELD_LENGTH', '_CD_FILENAME_LENGTH', '_CD_FLAG_BITS', '_CD_INTERNAL_FILE_ATTRIBUTES', '_CD_LOCAL_HEADER_OFFSET', '_CD_SIGNATURE', '_CD_TIME', '_CD_UNCOMPRESSED_SIZE', '_ECD_COMMENT', '_ECD_COMMENT_SIZE', '_ECD_DISK_NUMBER', '_ECD_DISK_START', '_ECD_ENTRIES_THIS_DISK', '_ECD_ENTRIES_TOTAL', '_ECD_LOCATION', '_ECD_OFFSET', '_ECD_SIGNATURE', '_ECD_SIZE', '_EndRecData', '_EndRecData64', '_FH_COMPRESSED_SIZE', '_FH_COMPRESSION_METHOD', '_FH_CRC', '_FH_EXTRACT_SYSTEM', '_FH_EXTRACT_VERSION', '_FH_EXTRA_FIELD_LENGTH', '_FH_FILENAME_LENGTH', '_FH_GENERAL_PURPOSE_FLAG_BITS', '_FH_LAST_MOD_DATE', '_FH_LAST_MOD_TIME', '_FH_SIGNATURE', '_FH_UNCOMPRESSED_SIZE', '_SharedFile', '_Tellable', '_ZipDecrypter', '_ZipWriteFile', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_check_compression', '_check_zipfile', '_get_compressor', '_get_decompressor', 'binascii', 'bz2', 'compressor_names', 'crc32', 'error', 'importlib', 'io', 'is_zipfile', 'lzma', 'main', 'os', 're', 'shutil', 'sizeCentralDir', 'sizeEndCentDir', 'sizeEndCentDir64', 'sizeEndCentDir64Locator', 'sizeFileHeader', 'stat', 'stringCentralDir', 'stringEndArchive', 'stringEndArchive64', 'stringEndArchive64Locator', 'stringFileHeader', 'struct', 'structCentralDir', 'structEndArchive', 'structEndArchive64', 'structEndArchive64Locator', 'structFileHeader', 'sys', 'threading', 'time', 'zlib']
Has visto un montón de clases y métodos, ¿verdad? Pero no vas a aprenderlos todos. Verás solo algunas clases y métodos para trabajar con archivos ZIP.
Veamos algunas excepciones, clases y métodos útiles con una breve explicación.
Excepciones
Una excepción es un mensaje que te permite mostrar el error exacto como quieras. En Python, usas las palabras clave try, except y finally para manejar errores.
Si no estás familiarizado con el manejo de errores, ve a la documentación de Python Error Handling para aprender más.
Veamos todas las excepciones del módulo zipfile.
zipfile.BadZipFile
zipfile.BadZipFile es una excepción del módulo zipfile. Este error se lanza para archivos ZIP dañados. Mira el ejemplo.
## zipfile.BadZipFile
import zipfile
def main():
try:
with zipfile.ZipFile('sample_file.zip') as file: # opening the zip file using 'zipfile.ZipFile' class
print("Ok")
except zipfile.BadZipFile: # if the zip file has any errors then it prints the error message which you wrote under the 'except' block
print('Error: Zip file is corrupted')
if __name__ == '__main__': main()
## I used a badfile for the test
Ok
zipfile.LargeZipFile
Si quieres trabajar con un ZIP grande, necesitas habilitar la funcionalidad ZIP64 al abrirlo. Si no la habilitas, se lanzará LargeZipFile. Mira el ejemplo.
## zipfile.LargeZipFile
## Without enabling 'Zip64'
import zipfile
def main():
try:
with zipfile.ZipFile('sample_file.zip') as file:
print('File size is compatible')
except zipfile.LargeZipFile: # it raises an 'LargeZipFile' error because you didn't enable the 'Zip64'
print('Error: File size if too large')
if __name__ == '__main__': main()
File size is compatible
## zipfile.LargeZipFile
## With enabling 'ZIP64'
import zipfile
def main():
try:
with zipfile.ZipFile('sample_file.zip', mode = 'r', allowZip64 = True) as file: # here enabling the 'Zip64'
print('File size is compatible')
except zipfile.LargeZipFile:
print('Error: File size if too large') # if the file size is too large to open it prints the error you have written
if __name__ == '__main__': main()
File size is compatible
Elige un archivo ZIP adecuado para probar el manejo de excepciones y ejecuta el programa. Así lo verás claro.
Clases
En pocas palabras, una clase es un conjunto de métodos y atributos. Usas los métodos y atributos de la clase donde quieras creando instancias de dicha clase.
Veamos algunas clases del módulo zipfile.
zipfile.ZipFile
La clase más común para trabajar con archivos ZIP es ZipFile.
zipfile.ZipFile se usa para escribir y leer archivos ZIP. Incluye métodos para gestionarlos.
Ahora, explora los métodos de la clase ZipFile con dir(). Mira el código.
import zipfile
print(dir(zipfile.ZipFile)) # accessing the 'ZipFile' class
['_RealGetContents', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_extract_member', '_fpclose', '_open_to_write', '_sanitize_windows_name', '_windows_illegal_name_trans_table', '_write_end_record', '_writecheck', 'close', 'comment', 'extract', 'extractall', 'fp', 'getinfo', 'infolist', 'namelist', 'open', 'printdir', 'read', 'setpassword', 'testzip', 'write', 'writestr']
Ya has usado la clase zipfile.ZipFile para leer archivos ZIP en ejemplos anteriores.
zipfile.ZipFile incluye muchos métodos como extract, open, getinfo, setpassword, etc., para trabajar con archivos ZIP.
Veamos algunos métodos de la clase ZipFile.
## zipfile.ZipFile
import zipfile
def main():
with zipfile.ZipFile('sample_file.zip') as file:
# ZipFile.infolist() returns a list containing all the members of an archive file
print(file.infolist())
# ZipFile.namelist() returns a list containing all the members with names of an archive file
print(file.namelist())
# ZipFile.getinfo(path = filepath) returns the information about a member of Zip file.
# It raises a KeyError if it doesn't contain the mentioned file
print(file.getinfo(file.namelist()[-1]))
# ZipFile.open(path = filepath, mode = mode_type, pwd = password) opens the members of an archive file
# 'pwd' is optional -> if it has password mention otherwise leave it
text_file = file.open(name = file.namelist()[-1], mode = 'r')
# 'read()' method of the file prints all the content of the file. You see this method in file handling.
print(text_file.read())
# You must close the file if you don't open a file using 'with' keyword
# 'close()' method is used to close the file
text_file.close()
# ZipFile.extractall(path = filepath, pwd = password) extracts all the files to current directory
file.extractall()
# after executing check the directory to see extracted files
if __name__ == '__main__': main()
[<ZipInfo filename='extra_file.txt' filemode='-rw-rw-rw-' file_size=59>, <ZipInfo filename='READ ME.txt' filemode='-rw-rw-rw-' file_size=59>, <ZipInfo filename='even_odd.py' filemode='-rw-rw-rw-' file_size=129>]
['extra_file.txt', 'READ ME.txt', 'even_odd.py']
<ZipInfo filename='even_odd.py' filemode='-rw-rw-rw-' file_size=129>
b"num = int(input('Enter a Number:- '))\r\nif num % 2 == 0:\r\n\tprint('{} is Even'.fromat(num))\r\nelse:\r\n\tprint('{} is Odd'.fromat(num))"
Si quieres aprender todos los métodos de la clase ZipFile, usa la función help() sobre el método que te interese.
O ve a la documentación oficial de Python.
zipfile.ZipInfo
La clase zipfile.ZipInfo se usa para representar un elemento de una carpeta ZIP.
Primero, explora todos los objetos de la clase zipfile.ZipInfo con dir(). Mira el código.
## zipfile.ZipInfo
import zipfile
print(dir(zipfile.ZipInfo))
['CRC', 'FileHeader', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_decodeExtra', '_encodeFilenameFlags', '_raw_time', 'comment', 'compress_size', 'compress_type', 'create_system', 'create_version', 'date_time', 'external_attr', 'extra', 'extract_version', 'file_size', 'filename', 'flag_bits', 'from_file', 'header_offset', 'internal_attr', 'is_dir', 'orig_filename', 'reserved', 'volume']
Ahora vas a ver algunos métodos de la clase zipfile.ZipInfo.
## zipfile.ZipInfo
import zipfile
def main():
with zipfile.ZipFile('sample_file.zip') as file:
# 'infolist()' is the object of 'ZipFile' class
# 'infolist()' returns a list containing all the folders and files of the zip -> 'ZipInfo' objects
# assigning last element of the list to a variable to test all the methods of 'ZipInfo'
archive = file.infolist()
read_me_file = archive[-1]
# 'ZipInfo' methods
# ZipInfo_object.filename returns the name of the file
print("Name of the file:- {}".format(read_me_file.filename))
# ZipInfo_object.file_size returns the size of the file
print("Size of the file:- {}".format(read_me_file.file_size))
# ZipInfo_object.is_dir() returns True if it's directory otherwise False
print("Is directory:- {}".format(read_me_file.is_dir()))
# ZipInfo_object.date_time() returns the created date & time of file
print("File created data & time:- {}".format(read_me_file.date_time))
if __name__ == '__main__': main()
Name of the file:- sample_file/READ ME.txt
Size of the file:- 59
Is directory:- False
File created data & time:- (2018, 10, 4, 11, 32, 22)
Ve a ZipInfo si quieres aprender más sobre los objetos de ZipInfo.
Métodos
Los métodos son bloques de código con una funcionalidad concreta en el programa. Por ejemplo, si quieres el valor absoluto de un número, puedes usar el método de Python llamado abs.
Puedes usarlos donde quieras. Veamos algunos métodos del módulo zipfile.
zipfile.is_zipfile()
El método is_zipfile(filename) del módulo zipfile devuelve True si el archivo es un ZIP válido; en caso contrario devuelve False.
Veamos un ejemplo.
## zipfile.is_zip(filename)
import zipfile
def main():
print(zipfile.is_zipfile('sample_file.zip')) # it returns True
if __name__ == '__main__': main()
True
Cómo manejar archivos ZIP
En esta sección aprenderás a manejar archivos ZIP como abrir, extraer, escribir, etc.
Extraer un archivo ZIP
Extrae los archivos de un ZIP al directorio actual usando el método extractall.
## extracting zip file
import zipfile
def main():
# assigning filename to a variable
file_name = 'sample_file.zip'
# opening Zip using 'with' keyword in read mode
with zipfile.ZipFile(file_name, 'r') as file:
# printing all the information of archive file contents using 'printdir' method
print(file.printdir())
# extracting the files using 'extracall' method
print('Extracting all files...')
file.extractall()
print('Done!') # check your directory of zip file to see the extracted files
if __name__ == '__main__': main()
File Name Modified Size
sample_file/ 2018-10-04 11:33:22 0
sample_file/even_odd.py 2018-06-29 23:35:54 129
sample_file/READ ME.txt 2018-10-04 11:32:22 59
None
Extracting all files...
Done!
Extraer un ZIP con contraseña
Para extraer un ZIP con contraseña, debes pasar un valor al argumento posicional pwd de los métodos extract(pwd = password) o extractall(pwd = password).
La contraseña debe estar en bytes. Para convertir un str a bytes usa el método integrado bytes de Python con codificación utf-8.
Veamos un ejemplo.
## extracting zip with password
import zipfile
def main():
file_name = 'pswd_file.zip'
pswd = 'datacamp'
with zipfile.ZipFile(file_name) as file:
# password you pass must be in the bytes you converted 'str' into 'bytes'
file.extractall(pwd = bytes(pswd, 'utf-8'))
if __name__ == '__main__': main()
También puedes extraer archivos usando el método setpassword(pwd = password) de la clase ZipFile. Mira el ejemplo.
## extracting zip with password
import zipfile
def main():
file_name = 'pswd_file.zip'
pswd = 'datacamp'
with zipfile.ZipFile(file_name) as file:
# 'setpassword' method is used to give a password to the 'Zip'
file.setpassword(pwd = bytes(pswd, 'utf-8'))
file.extractall()
if __name__ == '__main__': main()
Crear archivos ZIP
Para crear un archivo ZIP no necesitas métodos adicionales. Simplemente pasa el nombre a la clase ZipFile y creará un archivo en el directorio actual.
Mira el ejemplo.
## Creating Zip file
import zipfile
def main():
archive_name = 'example_file.zip'
# below one line of code will create a 'Zip' in the current working directory
with zipfile.ZipFile(archive_name, 'w') as file:
print("{} is created.".format(archive_name))
if __name__ == '__main__': main()
example_file.zip is created.
Escribir en archivos ZIP
Debes abrir los archivos ZIP en modo write para escribir archivos en el contenedor. Este modo sobrescribe todo lo que haya en el ZIP.
Veamos un ejemplo.
## Writing files to zip
import zipfile
def main():
file_name = 'sample_file.zip'
# Opening the 'Zip' in writing mode
with zipfile.ZipFile(file_name, 'w') as file:
# write mode overrides all the existing files in the 'Zip.'
# you have to create the file which you have to write to the 'Zip.'
file.write('extra_file.txt')
print('File overrides the existing files')
# opening the 'Zip' in reading mode to check
with zipfile.ZipFile(file_name, 'r') as file:
print(file.namelist())
if __name__ == '__main__': main()
File overrides the existing files
['extra_file.txt']
Añadir archivos a un ZIP
Debes abrir el ZIP en modo append (a) para añadir archivos al ZIP. No sobrescribe los existentes.
Veamos un ejemplo.
## Appending files to zip
import zipfile
def main():
file_name = 'sample_file.zip'
# opening the 'Zip' in writing mode
with zipfile.ZipFile(file_name, 'a') as file:
# append mode adds files to the 'Zip'
# you have to create the files which you have to add to the 'Zip'
file.write('READ ME.txt')
file.write('even_odd.py')
print('Files added to the Zip')
# opening the 'Zip' in reading mode to check
with zipfile.ZipFile(file_name, 'r') as file:
print(file.namelist())
if __name__ == '__main__': main()
Files added to the Zip
['extra_file.txt', 'READ ME.txt', 'even_odd.py']
Hasta ahora has aprendido a manejar archivos ZIP. Ya puedes abrir, escribir, añadir, extraer, crear, etc. ZIPs. Ahora vas a escribir un programa sencillo.
¿De qué va?
Extraer múltiples sub-ZIPs con contraseña usando bucles y zipfile
- Tienes un ZIP que contiene subarchivos ZIP anidados. Cada uno tiene como contraseña su propio nombre. El reto es descomprimir todos los ZIP hasta llegar al final.
Pasos para resolver el problema
- Extrae el archivo padre usando su nombre como contraseña.
- Obtén el nombre del primer hijo con el método namelist(). Guárdalo en una variable.
-
Ejecuta un bucle infinito.
-
Comprueba si el archivo es ZIP con is_zipfile(). Si lo es, haz lo siguiente.
-
Abre el ZIP con la variable name.
-
Obtén la contraseña del ZIP a partir de la variable name.
-
Extrae el ZIP.
-
Obtén y guarda el nombre del siguiente ZIP en la variable name.
-
-
si no,
- rompe el bucle con break.
-
He creado el procedimiento anterior. Si quieres, puedes adaptarlo según cómo estén organizados tus archivos.
## Solution
import zipfile
def main():
# storing the parent name
parent_file_name = '000.zip'
with zipfile.ZipFile(parent_file_name, 'r') as parent_file:
# extracting the parent file
pswd = bytes(parent_file_name.split('.')[0], 'utf-8')
parent_file.extractall(pwd = pswd)
# getting the first child
next_zip_name = parent_file.namelist()[0]
# looping through the sub zips infinite times until you don't encouter a 'Zip' file
while True:
if zipfile.is_zipfile(next_zip_name):
# opening the zip
with zipfile.ZipFile(next_zip_name, 'r') as child_file:
# getting password from the zip name
pswd = bytes(next_zip_name.split('.')[0], 'utf-8')
# extracting the zip
child_file.extractall(pwd = pswd)
# getting the child zip name
next_zip_name = child_file.namelist()[0]
else:
break
if __name__ == '__main__': main()
Tras ejecutar el programa, verás que todos los sub-ZIP se han extraído en el directorio actual.
Nota final
¡Enhorabuena por completar el tutorial!
Espero que te haya gustado. Este artículo te será muy útil cuando trabajes con archivos ZIP. Ahora ya sabes trabajar con ZIP.
Si tienes alguna duda sobre el artículo, déjala en los comentarios. Te responderé lo antes posible.
De nuevo, si eres nuevo en Python, haz el curso gratuito de DataCamp Intro to Python for Data Science para aprender el lenguaje Python o lee la documentación oficial de Python.
¡Feliz programación!