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.

7 min de leitura Equipe Python Brasil

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_DEFLATED quando o objetivo for reduzir tamanho. O padrão ZIP_STORED apenas 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, confira namelist() e rejeite caminhos absolutos e .. (Zip Slip).
  • Prefira shutil para pastas inteiras. Para um único arquivo ou lote controlado, zipfile dá mais controle; para compactar um diretório recursivamente, shutil.make_archive é mais limpo.
  • Trate erros de arquivo. Envolver leitura e escrita em try/except evita 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 com read().

Próximos passos

Para evoluir depois deste tutorial:

  1. Combine com a geração de relatórios — depois de produzir PDFs ou planilhas, empacote o lote em um .zip antes do envio.
  2. Envie por e-mail — conecte a compactação ao envio de e-mails com Python para entregar pacotes de arquivos sem toque manual.
  3. Agende backups — use shutil.make_archive em 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.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português