Manipular Excel con Python — Leer, escribir y formatear con `openpyxl
Excel sigue dominando el mundo de los datos. En oficinas, contabilidad, ventas, marketing, recursos humanos — los .xlsx están por todos lados. Y en algún momento te toca automatizar el trabajo con ellos: leer 200 ficheros, generar un informe mensual, formatear una plantilla, exportar resultados de un script.
La buena noticia: Python lo hace fácil. La librería openpyxl te permite leer, escribir, formatear, añadir fórmulas y casi todo lo que harías a mano en Excel — pero programable, repetible y sin errores humanos.
En esta entrada te enseño los patrones que de verdad usas: leer datos, generar un fichero nuevo, copiar de plantilla, aplicar estilos básicos, fórmulas, y los errores típicos que te muerden cuando empiezas.
Contenido
Instalar openpyxl
pip install openpyxl
💡 ¿Aún no tienes el reflejo de crear un venv antes? Mira entornos virtuales con
venv. Cinco comandos que te ahorran meses de problemas.
openpyxl lee y escribe ficheros .xlsx (Excel moderno). No lee .xls antiguos (para eso necesitarías xlrd o convertir antes). Y no ejecuta macros VBA — solo manipula la hoja en sí.
Leer un Excel
Lo más típico: tienes un fichero con datos y los quieres procesar.
from openpyxl import load_workbook
wb = load_workbook("ventas.xlsx")
hoja = wb.active # primera hoja activa
print(hoja.title) # "Sheet1" o lo que se llame
print(hoja.max_row, hoja.max_column) # 100 5 → 100 filas, 5 columnas
# Leer una celda concreta
print(hoja["A1"].value)
print(hoja.cell(row=2, column=3).value) # equivalente a "C2"
# Recorrer filas
for fila in hoja.iter_rows(min_row=2, values_only=True):
print(fila)
# (1, 'Camiseta', 19.99, '2026-04-01', 'tienda-online')
# (2, 'Pantalón', 39.99, '2026-04-02', 'tienda-fisica')
# ...
Dos modos de iterar:
hoja.iter_rows(values_only=True)te da tuplas con los valores.hoja.iter_rows()(sinvalues_only) te da objetosCell— útil cuando necesitas formato, fórmula, etc.
Acceder a hojas concretas
Si el fichero tiene varias hojas:
wb = load_workbook("informe.xlsx")
print(wb.sheetnames) # ["Resumen", "Detalle", "Datos"]
resumen = wb["Resumen"]
detalle = wb["Detalle"]
# Crear una hoja nueva
nueva = wb.create_sheet("Calculos", index=0) # al principio
📥 Llévate el cheatsheet de Python (gratis)
PDF de 6 páginas con lo esencial: tipos, condicionales, bucles, estructuras de datos, funciones y los errores que más vas a cometer. Para tener al lado mientras programas.
Sin spam. Te apuntas a la lista, descargas el cheatsheet y recibes contenido de Python cada semana.
Crear un Excel desde cero
Caso común: tu script genera un informe.
from openpyxl import Workbook
wb = Workbook()
hoja = wb.active
hoja.title = "Ventas"
# Cabecera
hoja.append(["Producto", "Unidades", "Precio"])
# Datos
hoja.append(["Camiseta", 10, 19.99])
hoja.append(["Pantalón", 5, 39.99])
hoja.append(["Gorra", 8, 14.50])
wb.save("ventas_2026.xlsx")
hoja.append(lista) añade una fila al final. Es la forma más rápida de generar tablas.
💡 ¿Datos vienen de una API o JSON? Mira JSON en Python y luego los vuelcas con
hoja.append()en bucle.
Escribir celdas concretas
Para puntos específicos:
hoja["A1"] = "Informe mensual"
hoja["B5"] = 1234.56
hoja.cell(row=10, column=2, value="Total")
hoja["A1"] = X y hoja.cell(row=R, column=C, value=X) son equivalentes.
Fórmulas
Sí, puedes meter fórmulas reales que Excel evalúa al abrir el fichero:
hoja["B2"] = 10
hoja["B3"] = 20
hoja["B4"] = 30
hoja["B5"] = "=SUM(B2:B4)" # ← fórmula como string
wb.save("con_formulas.xlsx")
Cuando abras el .xlsx en Excel, B5 mostrará 60. Mientras el fichero esté solo en disco (sin abrirse en Excel), el valor calculado no está — solo la fórmula.
⚠️ Ojo: si lees
hoja["B5"].valuejusto después de escribirlo en Python, te devolverá el string"=SUM(B2:B4)", no60. Para leer valores calculados de un fichero ya abierto y guardado en Excel, necesitasload_workbook("archivo.xlsx", data_only=True).
Estilos: poner bonita la salida
Esto es donde openpyxl empieza a hacer cosas que ahorran horas a mano:
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
wb = Workbook()
hoja = wb.active
hoja.append(["Producto", "Unidades", "Precio"])
# Cabecera en negrita, fondo amarillo, centrada
for celda in hoja[1]:
celda.font = Font(bold=True, color="000000")
celda.fill = PatternFill("solid", fgColor="FFD539")
celda.alignment = Alignment(horizontal="center")
# Datos
hoja.append(["Camiseta", 10, 19.99])
hoja.append(["Pantalón", 5, 39.99])
# Anchura de columnas
hoja.column_dimensions["A"].width = 20
hoja.column_dimensions["B"].width = 12
hoja.column_dimensions["C"].width = 12
wb.save("ventas_estilo.xlsx")
Tres clases que cubren el 95% de los casos:
Font(bold, italic, color, size)— fuente.PatternFill("solid", fgColor=...)— fondo de celda.Alignment(horizontal, vertical)— alineación.
Los colores van en hex sin #: "FFD539", "3781A9".
Casos reales típicos
Combinar varios CSV en un solo Excel con varias hojas
from openpyxl import Workbook
import csv
from pathlib import Path
wb = Workbook()
wb.remove(wb.active) # quita la hoja por defecto
for csv_path in Path("datos").glob("*.csv"):
hoja = wb.create_sheet(csv_path.stem) # nombre de hoja = nombre fichero sin ext
with csv_path.open(encoding="utf-8") as f:
for fila in csv.reader(f):
hoja.append(fila)
wb.save("consolidado.xlsx")
💡
csv_path.stemte da el nombre del fichero sin extensión. Para esto y otras operaciones con rutas:pathlibvsos.path.
Filtrar filas y exportar a Excel
from openpyxl import load_workbook, Workbook
origen = load_workbook("ventas.xlsx")["Datos"]
destino_wb = Workbook()
destino = destino_wb.active
# Cabecera
destino.append([c.value for c in origen[1]])
# Solo filas con precio > 50
for fila in origen.iter_rows(min_row=2, values_only=True):
if fila[2] and fila[2] > 50:
destino.append(fila)
destino_wb.save("ventas_premium.xlsx")
Generar resumen con totales y formato de moneda
from openpyxl import Workbook
from openpyxl.styles import Font
wb = Workbook()
hoja = wb.active
hoja.append(["Producto", "Cantidad", "Precio", "Total"])
datos = [("Camiseta", 10, 19.99), ("Pantalón", 5, 39.99), ("Gorra", 8, 14.50)]
for nombre, cantidad, precio in datos:
hoja.append([nombre, cantidad, precio, f"=B{hoja.max_row + 1}*C{hoja.max_row + 1}"])
# Ojo al cálculo del número de fila al meter la fórmula:
# hoja.max_row + 1 → la fila que se va a crear con append()
# Total general en última fila
fila_total = hoja.max_row + 1
hoja.cell(row=fila_total, column=1, value="TOTAL")
hoja.cell(row=fila_total, column=4, value=f"=SUM(D2:D{hoja.max_row})")
# Formato negrita en total
for celda in hoja[fila_total]:
celda.font = Font(bold=True)
# Formato de número (moneda)
for celda in hoja["C"]:
celda.number_format = "#,##0.00 €"
for celda in hoja["D"]:
celda.number_format = "#,##0.00 €"
wb.save("resumen.xlsx")
number_format es la cadena de formato de Excel. Algunos útiles:
"#,##0"— entero con separador de miles"#,##0.00"— dos decimales"#,##0.00 €"— con símbolo de euro"0.00%"— porcentaje"yyyy-mm-dd"— fecha ISO
Errores típicos al usar openpyxl
# 1. Confundir filas-1-indexadas con 0-indexadas
hoja.cell(row=0, column=0) # ❌ openpyxl es 1-indexado, no 0
hoja.cell(row=1, column=1) # ✓
# 2. Olvidar wb.save() — no se escribe nada en disco
wb = Workbook()
hoja = wb.active
hoja["A1"] = "hola"
# ❌ falta wb.save("file.xlsx")
# 3. Reabrir fichero con fórmulas y leer string en vez de valor
wb = load_workbook("con_formula.xlsx")
print(wb.active["B5"].value) # "=SUM(B2:B4)" si Excel no lo recalculó
# Si quieres el valor calculado:
wb = load_workbook("con_formula.xlsx", data_only=True)
print(wb.active["B5"].value) # 60 (siempre que el fichero se haya abierto y guardado en Excel)
# 4. Fechas y horas en pandas/datetime mezcladas con strings
hoja["A1"] = "2026-04-19" # se guarda como string
from datetime import date
hoja["A2"] = date(2026, 4, 19) # se guarda como fecha real de Excel ✓
# 5. Hojas con nombre con caracteres prohibidos
wb.create_sheet("Datos:Mes") # ❌ Excel no permite : en nombres
# 6. Pasar pandas DataFrame directamente a hoja.append
hoja.append(df) # ❌ append espera lista, no DataFrame
for _, fila in df.iterrows():
hoja.append(fila.tolist()) # ✓
# o para esto, mejor:
df.to_excel("salida.xlsx", index=False) # pandas sabe escribir Excel directamente
💡 Si tu mundo es datos, lo más productivo suele ser usar
pandaspara procesar yto_excel/read_excelpara entrada/salida, y solo bajar aopenpyxlcuando necesites formato granular o macros raras.
Resumen
| Operación | Sintaxis |
|---|---|
| Abrir | load_workbook("file.xlsx") |
| Crear nuevo | Workbook() |
| Hoja activa | wb.active |
| Hoja por nombre | wb["Nombre"] |
| Celda por coord | hoja["A1"] o hoja.cell(row, col) |
| Iterar filas | hoja.iter_rows(values_only=True) |
| Añadir fila | hoja.append([...]) |
| Estilo | Font, PatternFill, Alignment |
| Formato número | celda.number_format = "#,##0.00 €" |
| Anchura columna | hoja.column_dimensions["A"].width = 20 |
| Guardar | wb.save("salida.xlsx") |
| Leer valores calculados | load_workbook(..., data_only=True) |
¿Te ha valido esto?
Si te ha resultado útil, llévate el cheatsheet de Python en PDF — 6 páginas con tipos, condicionales, bucles, estructuras de datos, funciones y los errores típicos. Para tener al lado mientras programas. Gratis.
Sin spam. Email + cheatsheet + un correo por semana con tutoriales nuevos.
Tu siguiente paso
Si has llegado hasta aquí, ya puedes automatizar el 80% de las cosas que se hacen a mano en Excel: generar informes, consolidar varios ficheros, aplicar formato consistente, calcular totales. Es una de las habilidades que en cualquier oficina te convierte en “ese que hace cosas que parecen magia”.
Si quieres aprender Python desde la base hasta proyectos reales con archivos, datos y APIs, en El Pythonista lo enseño paso a paso.
Un abrazo,
Oscar
¿Quieres aprender Python en orden, no a saltos?
Esto que has leído es solo una pieza. En El Pythonista lo verás todo encadenado: 11 módulos, 37+ horas de vídeo, 734 actividades y un proyecto real (MovieTracker) que crece contigo desde la primera variable hasta el deploy a producción.
37+ horas · 734 actividades · Proyecto real · Acceso de por vida · 14 días de garantía
