Como automatizei a extração de 10 anos de dados do IBGE com Python
Extraindo tabelas de PDFs complexos da PNAD usando pdfplumber, pandas e um pouco de paciência.
Como automatizei a extração de 10 anos de dados do IBGE com Python
Extraindo tabelas de PDFs complexos da PNAD usando pdfplumber, pandas e um pouco de paciência.

Tudo começou com uma mensagem simples: "Amor, você pode me ajudar a pegar uns dados do IBGE para um trabalho?"
Minha esposa estava na faculdade e precisava dos microdados da PNAD — Pesquisa Nacional por Amostra de Domicílios para analisar a evolução da distribuição de renda no Brasil ao longo de uma década. O problema? Ela precisava especificamente das tabelas 4.6 (pessoas por classes de rendimento) e 4.22 (a mesma segmentação por sexo), para cada ano de 2002 a 2013.

Se você já tentou extrair dados de PDFs do IBGE, sabe que não é simplesmente "copiar e colar". As tabelas são extensas, o layout é preservado, e fazer isso manualmente para 10 anos significaria dias de trabalho tedioso — sem garantia de que os números sairiam corretos.
Respirei fundo, olhei para o terminal e pensei: "Tem que ter um jeito melhor."
O desafio: PDFs que não colaboram

Os PDFs da PNAD são documentos digitalizados com layout publishing — ou seja, as tabelas foram montadas visualmente, não como estruturas de dados. Cada célula, cada linha, cada número está posicionado em coordenadas absolutas na página. Não existe uma tag <table> escondida ali.
Sem OCR, sem metadados. Apenas palavras flutuando em posições precisas (X, Y) no espaço da página.
Além disso, cada PDF tem centenas de páginas. As tabelas 4.6 e 4.22 estão enterradas no meio do documento, entre outras dezenas de tabelas. Era preciso:
-
Baixar automaticamente cada PDF do catálogo online do IBGE.
-
Localizar as páginas certas dentro de cada arquivo.
-
Extrair os dados de tabelas cujo único "formato" é a posição espacial das palavras.
-
Organizar tudo em colunas padronizadas: ano, situação, classes de rendimento, pessoas (total/homens/mulheres) e rendimento médio.
-
Exportar para algo utilizável — no caso, planilhas Excel.
Fazer isso manualmente? Improvável. Automatizar? Desafio aceito.
A solução: um pipeline em 3 atos

O resultado foi um extrator escrito em Python 3.12, organizado em torno de três etapas bem definidas, cada uma com seu próprio componente:
1. Coleta — IBGEClient + PnadScraper
O primeiro passo era obter os PDFs. O IBGEClient acessa o catálogo online do IBGE via requests + BeautifulSoup, localiza os links de todos os PDFs da PNAD no período desejado e baixa apenas aqueles ainda não presentes no disco. O PnadScraper orquestra essa etapa, garantindo que o download seja feito de forma inteligente (pulando arquivos já existentes).
2. Processamento — PnadProcessor (o coração do projeto)
Aqui mora a mágica — e a complexidade. O PnadProcessor abre cada PDF com a biblioteca pdfplumber e faz o seguinte:
-
Localiza as tabelas: Varre página por página procurando pelo texto "Tabela 4.6" e "Tabela 4.22" usando regex. Uma vez encontrada a primeira página da tabela, continua capturando páginas seguintes até encontrar outra tabela — isso porque cada tabela pode ocupar várias páginas.
-
Extrai por coordenadas espaciais: Em vez de tentar ler a tabela como um bloco (o que pdfplumber tenta fazer com extract_table()), a abordagem aqui é mais artesanal: extrai todas as palavras das páginas da tabela com suas coordenadas (x0, x1, y0) e depois as reagrupa em linhas.
-
Identifica o cabeçalho: Procura a linha que contém "Total", "Homens" e "Mulheres" — o cabeçalho clássico dessas tabelas.
-
Determina limites de colunas: Usa a posição X das palavras do cabeçalho para inferir onde cada coluna começa e termina, expandindo os limites para cobrir os intervalos entre palavras.
-
Atribui valores às colunas: Cada palavra na linha de dados é atribuída à coluna correta com base em sua coordenada X. Palavras que caem fora dos limites conhecidos são tratadas como parte do rótulo da linha.
# Trecho ilustrativo da lógica de atribuição às colunas
for w in sorted(line_words, key=lambda x: x["x0"]):
x0 = w["x0"]
text = w["text"]
assigned = False
for i, (left, right, _) in enumerate(col_boundaries):
if left <= x0 <= right:
# Palavra pertence a esta coluna
values[i] = (values[i] + " " + text).strip()
assigned = True
break
if not assigned:
label_parts.append(text) # Fora das colunas: é rótulo de linha
O resultado é um pandas.DataFrame perfeitamente estruturado, com as colunas padronizadas do projeto.
Exportação — ExcelWriter
Por fim, o ExcelWriter recebe os dicionários de DataFrames e os persiste em arquivos .xlsx usando pandas + openpyxl. São gerados:
-
Um arquivo consolidado (pnad_tabelas_4_6_e_4_22.xlsx) com abas separadas por ano e tabela.
-
Arquivos individuais (pnad{ano}{tabela}.xlsx) para cada ano/tabela — ao todo, 20 arquivos.
Arquitetura do sistema
Visualmente, o fluxo é simples e linear:
main()
├── PnadScraper.collect_and_download()
│ └── IBGEClient (catálogo → links → download)
├── PnadProcessor.process_all()
│ └── PnadProcessor.process_pdf() (para cada PDF)
│ ├── _find_table_pages() # Localiza por regex
│ └── _extract_table_from_words() # Extrai por coordenadas
└── ExcelWriter.save_all()
└── pd.ExcelWriter() + df.to_excel()
Cada componente tem uma responsabilidade única e testável — uma arquitetura que facilitou muito a depuração e a evolução do código.
Resultados
O pipeline foi executado e processou com sucesso 10 dos 11 anos do período:
| Anos | Tabela 4.6 | Tabela 4.22 |
|------|:----------:|:-----------:|
| 2002 | ✅ | ✅ |
| 2004 | ✅ | ✅ |
| 2005 | ✅ | ✅ |
| 2006 | ✅ | ✅ |
| 2007 | ✅ | ✅ |
| 2008 | ✅ | ✅ |
| 2009 | ✅ | ✅ |
| 2011 | ✅ | ✅ |
| 2012 | ✅ | ✅ |
| 2013 | ✅ | ✅ |
Nota: O ano de 2010 não é contemplado pois a PNAD não foi a campo naquele ano (Censo Demográfico).
Cada planilha gerada segue uma estrutura padronizada:
| Coluna | Descrição |
|--------|-----------|
| Ano | Ano da pesquisa |
| Situação / Classes de rendimento | Descrição da linha (ex.: "Total", "Urbana", "Até 1/2 salário mínimo") |
| Pessoas - Total / Homens / Mulheres | Quantidade de pessoas |
| Rendimento (R$) - Total / Homens / Mulheres | Rendimento médio em reais |
Total gerado: 1 arquivo consolidado + 20 arquivos individuais (2 tabelas × 10 anos).
A limitação honesta: o ano 2003
Nem tudo saiu perfeito. O PDF de 2003 utiliza uma codificação proprietária de fonte (CJK / fonte embedada não padrão) que impede a extração de texto por bibliotecas como pdfplumber. As páginas retornam texto vazio, e sem texto não é possível localizar nem extrair as tabelas.
É o tipo de problema que foge do escopo de uma abordagem puramente baseada em layout. Uma solução possível seria utilizar OCR — com pytesseract + pdf2image, por exemplo — para processar esse PDF específico. Fica como melhoria futura.
O que aprendi
Este projeto, que começou como um favor para a minha esposa, acabou se tornando um estudo fascinante sobre:
-
Extração de dados de PDFs com layout complexo — pdfplumber é uma ferramenta poderosa quando você entende o modelo de dados subjacente (palavras com coordenadas).
-
Parsing por coordenadas espaciais — uma técnica útil sempre que você precisa extrair informações de documentos cujo layout é consistente mas não há marcação semântica.
-
Engenharia de pipeline — separar responsabilidades em download, processamento e exportação tornou o código muito mais fácil de manter e evoluir.
-
Resiliência com falhas conhecidas — documentar a limitação do ano 2003 foi tão importante quanto celebrar os 10 anos processados.
E, acima de tudo, aprendi que um problema real e urgente (o prazo do trabalho da faculdade) é o melhor combustível para um projeto de código.
O trabalho da minha esposa? Foi entregue no prazo, com dados limpos e organizados. E o código que nasceu dessa necessidade está disponível como referência para quem mais precisar extrair dados dos PDFs da PNAD.
Para saber mais
O código completo do projeto está aberto no GitHub. Se você precisa trabalhar com dados da PNAD, enfrenta PDFs complexos no seu dia a dia, ou simplesmente quer ver a implementação detalhada da lógica de extração por coordenadas, fique à vontade para explorar, usar e contribuir.
Link do projeto: https://github.com/nandodevs/extracao_pnad_ibge
Tecnologias utilizadas:
-
Python 3.12
-
pdfplumber — extração tabular por coordenadas
-
pandas + openpyxl — manipulação e exportação de dados
-
requests + BeautifulSoup — scraping do catálogo do IBGE
-
lxml + html5lib — parsing HTML
Contribuições são bem-vindas — especialmente se alguém quiser encarar o desafio do OCR para 2003!
*Este artigo foi baseado no projeto real "Extrator de Dados PNAD — Tabelas 4.6 e 4.22", desenvolvido para automatizar a extração de dados socioeconômicos dos PDFs públicos do IBGE.*
메타데이터
- post_id
- 7cd01e251d2a
- slug
- como-automatizei-a-extração-de-10-anos-de-dados-do-ibge-com-python-7cd01e251d2a
- url
- https://medium.com/@sis_data/como-automatizei-a-extra%C3%A7%C3%A3o-de-10-anos-de-dados-do-ibge-com-python-7cd01e251d2a
- canonical_url
- https://medium.com/@sis_data/como-automatizei-a-extra%C3%A7%C3%A3o-de-10-anos-de-dados-do-ibge-com-python-7cd01e251d2a
- author_url
- https://medium.com/@sis_data
- status
- ok
- fetched_at
- 2026-06-09 15:37:30