---
title: "Compactar e Extrair Arquivos ZIP em Python: zipfile e shutil"
url: "https://python.dev.br/blog/python-zipfile-shutil-comprimir-extrair/"
markdown_url: "https://python.dev.br/blog/python-zipfile-shutil-comprimir-extrair.MD"
description: "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."
date: "2026-07-10"
author: "Equipe Python Brasil"
---

# 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](/blog/python-gerar-pdf-relatorios/), exportar [planilhas Excel](/blog/python-e-excel-openpyxl/) ou produzir [documentos Word](/blog/python-e-word-python-docx/), 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](/blog/python-para-iniciantes-guia-completo/) e o de [manipulação de arquivos](/blog/manipulacao-de-arquivos-python/); 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`:

```python
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:

```python
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:

```python
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](/blog/automatizacao-com-python/) 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:

```python
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](/blog/tratamento-de-erros-python/) 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:

```python
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](/blog/web-scraping-python/)), valide os nomes antes de extrair:

```python
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:

```python
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`:

```python
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:

```python
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):

```python
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](https://dados.gov.br/) — 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:

```bash
pip install pyzipper
```

```python
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](/blog/tratamento-de-erros-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](/blog/python-para-automacao-de-emails/) 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](/vagas/) e o [guia de primeiro emprego como programador Python](/carreira/primeiro-emprego-python/).
