Compactar e Extrair Arquivos ZIP em Python: zipfile e shutil
Aprenda a compactar e extrair arquivos ZIP em Python com os módulos zipfile e shutil da biblioteca padrão: criar, listar, extrair com segurança, compactar pastas inteiras, ler arquivos sem descompactar e evitar o ataque Zip Slip. Guia prático em português.
Compactar arquivos em um único .zip é uma das tarefas mais corriqueiras em automação de escritório e em pipelines de dados. Depois de gerar um lote de relatórios em PDF, exportar planilhas Excel ou produzir documentos Word, o passo natural é empacotar tudo para enviar por e-mail, fazer um backup ou atender a uma exigência de portal governamental. A boa notícia é que o Python já traz tudo o que você precisa no interpretador, sem instalar nada: os módulos zipfile e shutil, ambos da biblioteca padrão.
Neste tutorial você vai aprender a criar arquivos ZIP, listar o conteúdo, extrair com segurança, compactar pastas inteiras e ler arquivos embutidos sem descompactar — tudo com exemplos práticos voltados para a realidade brasileira. Se você está começando, vale revisar o guia de Python para iniciantes e o de manipulação de arquivos; aqui o foco é um formato específico e recorrente.
Criando o primeiro arquivo ZIP
O módulo zipfile oferece a classe ZipFile, que funciona como um gerenciador de contexto (with). O ponto de atenção mais comum é o parâmetro compression: por padrão, os arquivos entram sem compressão (ZIP_STORED). Para de fato reduzir o tamanho, informe ZIP_DEFLATED:
import zipfile
with zipfile.ZipFile("relatorios.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip:
zip.write("janeiro.pdf")
zip.write("fevereiro.pdf")
zip.write("marco.pdf")
print("Arquivo relatorios.zip criado com sucesso.")
Ao executar o script, surge o arquivo relatorios.zip contendo os três PDFs comprimidos. O ZIP_DEFLATED depende da biblioteca zlib, que já acompanha as distribuições oficiais do Python, então não há nada extra a instalar.
Adicionando arquivos com outro nome interno
Às vezes o nome do arquivo no disco não é o que você quer dentro do ZIP (por exemplo, renomear saida.tmp para relatorio_final.pdf). O segundo argumento de write é o arcname, o nome interno:
with zipfile.ZipFile("pacote.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip:
zip.write("saida.tmp", arcname="relatorio_final.pdf")
zip.write("dados/planilha.xlsx", arcname="planilha.xlsx")
Definir o arcname também é a forma de evitar que o ZIP replique pastas inteiras (dados/...) dentro do pacote, mantendo a estrutura plana.
Criando um arquivo a partir de texto em memória
Se o conteúdo ainda não existe em disco (por exemplo, um CSV montado na hora), use writestr para gravar uma string ou bytes diretamente no ZIP:
import zipfile
linhas = "produto,preco\nCaderno,29.90\nCaneta,2.50\n"
with zipfile.ZipFile("catalogo.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip:
zip.writestr("catalogo.csv", linhas)
Esse padrão é útil em automações que geram o conteúdo dinamicamente e já o empacotam, sem criar arquivos temporários no disco.
Listando e inspecionando o conteúdo
Antes de extrair, convém saber o que há dentro. namelist() devolve os nomes; infolist() traz objetos com tamanho, data e taxa de compressão:
import zipfile
with zipfile.ZipFile("relatorios.zip") as zip:
print("Arquivos:", zip.namelist())
for info in zip.infolist():
original = info.file_size
comprimido = info.compress_size
reducao = (1 - comprimido / original) * 100 if original else 0
print(f"{info.filename}: {original} → {comprimido} bytes ({reducao:.1f}% menor)")
Essa inspeção é especialmente útil para auditar um .zip recebido antes de processá-lo — uma etapa simples de tratamento de erros e segurança.
Extraindo arquivos
A extração também usa ZipFile no modo leitura. extractall() descompacta tudo para uma pasta; extract() pega um único membro:
import zipfile
with zipfile.ZipFile("relatorios.zip") as zip:
zip.extractall("saida/") # descompacta tudo em saida/
zip.extract("janeiro.pdf", "um_arquivo/") # extrai um só
Se a pasta de destino não existir, o Python a cria. Para extrair escolhendo o nome de saída, leia o conteúdo e grave você mesmo (veja a seção sobre leitura sem descompactar, adiante).
Segurança: evitando o ataque Zip Slip
Arquivos .zip podem conter nomes maliciosos como ../../../../etc/cron.d/tarefa ou caminhos absolutos (/etc/...). Esse é o ataque conhecido como Zip Slip. Desde o Python 3.6.2, os métodos extract e extractall normalizam os nomes e evitam escrever fora do diretório de destino. Mesmo assim, ao receber um ZIP de origem não confiável (upload de usuário, anexo de e-mail, dados coletados por scraping), valide os nomes antes de extrair:
import zipfile
from pathlib import Path
def seguro_para_extrair(caminho_zip: str, destino: str) -> bool:
base = Path(destino).resolve()
with zipfile.ZipFile(caminho_zip) as zip:
for nome in zip.namelist():
if nome.startswith(("/", "\\")) or ".." in Path(nome).parts:
return False
# confirma que o caminho final fica dentro de 'destino'
alvo = (base / nome).resolve()
if base not in alvo.parents and alvo != base:
return False
return True
if seguro_para_extrair("upload.zip", "saida/"):
with zipfile.ZipFile("upload.zip") as zip:
zip.extractall("saida/")
else:
print("ZIP recusado: contém caminhos suspeitos.")
Essa verificação é um hábito saudável em qualquer pipeline que aceite arquivos do exterior.
Compactando uma pasta inteira com shutil
Quando o objetivo é zipar uma pasta completa (com subpastas e tudo), o shutil.make_archive é mais prático do que percorrer os arquivos manualmente:
import shutil
caminho = shutil.make_archive(
base_name="backup_2026-07", # nome do arquivo (sem extensão)
format="zip", # também aceita 'tar', 'gztar', 'bztar'
root_dir="projetos", # a pasta a ser compactada
)
print(f"Backup criado em: {caminho}")
O resultado é backup_2026-07.zip. Para descompactar no caminho inverso, use shutil.unpack_archive:
import shutil
shutil.unpack_archive("backup_2026-07.zip", extract_dir="restaurado/")
Essa dupla é ideal para rotinas simples de backup de configurações, exports de sistema e pequenos pacotes de dados — sem precisar de bibliotecas externas.
Compactando apenas arquivos novos (incremental)
Para um backup incremental, combine make_archive com uma checagem de data. O exemplo abaixo zipa apenas arquivos .csv e .pdf modificados hoje:
import zipfile
from datetime import date
from pathlib import Path
hoje = date.today().isoformat()
pasta = Path("relatorios")
arquivos_recentes = [
p for p in pasta.glob("*.*")
if p.suffix in (".csv", ".pdf") and date.fromtimestamp(p.stat().st_mtime) >= date.today()
]
if arquivos_recentes:
with zipfile.ZipFile(f"incremental_{hoje}.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip:
for p in arquivos_recentes:
zip.write(p, arcname=p.name)
print(f"Empacotados {len(arquivos_recentes)} arquivos novos.")
Para algo mais robusto, encapsule a lógica em uma função e agende com cron ou uma tarefa do Windows — a base de muitas automações de rotina.
Lendo arquivos sem descompactar
Uma vantagem do zipfile é poder ler o conteúdo de um membro direto da memória, sem gravar em disco. Use open() (objeto semelhante a arquivo) ou read() (bytes):
import zipfile
import csv
with zipfile.ZipFile("pacote.zip") as zip:
# lê um CSV embutido sem extrair
with zip.open("catalogo.csv") as f:
leitor = csv.DictReader(linha.decode("utf-8") for linha in f)
for registro in leitor:
print(registro["produto"], "→", registro["preco"])
Essa abordagem é perfeita para funções serverless e containers, onde gravar arquivos temporários é indesejável. Também é o caminho para processar dados abertos — muitos conjuntos públicos são distribuídos em .zip, e você pode consumir o conteúdo interno diretamente.
Criando ZIPs com senha (pyzipper)
O zipfile não cria arquivos criptografados — ele só lê ZIPs com a criptografia antiga (ZipCrypto) via setpassword(). Para gerar um .zip protegido por senha com criptografia AES, instale o pyzipper, um fork compatível:
pip install pyzipper
import pyzipper
with pyzipper.AESZipFile("sigiloso.zip", "w", compression=pyzipper.ZIP_DEFLATED) as zip:
zip.setpassword(b"minha-senha-forte")
zip.setencryption(pyzipper.WZ_AES, nbits=256)
zip.write("contrato.pdf")
Use uma senha forte e, em ambientes de produção, carregue-a a partir de uma variável de ambiente ou de um cofre de segredos — nunca deixe a senha escrita no código.
Boas práticas
- Sempre use
ZIP_DEFLATEDquando o objetivo for reduzir tamanho. O padrãoZIP_STOREDapenas empacota, sem compressão. - Pense no
arcname. Definir o nome interno evita reproduzir pastas inteiras dentro do ZIP e permite renomear na origem. - Valide origens externas. Antes de
extractall()em zips de upload ou anexo, confiranamelist()e rejeite caminhos absolutos e..(Zip Slip). - Prefira
shutilpara pastas inteiras. Para um único arquivo ou lote controlado,zipfiledá mais controle; para compactar um diretório recursivamente,shutil.make_archiveé mais limpo. - Trate erros de arquivo. Envolver leitura e escrita em
try/exceptevita que um arquivo travado interrompa um lote — veja as práticas do nosso guia de tratamento de erros em Python. - Atenção ao tamanho em memória. Para arquivos muito grandes, prefira iterar com
open()em vez de carregar tudo comread().
Próximos passos
Para evoluir depois deste tutorial:
- Combine com a geração de relatórios — depois de produzir PDFs ou planilhas, empacote o lote em um
.zipantes do envio. - Envie por e-mail — conecte a compactação ao envio de e-mails com Python para entregar pacotes de arquivos sem toque manual.
- Agende backups — use
shutil.make_archiveem uma tarefa agendada para fazer cópias periódicas das suas configurações e dados.
Compactar e extrair arquivos ZIP é uma habilidade pequena que aparece em dezenas de automações reais. Comece com um ZipFile simples, valide o resultado e expanda para pastas inteiras, leitura em memória e proteção por senha. Para acompanhar as oportunidades do mercado de desenvolvimento Python no Brasil, confira nossas vagas de Python e o guia de primeiro emprego como programador Python.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português