OpenPyXL: Como Automatizar Excel com Python
Aprenda OpenPyXL para criar, ler e automatizar planilhas Excel com Python: fórmulas, estilos, gráficos, arquivos grandes e relatório completo.
OpenPyXL é a biblioteca indicada para criar, ler e editar arquivos Excel .xlsx com Python quando você precisa controlar células, fórmulas, estilos, abas, filtros, imagens ou gráficos. O fluxo básico é instalar openpyxl, abrir ou criar um Workbook, selecionar uma planilha, alterar os valores e salvar o arquivo. Ela não exige o Microsoft Excel instalado.
Na prática, você pode usar OpenPyXL para transformar uma exportação bruta em um relatório formatado, consolidar arquivos recebidos de filiais, preencher um modelo financeiro, criar uma planilha para um cliente ou eliminar tarefas repetitivas de copiar e colar. Se o trabalho envolve principalmente análise e agrupamento de dados, Pandas pode ser a ferramenta central; quando a entrega final precisa ser um Excel bem formatado, OpenPyXL assume o acabamento.
Este tutorial vai do primeiro arquivo até um relatório de vendas completo, com validação, fórmulas, tabela, congelamento de cabeçalho e gráfico. Para automações mais amplas, veja também o guia de automação de planilhas com Python e o conteúdo sobre manipulação de arquivos.
O que o OpenPyXL faz — e o que ele não faz
| Tarefa | OpenPyXL atende? | Observação |
|---|---|---|
Criar arquivos .xlsx | Sim | Não precisa ter Excel instalado |
| Ler e editar planilhas existentes | Sim | Inclui células, abas, estilos e fórmulas |
| Criar gráficos e tabelas | Sim | Com recursos próprios do formato Excel |
| Preservar macros existentes | Parcialmente | Use keep_vba=True em arquivos .xlsm |
| Criar ou executar macros VBA | Não | Exige outra abordagem |
Ler o formato antigo .xls | Não | Converta para .xlsx ou use outra biblioteca |
| Calcular fórmulas como o Excel | Não | A biblioteca grava fórmulas, mas não é um motor de cálculo |
| Controlar o aplicativo Excel | Não | No Windows, automação da interface usa outras ferramentas |
Essa distinção evita uma frustração comum: OpenPyXL manipula o arquivo, não o programa Microsoft Excel. Ao escrever =SUM(B2:B10), a fórmula fica salva no documento, mas o resultado em cache só será atualizado quando Excel, LibreOffice ou outro motor compatível recalcular a pasta de trabalho.
Instalação e estrutura inicial
Crie um ambiente virtual antes de instalar dependências:
python -m venv .venv
source .venv/bin/activate # Linux ou macOS
python -m pip install openpyxl
No PowerShell:
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install openpyxl
Se o projeto usa uv:
uv init relatorio-excel
cd relatorio-excel
uv add openpyxl
O pacote instalado e o nome do import são iguais: openpyxl.
Como criar uma planilha Excel com Python
O objeto Workbook representa o arquivo inteiro. Cada Worksheet representa uma aba:
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
ws.title = "Vendas"
ws["A1"] = "Produto"
ws["B1"] = "Quantidade"
ws["C1"] = "Preço unitário"
ws["D1"] = "Total"
ws.append(["Notebook", 3, 3500.00, "=B2*C2"])
ws.append(["Monitor", 5, 1200.00, "=B3*C3"])
ws.append(["Teclado", 8, 180.00, "=B4*C4"])
wb.save("vendas.xlsx")
Há duas formas úteis de preencher células:
ws["A1"] = valoré clara para posições específicas;ws.append([...])adiciona uma linha ao final e funciona bem em importações.
Salvar com wb.save() cria ou sobrescreve o arquivo informado. Em uma automação real, prefira salvar primeiro em um arquivo de saída, como vendas_processadas.xlsx, para não destruir a fonte antes de validar o resultado.
Como ler um arquivo .xlsx existente
Use load_workbook:
from openpyxl import load_workbook
wb = load_workbook("vendas.xlsx")
ws = wb["Vendas"]
print(ws["A2"].value)
print(ws.max_row, ws.max_column)
for produto, quantidade, preco, total in ws.iter_rows(
min_row=2,
values_only=True,
):
print(produto, quantidade, preco, total)
Com values_only=True, cada linha retorna valores Python em vez de objetos Cell. Isso deixa o código mais simples quando você só precisa ler os dados.
Ler o valor calculado de fórmulas
Ao abrir com data_only=True, o OpenPyXL retorna o último resultado armazenado pela suíte de planilhas, em vez do texto da fórmula:
from openpyxl import load_workbook
wb = load_workbook("vendas.xlsx", data_only=True)
ws = wb["Vendas"]
print(ws["D2"].value)
Se o arquivo nunca foi aberto e recalculado depois que a fórmula foi criada, o valor pode ser None ou estar desatualizado. Para cálculos críticos, faça a conta também no Python ou inclua uma etapa de recálculo com uma ferramenta apropriada.
Editar células, linhas e abas
A alteração de um arquivo existente segue o mesmo modelo:
from openpyxl import load_workbook
wb = load_workbook("vendas.xlsx")
ws = wb["Vendas"]
ws["C2"] = 3299.90
ws["D2"] = "=B2*C2"
resumo = wb.create_sheet("Resumo")
resumo["A1"] = "Receita total"
resumo["B1"] = "=SUM(Vendas!D2:D4)"
wb.save("vendas_atualizadas.xlsx")
Para remover uma aba:
if "Temporária" in wb.sheetnames:
del wb["Temporária"]
Antes de selecionar uma aba pelo nome, valide sua existência. Arquivos enviados por outras pessoas podem chegar com nomes diferentes, espaços extras ou traduções.
Aplicando formatação profissional
Um relatório precisa ser legível. OpenPyXL oferece fontes, preenchimentos, bordas, alinhamento e formatos numéricos:
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
azul = "306998"
amarelo = "FFD43B"
borda_fina = Side(style="thin", color="D1D5DB")
for celula in ws[1]:
celula.font = Font(bold=True, color="FFFFFF")
celula.fill = PatternFill("solid", fgColor=azul)
celula.alignment = Alignment(horizontal="center")
celula.border = Border(bottom=borda_fina)
for linha in range(2, ws.max_row + 1):
ws.cell(linha, 3).number_format = 'R$ #,##0.00'
ws.cell(linha, 4).number_format = 'R$ #,##0.00'
ws.column_dimensions["A"].width = 24
ws.column_dimensions["B"].width = 14
ws.column_dimensions["C"].width = 18
ws.column_dimensions["D"].width = 18
ws.freeze_panes = "A2"
ws.auto_filter.ref = ws.dimensions
number_format muda a apresentação, não o valor armazenado. Guarde preços como float ou, em fluxos financeiros que exijam precisão decimal, prepare os valores com Decimal antes da exportação.
O congelamento A2 mantém a primeira linha visível durante a rolagem. O filtro automático permite ordenar e filtrar diretamente no Excel.
Transformando os dados em uma tabela do Excel
Uma tabela estruturada melhora a navegação e faz o intervalo crescer de forma previsível:
from openpyxl.styles import PatternFill
from openpyxl.worksheet.table import Table, TableStyleInfo
referencia = f"A1:D{ws.max_row}"
tabela = Table(displayName="TabelaVendas", ref=referencia)
tabela.tableStyleInfo = TableStyleInfo(
name="TableStyleMedium2",
showFirstColumn=False,
showLastColumn=False,
showRowStripes=True,
showColumnStripes=False,
)
ws.add_table(tabela)
O displayName não pode conter espaços e deve ser único dentro do arquivo. A primeira linha do intervalo precisa conter cabeçalhos em texto.
Criando fórmulas e totais
Você pode gravar qualquer fórmula compatível com a planilha de destino:
linha_total = ws.max_row + 1
ws.cell(linha_total, 3, "Total geral")
ws.cell(linha_total, 4, f"=SUM(D2:D{linha_total - 1})")
ws.cell(linha_total, 3).font = Font(bold=True)
ws.cell(linha_total, 4).font = Font(bold=True)
ws.cell(linha_total, 4).number_format = 'R$ #,##0.00'
As funções são normalmente escritas em inglês no arquivo, mesmo que a interface do Excel esteja em português. Use SUM, AVERAGE, IF e VLOOKUP ou as alternativas modernas suportadas pela versão de destino.
Para relatórios distribuídos a públicos diferentes, teste no Excel e no LibreOffice. Recursos muito novos podem não ter comportamento idêntico em todas as suítes.
Adicionando um gráfico
O exemplo abaixo cria um gráfico de barras com o total por produto:
from openpyxl.chart import BarChart, Reference
ultima_linha_dados = ws.max_row - 1 # desconsidera a linha de total
grafico = BarChart()
grafico.type = "col"
grafico.style = 10
grafico.title = "Vendas por produto"
grafico.y_axis.title = "Receita (R$)"
grafico.x_axis.title = "Produto"
dados = Reference(
ws,
min_col=4,
min_row=1,
max_row=ultima_linha_dados,
)
categorias = Reference(
ws,
min_col=1,
min_row=2,
max_row=ultima_linha_dados,
)
grafico.add_data(dados, titles_from_data=True)
grafico.set_categories(categorias)
grafico.height = 8
grafico.width = 14
ws.add_chart(grafico, "F2")
Para análises com muitos pontos ou gráficos mais sofisticados, é comum gerar a visualização com Matplotlib, salvar como PNG e inserir a imagem no Excel. O guia de Matplotlib em Python mostra esse caminho.
Mini-projeto: relatório de vendas validado
Agora vamos reunir as peças em um script executável. A entrada é uma lista de dicionários, mas poderia vir de CSV, API ou banco de dados:
from pathlib import Path
from openpyxl import Workbook
from openpyxl.chart import BarChart, Reference
from openpyxl.styles import Alignment, Font, PatternFill
from openpyxl.worksheet.table import Table, TableStyleInfo
def validar_vendas(vendas: list[dict]) -> None:
campos = {"produto", "quantidade", "preco_unitario"}
if not vendas:
raise ValueError("A lista de vendas está vazia")
for indice, venda in enumerate(vendas, start=1):
ausentes = campos - venda.keys()
if ausentes:
raise ValueError(
f"Venda {indice} sem campos: {', '.join(sorted(ausentes))}"
)
if venda["quantidade"] < 0 or venda["preco_unitario"] < 0:
raise ValueError(f"Venda {indice} contém valor negativo")
def gerar_relatorio(vendas: list[dict], destino: Path) -> None:
validar_vendas(vendas)
wb = Workbook()
ws = wb.active
ws.title = "Vendas"
ws.append(["Produto", "Quantidade", "Preço unitário", "Total"])
for venda in vendas:
linha = ws.max_row + 1
ws.append([
venda["produto"],
venda["quantidade"],
venda["preco_unitario"],
f"=B{linha}*C{linha}",
])
azul = "306998"
for celula in ws[1]:
celula.font = Font(bold=True, color="FFFFFF")
celula.fill = PatternFill("solid", fgColor=azul)
celula.alignment = Alignment(horizontal="center")
for linha in range(2, ws.max_row + 1):
ws.cell(linha, 3).number_format = 'R$ #,##0.00'
ws.cell(linha, 4).number_format = 'R$ #,##0.00'
ultima_linha_dados = ws.max_row
linha_total = ultima_linha_dados + 1
ws.cell(linha_total, 3, "Total geral")
ws.cell(linha_total, 4, f"=SUM(D2:D{ultima_linha_dados})")
ws.cell(linha_total, 3).font = Font(bold=True)
ws.cell(linha_total, 4).font = Font(bold=True)
ws.cell(linha_total, 4).number_format = 'R$ #,##0.00'
tabela = Table(
displayName="TabelaVendas",
ref=f"A1:D{ultima_linha_dados}",
)
tabela.tableStyleInfo = TableStyleInfo(
name="TableStyleMedium2",
showRowStripes=True,
)
ws.add_table(tabela)
grafico = BarChart()
grafico.title = "Receita por produto"
grafico.y_axis.title = "Receita (R$)"
grafico.add_data(
Reference(ws, min_col=4, min_row=1, max_row=ultima_linha_dados),
titles_from_data=True,
)
grafico.set_categories(
Reference(ws, min_col=1, min_row=2, max_row=ultima_linha_dados)
)
ws.add_chart(grafico, "F2")
ws.freeze_panes = "A2"
ws.column_dimensions["A"].width = 24
ws.column_dimensions["B"].width = 14
ws.column_dimensions["C"].width = 18
ws.column_dimensions["D"].width = 18
destino.parent.mkdir(parents=True, exist_ok=True)
wb.save(destino)
vendas = [
{"produto": "Notebook", "quantidade": 3, "preco_unitario": 3500.00},
{"produto": "Monitor", "quantidade": 5, "preco_unitario": 1200.00},
{"produto": "Teclado", "quantidade": 8, "preco_unitario": 180.00},
]
gerar_relatorio(vendas, Path("saida/relatorio_vendas.xlsx"))
A validação vem antes da apresentação. Esse detalhe diferencia uma automação confiável de um script que apenas gera um arquivo bonito. Em produção, registre quantas linhas foram processadas, trate exceções de arquivo e mantenha uma cópia da entrada. O guia de tratamento de erros em Python ajuda a estruturar essa camada.
OpenPyXL ou Pandas: qual usar?
| Necessidade | Melhor ponto de partida |
|---|---|
| Formatar células, abas e gráficos | OpenPyXL |
| Limpar e agrupar milhares de registros | Pandas |
| Ler um Excel como tabela para análise | Pandas |
Editar um template .xlsx existente | OpenPyXL |
| Gerar várias abas a partir de DataFrames | Pandas + OpenPyXL |
| Controlar detalhes da entrega final | OpenPyXL |
As bibliotecas são complementares. Um fluxo comum é:
import pandas as pd
from openpyxl import load_workbook
# 1. Pandas prepara e resume os dados
df = pd.read_csv("vendas.csv")
resumo = df.groupby("produto", as_index=False)["valor"].sum()
resumo.to_excel("relatorio.xlsx", index=False, sheet_name="Resumo")
# 2. OpenPyXL faz o acabamento do arquivo
wb = load_workbook("relatorio.xlsx")
ws = wb["Resumo"]
ws.freeze_panes = "A2"
ws.auto_filter.ref = ws.dimensions
ws.column_dimensions["A"].width = 28
ws.column_dimensions["B"].width = 18
wb.save("relatorio.xlsx")
Para planilhas pequenas e regras simples, usar somente OpenPyXL reduz dependências. Para transformação tabular intensa, Pandas deixa o código mais expressivo.
Como trabalhar com arquivos grandes
O modo normal carrega a pasta de trabalho na memória. Para arquivos grandes, use leitura incremental:
from openpyxl import load_workbook
wb = load_workbook(
"arquivo_grande.xlsx",
read_only=True,
data_only=True,
)
ws = wb.active
for linha in ws.iter_rows(values_only=True):
processar(linha)
wb.close()
Para escrita:
from openpyxl import Workbook
wb = Workbook(write_only=True)
ws = wb.create_sheet("Dados")
ws.append(["id", "valor"])
for item in gerar_itens():
ws.append([item.id, item.valor])
wb.save("dados_grandes.xlsx")
Os modos read_only e write_only economizam memória, mas limitam edição aleatória e alguns estilos. Se o volume for realmente grande e o consumidor não exigir Excel, considere CSV, Parquet ou banco de dados. O tutorial de CSV com Python cobre a alternativa mais universal.
Macros, senhas e recursos avançados
Para preservar macros existentes:
from openpyxl import load_workbook
wb = load_workbook("modelo.xlsm", keep_vba=True)
ws = wb["Dados"]
ws["B2"] = "Atualizado pelo Python"
wb.save("modelo_atualizado.xlsm")
Faça uma cópia e teste o arquivo resultante. O OpenPyXL não executa VBA e nem todos os objetos avançados do Excel são preservados em qualquer edição. Arquivos com conexões externas, controles, assinaturas digitais ou modelos corporativos complexos exigem validação específica.
OpenPyXL também não é uma solução de segurança para proteger informações sensíveis. Se a planilha contém dados pessoais, financeiros ou comerciais, controle acesso ao arquivo e ao local de armazenamento; não confie apenas em ocultar abas ou células.
Erros comuns ao automatizar Excel
Sobrescrever o arquivo original
Salve a primeira versão processada com outro nome. Só substitua a fonte quando houver backup e validação automatizada.
Misturar números e textos
"R$ 1.200,00" é texto. Prefira armazenar 1200.0 e aplicar formato monetário. Assim, filtros, somas e gráficos continuam funcionando.
Confiar em max_row sem verificar o conteúdo
Algumas planilhas carregam formatação em milhares de linhas vazias. Se isso ocorrer, percorra valores reais e determine o intervalo útil em vez de assumir que max_row representa a última venda.
Esperar que a fórmula seja calculada pelo Python
OpenPyXL grava a fórmula, mas não calcula seu resultado. Para regras que precisam de validação imediata, calcule também no código.
Usar índices fixos sem cabeçalhos
Arquivos externos mudam. Valide nomes de colunas ou crie um mapa de cabeçalhos antes de assumir que preço sempre está na coluna C.
Ignorar datas e fusos
O Excel armazena datas de forma própria, e o OpenPyXL converte muitas delas em datetime. Padronize datas antes da exportação e deixe explícito se horários estão em UTC ou no fuso local. Veja o guia de datas e horas com Python.
Ideia de projeto para portfólio
Uma automação de Excel ganha força no portfólio quando resolve um fluxo completo, não quando apenas pinta o cabeçalho. Um bom projeto pode:
- receber três arquivos de vendas mensais;
- validar colunas obrigatórias e valores negativos;
- consolidar os registros;
- gerar uma aba por região;
- criar resumo com totais e gráfico;
- registrar erros em um arquivo de log;
- salvar o resultado com data no nome;
- incluir testes para as funções de validação e cálculo.
No README, explique a dor, as entradas, a saída, como executar e quais limitações existem. Para alinhar o projeto à busca de emprego, consulte projetos de portfólio Python, o guia de estágio com Python e as vagas Python.
Perguntas frequentes sobre OpenPyXL
Como instalar OpenPyXL?
Ative um ambiente virtual e execute python -m pip install openpyxl. Com uv, use uv add openpyxl. Confirme com python -c "import openpyxl; print(openpyxl.__version__)".
OpenPyXL lê arquivos .xls?
Não. O formato .xls é anterior ao .xlsx. Converta o arquivo em uma suíte de escritório ou escolha uma biblioteca adequada ao formato legado.
OpenPyXL funciona sem Microsoft Excel?
Sim. A biblioteca lê e grava a estrutura do arquivo diretamente, inclusive em Linux e servidores. O Excel só entra no fluxo se você depender do recálculo, de VBA ou de recursos específicos do aplicativo.
OpenPyXL ou Pandas para Excel?
OpenPyXL é melhor para controlar a pasta de trabalho. Pandas é melhor para transformar dados tabulares. Use os dois quando precisar de análise eficiente e entrega formatada.
Como não perder macros de um .xlsm?
Abra com keep_vba=True, salve como .xlsm e valide uma cópia. Isso preserva macros existentes em muitos casos, mas não permite criar ou executar VBA.
Conclusão
Automatizar Excel com Python e OpenPyXL é útil porque conecta código a um formato que empresas brasileiras já usam todos os dias. Com a mesma biblioteca, você consegue ler uma exportação, validar registros, escrever fórmulas, aplicar estilos, criar tabelas e entregar um relatório .xlsx que outra pessoa abre sem precisar conhecer Python.
Comece por um arquivo pequeno e preserve o original. Depois, separe validação, transformação e apresentação em funções diferentes. Quando o projeto crescer, combine OpenPyXL com Pandas, testes e logs. Para fechar o fluxo de documentos, avance para Word com python-docx, relatórios em PDF e Google Sheets com gspread.