Extraer archivos de un ZIP con Python

extractall descomprime todo en una línea. Pero a veces no hace falta extraer nada: se puede leer un archivo de dentro del ZIP directamente.

Extraer todo

import zipfile

with zipfile.ZipFile("copia.zip") as zf:
    print(zf.namelist())
    zf.extractall("extraido")
salidaextraído: ['a.txt', 'b.txt', 'sub/c.txt']

El with cierra el ZIP al terminar. La versión anterior lo cerraba a mano en un finally, que hace lo mismo con más líneas.

Un solo archivo, sin extraer

with zipfile.ZipFile("copia.zip") as zf:
    print(zf.read("sub/c.txt").decode("utf-8").strip())
salidadentro

read devuelve los bytes de esa entrada, y no hay que tocar el disco. Si es texto, se convierte con decode. Para archivos grandes, zf.open("nombre") los da como un archivo que se puede leer por partes.

Las rutas peligrosas

El nombre de una entrada de un ZIP es texto libre y puede contener ../, de modo que un ZIP preparado intentaría escribir fuera de la carpeta de destino. Es la vulnerabilidad que se conoce como Zip Slip. Lo probamos:

with zipfile.ZipFile("malo.zip", "w") as zf:
    zf.writestr("../../fuera.txt", "te he escrito fuera")

with zipfile.ZipFile("malo.zip") as zf:
    zf.extractall("destino")
salidala entrada se llamaba ../../fuera.txt y acabó en: ['destino/fuera.txt'] | ¿existe ../fuera.txt? False

extractall de Python se defiende solo: quita los .. y las barras iniciales, y el archivo acaba dentro de destino. Es una diferencia importante con otros lenguajes, donde la comprobación la tienes que escribir tú. Ojo: si en lugar de extractall escribes los archivos a mano con zf.read y una ruta construida con el nombre, esa protección no existe.

Si no es un ZIP

salidaBadZipFile: File is not a zip file

Archivo dañado, descarga a medias o simplemente otro formato con la extensión cambiada. Se captura con except zipfile.BadZipFile.

El ejemplo anterior pasaba una contraseña con pwd=. zipfile solo entiende el cifrado antiguo de ZIP, que es débil; los ZIP con cifrado AES, los que crean 7-Zip o WinRAR, no los abre. Para esos hace falta un paquete como pyzipper.

Más información en inglés: Extract files from a ZIP in Python.

Todo el código se ejecutó con Python 3.14 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.

Deja un comentario