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.

12 Nov 2025 12 min de leitura Equipe Python Dev BR

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

TarefaOpenPyXL atende?Observação
Criar arquivos .xlsxSimNão precisa ter Excel instalado
Ler e editar planilhas existentesSimInclui células, abas, estilos e fórmulas
Criar gráficos e tabelasSimCom recursos próprios do formato Excel
Preservar macros existentesParcialmenteUse keep_vba=True em arquivos .xlsm
Criar ou executar macros VBANãoExige outra abordagem
Ler o formato antigo .xlsNãoConverta para .xlsx ou use outra biblioteca
Calcular fórmulas como o ExcelNãoA biblioteca grava fórmulas, mas não é um motor de cálculo
Controlar o aplicativo ExcelNãoNo 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?

NecessidadeMelhor ponto de partida
Formatar células, abas e gráficosOpenPyXL
Limpar e agrupar milhares de registrosPandas
Ler um Excel como tabela para análisePandas
Editar um template .xlsx existenteOpenPyXL
Gerar várias abas a partir de DataFramesPandas + OpenPyXL
Controlar detalhes da entrega finalOpenPyXL

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:

  1. receber três arquivos de vendas mensais;
  2. validar colunas obrigatórias e valores negativos;
  3. consolidar os registros;
  4. gerar uma aba por região;
  5. criar resumo com totais e gráfico;
  6. registrar erros em um arquivo de log;
  7. salvar o resultado com data no nome;
  8. 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.

E
Equipe Python Dev BR

Contribuidor do Python Dev BR

Artigos relacionados