Extrair tabelas de PDF em Python
Extrair tabelas de documentos PDF é uma das tarefas mais comuns em pipelines de processamento de documentos. Seja para tirar dados financeiros de relatórios anuais, raspar catálogos de produtos ou alimentar dados estruturados em um LLM, uma extração de tabelas confiável é indispensável. Este guia cobre tudo o que você precisa saber para fazer isso em Python, de one-liners rápidos a fluxos de produção para tabelas que atravessam várias páginas.
Motor de detecção
O PDF Oxide usa o pipeline universal de detecção de tabelas bordas → snap/merge → interseções → células → grupos — a mesma abordagem adotada por Tabula, pdfplumber e PyMuPDF, implementada em Rust puro.
Recursos de detecção:
- Baseado em interseções — encontra cruzamentos de linhas H×V, constrói células a partir dos retângulos de quatro cantos e agrupa em tabelas via union-find.
- Grade estendida — quando as linhas horizontais e verticais estão em regiões diferentes da página, monta uma grade virtual com o produto cartesiano de todas as coordenadas.
- Detecção de texto com consciência de colunas — segmenta layouts de duas colunas pelo histograma de projeção em X e executa detecção por texto isolada em cada coluna.
- Tabelas de texto delimitadas por H-rules — detecta tabelas limitadas por linhas horizontais sem linhas verticais (muito comum em artigos acadêmicos).
- Detecção híbrida de linhas — infere os limites das linhas a partir das posições Y do texto quando só há bordas verticais (itens de nota fiscal).
- Reconstrução de linhas pontilhadas/tracejadas — junta segmentos curtos em bordas contínuas.
- Divisão por separadores de seção — parte formulários multiseção em divisores horizontais que ocupam toda a largura da página.
- Filtro por cobertura de bordas — descarta bordas órfãs que não participam de nenhuma grade candidata.
Configuração
TableDetectionConfig expõe os seguintes parâmetros ajustáveis:
| Campo | Padrão | Descrição |
|---|---|---|
horizontal_strategy |
"lines_strict" |
"lines_strict", "lines", "text" ou "explicit" |
vertical_strategy |
"lines_strict" |
Mesmo vocabulário |
v_split_gap |
20.0 pt |
Espaço entre linhas verticais que provoca a divisão em tabelas separadas (era fixo em 4 pt antes da v0.3.20) |
snap_tolerance |
3.0 pt |
Tolerância ao unir bordas próximas |
text_tolerance |
3.0 pt |
Tolerância ao unir linhas de texto |
Mudança de comportamento
A partir da v0.3.20, a estratégia padrão do extract_tables() em Python é Both (detecta via linhas e via texto). Páginas que dependiam do antigo padrão só-texto precisam passar horizontal_strategy="text" e vertical_strategy="text" explicitamente.
O binding Python agora lê corretamente vertical_strategy do dicionário table_settings — antes esse valor era ignorado silenciosamente.
Renderização
As tabelas extraídas são emitidas com alinhamento de colunas preenchido por espaços (no lugar dos caracteres ASCII de desenho das versões anteriores). Colunas de moeda e números são alinhadas à direita automaticamente. Prefixos de numeração de formulário ("1 Apr 11" → "Apr 11") e células decorativas de traço/sublinhado ("------") são removidos no momento da renderização.
Extraia os dados de tabela de um PDF pela conversão a Markdown:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("invoice.pdf")
md = doc.to_markdown(0, detect_headings=True)
print(md)
# A saída inclui tabelas no formato GFM:
# | Item | Qty | Price |
# |------|-----|-------|
# | Widget | 10 | $9.99 |
WASM
import { WasmPdfDocument } from "pdf-oxide-wasm";
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdown(0);
console.log(md);
// A saída inclui tabelas no formato GFM:
// | Item | Qty | Price |
// |------|-----|-------|
// | Widget | 10 | $9.99 |
doc.free();
Rust
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("invoice.pdf")?;
let md = doc.to_markdown(0, true)?;
println!("{}", md);
Go
package main
import (
"fmt"
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
doc, err := pdfoxide.Open("invoice.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
md, err := doc.ToMarkdown(0)
if err != nil { log.Fatal(err) }
fmt.Println(md)
}
C#
using PdfOxide;
using var doc = PdfDocument.Open("invoice.pdf");
Console.WriteLine(doc.ToMarkdown(0));
O PDF Oxide detecta layouts tabulares pela análise espacial de blocos de texto alinhados e emite tabelas em GitHub Flavored Markdown.
Por que extrair tabelas de PDF é difícil
Se você já tentou copiar uma tabela de um PDF e colar em uma planilha, já viu o resultado: normalmente, uma bagunça. Isso não é bug do seu leitor de PDF — reflete uma limitação fundamental do próprio formato PDF.
Os PDFs não têm o conceito de “tabela”. Diferente do HTML, que usa <table>, <tr> e <td> para definir a estrutura tabular, um arquivo PDF guarda apenas instruções de desenho: coloque este glifo na coordenada (x, y), trace uma linha do ponto A ao ponto B. Não existe uma camada semântica que diga “esses caracteres pertencem à célula da linha 3, coluna 2”. Toda biblioteca de extração precisa reconstruir essa estrutura analisando as posições espaciais do texto e das linhas na página.
Essa reconstrução é difícil por vários motivos:
-
Tabelas com e sem borda. Quando há linhas de grade visíveis, as ferramentas podem usá-las como limites de célula. Tabelas sem borda — comuns em demonstrativos financeiros, relatórios governamentais e artigos acadêmicos — não têm linha nenhuma. A biblioteca precisa inferir os limites das colunas só pelos espaços em branco entre blocos de texto, o que falha com frequência quando as colunas têm larguras variáveis ou os números estão alinhados à direita.
-
Células mescladas e cabeçalhos que abrangem colunas. Uma célula de cabeçalho que atravessa três colunas aparece como um único bloco de texto largo. Sem as linhas de grade para delimitá-la, o parser não tem como saber com segurança quais colunas o cabeçalho cobre. Algumas bibliotecas lidam bem com isso; muitas produzem em silêncio uma saída distorcida.
-
Conteúdo de célula em várias linhas. Quando a célula tem um parágrafo que se quebra em várias linhas, um parser ingênuo por linha trata cada quebra como uma linha distinta. Reagrupar tudo numa única célula exige entender a extensão vertical de cada linha da tabela.
-
Tabelas em várias páginas. Tabelas grandes costumam se estender por duas ou mais páginas. O cabeçalho pode ou não se repetir a cada página, e rodapés, marcas d’água ou números de página podem aparecer entre as linhas. Juntar esses pedaços em uma tabela coerente exige uma lógica consciente da paginação.
-
Texto rotacionado e layouts fora do padrão. Alguns PDFs rotacionam o texto dos cabeçalhos das colunas ou colocam tabelas dentro de layouts de várias colunas. Esses casos rompem as suposições de leitura da esquerda para a direita e de cima para baixo que a maioria dos parsers assume.
Entender esses desafios ajuda você a escolher a ferramenta certa para seus documentos. Para tabelas alinhadas simples — a maioria das notas fiscais, confirmações de pedido e relatórios simples — uma abordagem rápida de análise espacial como a do PDF Oxide resolve muito bem. Para documentos com mesclagens complexas, layouts sem borda ou formatação incomum, pode ser preciso uma biblioteca com heurísticas mais sofisticadas.
Extração de tabelas: PDF Oxide vs outras bibliotecas
Escolher uma biblioteca para extrair tabelas de PDF em Python depende dos seus documentos, dos requisitos de desempenho e do formato de saída necessário. Veja como as principais opções se comparam:
| Biblioteca | Detecção de tabelas | Tabelas com borda | Tabelas sem borda | Formato de saída | Velocidade |
|---|---|---|---|---|---|
| PDF Oxide | Integrada | Sim | Básica | Markdown/HTML | 0,8 ms |
| pdfplumber | Integrada | Sim | Avançada | Listas Python | 23,2 ms |
| Camelot | Integrada | Sim | Sim (lattice/stream) | DataFrames | ~50 ms+ |
| PyMuPDF | Básica (v1.23+) | Sim | Limitada | DataFrames | 4,6 ms |
| pypdf | Não | Não | Não | N/D | N/D |
| tabula-py | Integrada | Sim | Sim | DataFrames | ~100 ms+ (Java) |
PDF Oxide é disparado a opção mais rápida. Detecta tabelas pela análise espacial de blocos de texto alinhados e produz tabelas limpas em GitHub Flavored Markdown. Com 0,8 ms de tempo médio de extração, é 29× mais rápido que o pdfplumber e mais de 100× mais rápido que o tabula-py. Lida bem com tabelas com borda e com tabelas sem borda simples e alinhadas. Em pipelines de LLM onde você já precisa de Markdown, é a escolha natural.
pdfplumber tem a detecção de tabelas sem borda mais madura. O método find_tables() oferece estratégias configuráveis para detectar linhas e colunas com base no alinhamento do texto, e lida com células mescladas e conteúdo multilinha melhor que a maioria das alternativas. O custo é a velocidade: 23,2 ms por página deixam o processamento em lote bem mais lento.
Camelot oferece dois modos de detecção — lattice (para tabelas com borda) e stream (para tabelas sem borda). Produz DataFrames do pandas direto, o que é conveniente para fluxos de análise de dados. Em compensação, depende de Ghostscript e OpenCV, o que torna a instalação mais pesada, e a velocidade é a mais baixa entre as opções em Python puro.
PyMuPDF (fitz) adicionou uma extração básica de tabelas na versão 1.23. É rápido (4,6 ms) e funciona bem com tabelas simples com borda, mas o suporte a tabelas sem borda é limitado em comparação com pdfplumber ou Camelot.
pypdf não tem capacidade alguma de detecção de tabelas. Extrai texto bruto, então você precisaria escrever seu próprio parser para reconstruir a estrutura tabular.
tabula-py é um wrapper em Python para a biblioteca Tabula, em Java. Oferece boa detecção tanto em tabelas com quanto sem borda, mas exige um runtime Java e é a opção mais lenta por causa do overhead de inicialização da JVM. Ela se encaixa melhor em extrações pontuais do que em pipelines de alta vazão.
Para a maioria dos casos de uso em produção, a recomendação é usar o PDF Oxide como extrator principal pela velocidade e simplicidade e cair para o pdfplumber no subconjunto de documentos com tabelas complexas que pedem heurísticas mais sofisticadas.
Instalação
pip install pdf_oxide
Extração básica de tabelas
Como tabelas Markdown
A forma mais simples: converter a página para Markdown, que já inclui tabelas em sintaxe GFM:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
for i in range(doc.page_count()):
md = doc.to_markdown(i, detect_headings=True)
if "|" in md: # A página contém uma tabela
print(f"--- Página {i + 1} ---")
print(md)
WASM
const doc = new WasmPdfDocument(bytes);
for (let i = 0; i < doc.pageCount(); i++) {
const md = doc.toMarkdown(i);
if (md.includes("|")) { // A página contém uma tabela
console.log(`--- Página ${i + 1} ---`);
console.log(md);
}
}
doc.free();
Rust
let mut doc = PdfDocument::open("report.pdf")?;
for i in 0..doc.page_count()? {
let md = doc.to_markdown(i, true)?;
if md.contains("|") {
println!("--- Página {} ---", i + 1);
println!("{}", md);
}
}
Go
doc, _ := pdfoxide.Open("report.pdf")
defer doc.Close()
n, _ := doc.PageCount()
for i := 0; i < n; i++ {
md, _ := doc.ToMarkdown(i)
if strings.Contains(md, "|") {
fmt.Printf("--- Página %d ---\n%s\n", i+1, md)
}
}
C#
using var doc = PdfDocument.Open("report.pdf");
for (int i = 0; i < doc.PageCount; i++)
{
var md = doc.ToMarkdown(i);
if (md.Contains("|"))
Console.WriteLine($"--- Página {i + 1} ---\n{md}");
}
Extração estruturada de tabelas (v0.3.34)
Para acesso tipado a linhas e caixas delimitadoras sem passar pelo parsing de Markdown, chame ExtractTables(pageIndex) (Go, C#) ou extract_tables(page) (Python, Rust). Cada tabela expõe células estruturadas, então você pode mandar o resultado direto para um banco de dados ou DataFrame, sem regex.
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("invoice.pdf")
for table in doc.extract_tables(0):
for row in table.rows:
print(row)
Rust
let mut doc = PdfDocument::open("invoice.pdf")?;
for table in doc.extract_tables(0)? {
for row in &table.rows {
println!("{:?}", row);
}
}
Go
doc, _ := pdfoxide.Open("invoice.pdf")
defer doc.Close()
tables, _ := doc.ExtractTables(0)
for _, t := range tables {
for _, row := range t.Rows {
fmt.Println(row)
}
}
C#
using var doc = PdfDocument.Open("invoice.pdf");
foreach (var table in doc.ExtractTables(0))
foreach (var row in table.Rows)
Console.WriteLine(string.Join(" | ", row));
Converter tabelas Markdown em linhas
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("invoice.pdf")
md = doc.to_markdown(0)
# Extrair as linhas de tabela do Markdown
rows = []
for line in md.split("\n"):
line = line.strip()
if line.startswith("|") and not line.startswith("|--"):
cells = [cell.strip() for cell in line.split("|")[1:-1]]
rows.append(cells)
header = rows[0] if rows else []
data = rows[1:] if len(rows) > 1 else []
print(f"Colunas: {header}")
for row in data:
print(row)
WASM
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdown(0);
const rows = [];
for (const line of md.split("\n")) {
const trimmed = line.trim();
if (trimmed.startsWith("|") && !trimmed.startsWith("|--")) {
const cells = trimmed.split("|").slice(1, -1).map(c => c.trim());
rows.push(cells);
}
}
const header = rows[0] || [];
const data = rows.slice(1);
console.log("Colunas:", header);
data.forEach(row => console.log(row));
doc.free();
Rust
let mut doc = PdfDocument::open("invoice.pdf")?;
let md = doc.to_markdown(0, false)?;
let rows: Vec<Vec<String>> = md.lines()
.map(|l| l.trim())
.filter(|l| l.starts_with('|') && !l.starts_with("|--"))
.map(|l| l.split('|').skip(1).map(|c| c.trim().to_string())
.take_while(|c| !c.is_empty()).collect())
.collect();
if let Some(header) = rows.first() {
println!("Colunas: {:?}", header);
for row in &rows[1..] {
println!("{:?}", row);
}
}
Exportar para CSV
import csv
from pdf_oxide import PdfDocument
doc = PdfDocument("invoice.pdf")
md = doc.to_markdown(0)
rows = []
for line in md.split("\n"):
line = line.strip()
if line.startswith("|") and not line.startswith("|--"):
cells = [cell.strip() for cell in line.split("|")[1:-1]]
rows.append(cells)
with open("table.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(rows)
Exportar para DataFrame do Pandas
import pandas as pd
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
md = doc.to_markdown(0)
rows = []
for line in md.split("\n"):
line = line.strip()
if line.startswith("|") and not line.startswith("|--"):
cells = [cell.strip() for cell in line.split("|")[1:-1]]
rows.append(cells)
if rows:
df = pd.DataFrame(rows[1:], columns=rows[0])
print(df)
Usar posições de caractere para parsing de tabela sob medida
Se você precisa de controle fino, combine a extração em nível de caractere com análise espacial:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("financial.pdf")
chars = doc.extract_chars(0)
# Agrupar caracteres por posição Y (linhas)
rows = {}
for ch in chars:
row_key = round(ch.y / 2) * 2 # Encaixar na grade de 2 pt
rows.setdefault(row_key, []).append(ch)
# Ordenar linhas de cima para baixo e caracteres da esquerda para a direita
for y in sorted(rows.keys(), reverse=True):
line_chars = sorted(rows[y], key=lambda c: c.x)
text = "".join(c.char for c in line_chars)
print(text)
WASM
const doc = new WasmPdfDocument(bytes);
const chars = doc.extractChars(0);
// Agrupar caracteres por posição Y (linhas)
const rows = new Map();
for (const ch of chars) {
const rowKey = Math.round(ch.y / 2) * 2; // Encaixar na grade de 2 pt
if (!rows.has(rowKey)) rows.set(rowKey, []);
rows.get(rowKey).push(ch);
}
// Ordenar linhas de cima para baixo e caracteres da esquerda para a direita
const sortedKeys = [...rows.keys()].sort((a, b) => b - a);
for (const y of sortedKeys) {
const lineChars = rows.get(y).sort((a, b) => a.x - b.x);
const text = lineChars.map(c => c.char).join("");
console.log(text);
}
doc.free();
Rust
use std::collections::BTreeMap;
let mut doc = PdfDocument::open("financial.pdf")?;
let chars = doc.extract_chars(0)?;
let mut rows: BTreeMap<i32, Vec<_>> = BTreeMap::new();
for ch in &chars {
let row_key = ((ch.y / 2.0).round() * 2.0) as i32;
rows.entry(row_key).or_default().push(ch);
}
for (_, line_chars) in rows.iter().rev() {
let mut sorted = line_chars.clone();
sorted.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap());
let text: String = sorted.iter().map(|c| c.char).collect();
println!("{}", text);
}
Go
doc, _ := pdfoxide.Open("financial.pdf")
defer doc.Close()
chars, _ := doc.ExtractChars(0)
rows := map[int][]pdfoxide.Char{}
for _, ch := range chars {
key := int(math.Round(float64(ch.Y)/2) * 2)
rows[key] = append(rows[key], ch)
}
keys := make([]int, 0, len(rows))
for k := range rows { keys = append(keys, k) }
sort.Sort(sort.Reverse(sort.IntSlice(keys)))
for _, y := range keys {
line := rows[y]
sort.Slice(line, func(i, j int) bool { return line[i].X < line[j].X })
var b strings.Builder
for _, c := range line { b.WriteString(c.Char) }
fmt.Println(b.String())
}
C#
using var doc = PdfDocument.Open("financial.pdf");
var chars = doc.ExtractChars(0);
var rows = chars
.GroupBy(c => (int)(Math.Round(c.Y / 2) * 2))
.OrderByDescending(g => g.Key);
foreach (var row in rows)
{
var line = string.Concat(row.OrderBy(c => c.X).Select(c => c.Char));
Console.WriteLine(line);
}
Extrair tabelas para Markdown
Markdown é o formato de saída ideal quando você vai alimentar o conteúdo de um PDF a um grande modelo de linguagem, montar um pipeline de RAG ou guardar os dados extraídos em um formato legível tanto para pessoas quanto para máquinas. O PDF Oxide emite tabelas nativamente em GitHub Flavored Markdown (GFM), então não é preciso uma conversão extra.
from pdf_oxide import PdfDocument
doc = PdfDocument("quarterly-report.pdf")
# Extrair todas as tabelas de todas as páginas como Markdown
all_tables = []
for i in range(doc.page_count()):
md = doc.to_markdown(i, detect_headings=True)
# Dividir o markdown em seções e identificar blocos de tabela
in_table = False
current_table = []
for line in md.split("\n"):
if line.strip().startswith("|"):
in_table = True
current_table.append(line)
else:
if in_table and current_table:
all_tables.append("\n".join(current_table))
current_table = []
in_table = False
if current_table:
all_tables.append("\n".join(current_table))
print(f"Foram encontradas {len(all_tables)} tabelas")
for idx, table in enumerate(all_tables):
print(f"\n--- Tabela {idx + 1} ---")
print(table)
A saída GFM é diretamente compatível com prompts de LLM. Você pode passá-la sem alterações para uma chamada da API da OpenAI ou da Anthropic e o modelo entende a estrutura tabular sem nenhuma formatação extra:
# Passar a tabela extraída a um LLM para análise
prompt = f"""Analise a tabela financeira abaixo e resuma as principais tendências:
{all_tables[0]}
"""
Essa abordagem é bem mais rápida do que extrair tabelas com o pdfplumber e depois converter para Markdown manualmente.
Lidando com tabelas em várias páginas
Tabelas que se estendem por várias páginas são um desafio clássico da extração de PDFs. Demonstrativos financeiros, listas de inventário e peticionamentos regulatórios com frequência têm tabelas que percorrem duas, cinco ou dezenas de páginas. A ideia-chave é extrair a tabela de cada página separadamente e depois costurar as linhas, com cuidado para tratar cabeçalhos repetidos e artefatos da página.
from pdf_oxide import PdfDocument
doc = PdfDocument("long-report.pdf")
def extract_table_rows(md_text):
"""Extrair linhas de tabela do markdown, devolvendo cabeçalho e dados separados."""
header = None
data_rows = []
for line in md_text.split("\n"):
line = line.strip()
if not line.startswith("|") or line.startswith("|--"):
continue
cells = [cell.strip() for cell in line.split("|")[1:-1]]
if header is None:
header = cells
else:
data_rows.append(cells)
return header, data_rows
# Acumular linhas de todas as páginas
combined_header = None
combined_rows = []
for i in range(doc.page_count()):
md = doc.to_markdown(i)
header, rows = extract_table_rows(md)
if header is None:
continue # Nenhuma tabela nesta página
if combined_header is None:
combined_header = header
elif header == combined_header:
pass # Pular cabeçalho repetido em páginas posteriores
else:
# Outra tabela — salvar a atual e começar uma nova
print(f"Tabela com {len(combined_rows)} linhas encontrada")
combined_header = header
combined_rows = []
combined_rows.extend(rows)
if combined_header and combined_rows:
print(f"Colunas: {combined_header}")
print(f"Total de linhas: {len(combined_rows)}")
for row in combined_rows[:5]:
print(row)
if len(combined_rows) > 5:
print(f"... e mais {len(combined_rows) - 5} linhas")
Esse padrão funciona de forma confiável em tabelas cujo cabeçalho é repetido em cada página (o caso mais comum). Se o cabeçalho só aparece na primeira página, simplifique a lógica capturando o cabeçalho apenas da primeira página com tabela e tratando as linhas seguintes como dados.
Exportar tabelas para CSV ou DataFrame
Depois de extrair os dados, você quase sempre precisa deles em um formato estruturado para análises posteriores. Os exemplos a seguir mostram como ir de um PDF para um DataFrame do pandas ou um CSV em poucas linhas.
Exportação em lote: cada tabela em um CSV separado
import csv
from pdf_oxide import PdfDocument
doc = PdfDocument("catalog.pdf")
table_count = 0
for i in range(doc.page_count()):
md = doc.to_markdown(i)
rows = []
for line in md.split("\n"):
line = line.strip()
if line.startswith("|") and not line.startswith("|--"):
cells = [cell.strip() for cell in line.split("|")[1:-1]]
rows.append(cells)
if len(rows) > 1: # Pelo menos cabeçalho + uma linha de dados
table_count += 1
filename = f"table_page{i + 1}_{table_count}.csv"
with open(filename, "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(rows)
print(f"Arquivo {filename} salvo ({len(rows) - 1} linhas de dados)")
print(f"Foram exportadas {table_count} tabelas no total")
Tabela em várias páginas para DataFrame
Para tabelas que atravessam várias páginas, combine o padrão de costura com o pandas:
import pandas as pd
from pdf_oxide import PdfDocument
doc = PdfDocument("financial-statement.pdf")
header = None
all_rows = []
for i in range(doc.page_count()):
md = doc.to_markdown(i)
for line in md.split("\n"):
line = line.strip()
if not line.startswith("|") or line.startswith("|--"):
continue
cells = [cell.strip() for cell in line.split("|")[1:-1]]
if header is None:
header = cells
elif cells == header:
continue # Pular cabeçalho repetido
else:
all_rows.append(cells)
if header and all_rows:
df = pd.DataFrame(all_rows, columns=header)
# Limpar colunas numéricas
for col in df.columns:
# Tentar converter colunas que parecem numéricas
cleaned = df[col].str.replace(r"[$,%]", "", regex=True).str.strip()
try:
df[col] = pd.to_numeric(cleaned)
except (ValueError, TypeError):
pass # Manter como string
print(df.dtypes)
print(df.head(10))
df.to_csv("financial_data.csv", index=False)
Esse fluxo entrega um DataFrame limpo, com os tipos numéricos certos, pronto para análise com pandas, visualização no matplotlib ou carga em um banco de dados.
Tabelas complexas: quando usar o pdfplumber
A detecção de tabelas do PDF Oxide lida bem com tabelas alinhadas padrão. Em casos complexos — células mescladas, cabeçalhos que abrangem colunas, tabelas sem borda ou conteúdo multilinha — os algoritmos dedicados do pdfplumber são mais robustos:
import pdfplumber
with pdfplumber.open("complex-report.pdf") as pdf:
page = pdf.pages[0]
tables = page.extract_tables()
for table in tables:
for row in table:
print(row)
Quando usar cada um
| Cenário | Recomendado |
|---|---|
| Tabelas alinhadas simples | PDF Oxide (29× mais rápido) |
| Tabelas como parte do Markdown da página inteira | PDF Oxide |
| Células mescladas complexas / cabeçalhos que abrangem colunas | pdfplumber |
| Tabelas sem borda | pdfplumber |
| Processamento em lote com foco em velocidade | PDF Oxide |
Usar os dois juntos
Extração de texto rápida com PDF Oxide, extração de tabelas complexas com pdfplumber:
from pdf_oxide import PdfDocument
import pdfplumber
# Extração rápida de texto completo
doc = PdfDocument("report.pdf")
text = doc.extract_text(0)
# Extração direcionada de tabelas em páginas complexas
with pdfplumber.open("report.pdf") as pdf:
tables = pdf.pages[0].extract_tables()
Páginas relacionadas
- Conversão em Markdown — referência completa da API de Markdown
- Extração de texto — texto simples e extração por caractere
- PDF Oxide vs pdfplumber — comparação detalhada
- PDF para Markdown — guia de conversão para Markdown