Crear archivos ZIP con Python es útil para generar copias de seguridad, agrupar informes, comprimir carpetas y preparar paquetes para envío. La biblioteca estándar incluye el módulo zipfile, por lo que no necesitas instalar dependencias externas para trabajar con el formato ZIP.
Para ampliar conceptos, consulta las guías sobre archivos en Python, función zip(), manejo de excepciones, logging y automatización. Como referencias externas, revisa la documentación oficial de zipfile y la documentación oficial de pathlib.
Crear un ZIP con un archivo
from zipfile import ZipFile, ZIP_DEFLATED
with ZipFile("documentos.zip", "w", compression=ZIP_DEFLATED) as archivo_zip:
archivo_zip.write("informe.pdf")
El modo w crea un archivo nuevo y reemplaza uno existente con el mismo nombre. ZIP_DEFLATED activa compresión mediante Deflate.
Controlar el nombre interno
Sin indicar otra cosa, write() puede guardar la ruta proporcionada. Usa arcname para definir el nombre dentro del ZIP:
archivo_zip.write(
"salida/informe.pdf",
arcname="informe.pdf",
)
Esto evita incluir carpetas locales que no deberían aparecer para el destinatario.
Añadir varios archivos
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
archivos = [
Path("informe.pdf"),
Path("resumen.csv"),
Path("grafico.png"),
]
with ZipFile("entrega.zip", "w", compression=ZIP_DEFLATED) as archivo_zip:
for ruta in archivos:
archivo_zip.write(ruta, arcname=ruta.name)
Antes de comprimir, valida que cada ruta exista y sea un archivo.
Comprimir una carpeta completa
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
origen = Path("proyecto")
destino = Path("proyecto.zip")
with ZipFile(destino, "w", compression=ZIP_DEFLATED) as archivo_zip:
for ruta in origen.rglob("*"):
if ruta.is_file():
nombre_interno = ruta.relative_to(origen)
archivo_zip.write(ruta, arcname=nombre_interno)
relative_to() conserva la estructura interna sin incluir el camino completo del ordenador.
Excluir archivos innecesarios
IGNORAR_CARPETAS = {".git", ".venv", "__pycache__"}
IGNORAR_EXTENSIONES = {".pyc", ".log"}
for ruta in origen.rglob("*"):
if not ruta.is_file():
continue
if any(parte in IGNORAR_CARPETAS for parte in ruta.parts):
continue
if ruta.suffix.lower() in IGNORAR_EXTENSIONES:
continue
archivo_zip.write(ruta, arcname=ruta.relative_to(origen))
No incluyas entornos virtuales, repositorios Git, cachés ni secretos salvo que exista una razón clara.
Evitar incluir el ZIP dentro de sí mismo
Si el archivo de destino se crea dentro de la carpeta de origen, rglob() puede encontrarlo mientras se escribe. Guarda el destino fuera de la carpeta o exclúyelo explícitamente:
if ruta.resolve() == destino.resolve():
continue
Añadir archivos a un ZIP existente
with ZipFile("entrega.zip", "a", compression=ZIP_DEFLATED) as archivo_zip:
archivo_zip.write("notas.txt", arcname="notas.txt")
El modo a conserva el contenido anterior. Ten cuidado con nombres duplicados, porque pueden existir varias entradas con el mismo nombre dentro del ZIP.
Escribir contenido sin archivo temporal
from zipfile import ZipFile, ZIP_DEFLATED
with ZipFile("datos.zip", "w", compression=ZIP_DEFLATED) as archivo_zip:
archivo_zip.writestr("estado.txt", "Proceso completado\n")
writestr() resulta útil para informes pequeños, configuraciones generadas o manifiestos.
Crear un CSV dentro del ZIP
import csv
import io
from zipfile import ZipFile, ZIP_DEFLATED
buffer = io.StringIO()
escritor = csv.writer(buffer)
escritor.writerow(["producto", "cantidad"])
escritor.writerow(["teclado", 2])
escritor.writerow(["ratón", 3])
with ZipFile("inventario.zip", "w", compression=ZIP_DEFLATED) as archivo_zip:
archivo_zip.writestr("inventario.csv", buffer.getvalue())
Para archivos grandes, generar todo en memoria puede ser costoso. En esos casos, crea un archivo temporal controlado.
Niveles de compresión
with ZipFile(
"imagenes.zip",
"w",
compression=ZIP_DEFLATED,
compresslevel=6,
) as archivo_zip:
...
Un nivel más alto puede reducir el tamaño, pero requiere más CPU. Fotografías JPEG, vídeos y otros formatos ya comprimidos suelen reducirse poco.
Otros métodos de compresión
zipfile admite métodos según la versión de Python y módulos disponibles, como BZIP2 o LZMA. Para máxima compatibilidad con sistemas comunes, Deflate suele ser la opción más segura.
Guardar directorios vacíos
El recorrido de archivos no añade automáticamente carpetas vacías. Puedes crear una entrada terminada en barra:
archivo_zip.writestr("carpeta-vacia/", "")
Normalmente no es necesario, pero puede ser importante para plantillas de proyecto.
Verificar el contenido creado
from zipfile import ZipFile
with ZipFile("entrega.zip", "r") as archivo_zip:
print(archivo_zip.namelist())
archivo_danado = archivo_zip.testzip()
if archivo_danado is not None:
print(f"Entrada dañada: {archivo_danado}")
testzip() lee los archivos y comprueba sus CRC. Un resultado None indica que no detectó entradas corruptas.
Calcular hashes
Para verificar que el ZIP no cambió durante una transferencia, calcula SHA-256:
import hashlib
from pathlib import Path
def sha256(ruta):
resumen = hashlib.sha256()
with Path(ruta).open("rb") as archivo:
for bloque in iter(lambda: archivo.read(1024 * 1024), b""):
resumen.update(bloque)
return resumen.hexdigest()
Publica el hash por un canal confiable cuando la integridad sea importante.
Crear el ZIP de forma segura
No escribas directamente sobre un archivo final importante. Crea un temporal y reemplaza el destino después de verificarlo:
temporal = destino.with_suffix(".zip.tmp")
# Crear y verificar temporal...
temporal.replace(destino)
Si el proceso falla, el ZIP anterior permanece intacto.
Función reutilizable
from pathlib import Path
from zipfile import ZipFile, ZIP_DEFLATED
def comprimir_carpeta(origen, destino):
origen = Path(origen).resolve()
destino = Path(destino).resolve()
if not origen.is_dir():
raise NotADirectoryError(origen)
destino.parent.mkdir(parents=True, exist_ok=True)
with ZipFile(destino, "w", compression=ZIP_DEFLATED) as archivo_zip:
for ruta in origen.rglob("*"):
if ruta.is_file() and ruta.resolve() != destino:
archivo_zip.write(ruta, arcname=ruta.relative_to(origen))
with ZipFile(destino) as archivo_zip:
if archivo_zip.testzip() is not None:
raise ValueError("El ZIP creado contiene una entrada dañada")
Copias de seguridad
Un ZIP no es por sí solo una estrategia completa de backup. Mantén varias versiones, almacena copias en otro dispositivo o ubicación y prueba restauraciones. No incluyas bases de datos activas sin realizar antes un volcado consistente.
Protección con contraseña
La biblioteca estándar puede leer ciertos ZIP cifrados, pero no ofrece una solución moderna y completa para crear archivos protegidos con cifrado fuerte. Para datos sensibles, utiliza una herramienta especializada y verifica el algoritmo. No confundas compresión con seguridad.
Errores frecuentes
Los fallos comunes son incluir rutas absolutas, comprimir el archivo de destino dentro de sí mismo, sobrescribir sin confirmación, almacenar secretos, asumir que todos los formatos se comprimen mucho y no verificar el resultado. También es frecuente confundir la función incorporada zip() con el módulo zipfile.
Conclusión
zipfile permite crear archivos ZIP portables con archivos, carpetas y contenido generado. Controla siempre arcname, excluye datos innecesarios, verifica con testzip() y escribe primero en un destino temporal cuando el proceso sea crítico. Para copias de seguridad reales, combina compresión con versionado, almacenamiento separado y pruebas de restauración.






