Un programa que lee tus gastos de una hoja de cálculo exportada en CSV y saca un informe por categorías y por mes, desde la terminal y con exportación a JSON.
5 pasos
1 hora aproximadamente
55 líneas de código
Lo que vas a construir
Muchas apps de bancos y casi cualquier hoja de cálculo exportan los movimientos en CSV. Con unas pocas decenas de líneas de Python puedes sacar de ese archivo el informe que quieras. Vas a leer el CSV, agrupar por categorías, filtrar por mes y convertirlo en una herramienta de terminal con sus opciones.
Lo que vas a practicar
Leer CSV con csv.DictReader
Agrupar con defaultdict
Filtrar con comprensiones de listas
Dar formato a una tabla con f-strings
Opciones de terminal con argparse
Cómo ejecutarlo: Crea gastos.csv con los datos de ejemplo (o exporta los tuyos con las mismas columnas), guarda el programa como gastos.py y ejecútalo con python gastos.py. En el último paso necesita la opción del mes: python gastos.py --mes 2026-05.
csv.DictReader lee el archivo y convierte cada fila en un diccionario cuyas claves son los títulos de la primera línea: gasto["importe"]. Abre el archivo con newline="" (lo pide el módulo csv) y encoding="utf-8".
with cierra el archivo solo al terminar el bloque, aunque haya un error.
gastos.py
import csv
with open("gastos.csv", newline="", encoding="utf-8") as archivo:
gastos = list(csv.DictReader(archivo))
for gasto in gastos:
print(gasto["fecha"], gasto["concepto"], gasto["importe"])
print(f"{len(gastos)} gastos leídos")
Del CSV todo llega como texto: float() convierte el importe en número para poder sumarlo.
defaultdict(float) es un diccionario que, si pides una clave que no existe, la crea con 0.0. Así por_categoria[categoria] += importe funciona igual la primera vez que aparece una categoría que las siguientes.
gastos.pyLo nuevo, resaltado
import csv
from collections import defaultdict
with open("gastos.csv", newline="", encoding="utf-8") as archivo:
gastos = list(csv.DictReader(archivo))
total = 0.0
por_categoria = defaultdict(float) # si la categoría no existe, empieza en 0.0
for gasto in gastos:
importe = float(gasto["importe"])
total += importe
por_categoria[gasto["categoria"]] += importe
print(f"Total: {total:.2f} €")
for categoria, importe in por_categoria.items():
print(f" {categoria}: {importe:.2f} €")
El programa se reparte en funciones con un nombre claro: leer, filtrar y resumir. Cada una se puede probar y reutilizar por separado.
Como las fechas tienen el formato año-mes-día, los gastos de mayo son los que empiezan por «2026-05»: una comprensión de listas con startswith los filtra en una línea.
gastos.pyLo nuevo, resaltado
import csv
from collections import defaultdict
def leer_gastos(ruta):
with open(ruta, newline="", encoding="utf-8") as archivo:
return list(csv.DictReader(archivo))
def del_mes(gastos, mes):
"""Solo los gastos de un mes, con el formato «2026-05»."""
return [g for g in gastos if g["fecha"].startswith(mes)]
def resumir(gastos):
por_categoria = defaultdict(float)
for gasto in gastos:
por_categoria[gasto["categoria"]] += float(gasto["importe"])
return dict(por_categoria)
gastos = del_mes(leer_gastos("gastos.csv"), "2026-05")
resumen = resumir(gastos)
print(f"Mayo de 2026: {len(gastos)} gastos, {sum(resumen.values()):.2f} €")
for categoria, importe in resumen.items():
print(f" {categoria}: {importe:.2f} €")
Al ejecutarlo
Mayo de 2026: 8 gastos, 341.80 €
Casa: 78.15 €
Transporte: 75.00 €
Comida: 130.15 €
Ocio: 58.50 €
4
Paso 4 de 5
Un informe ordenado
sorted con key=lambda par: par[1] ordena las categorías por su importe y reverse=True pone primero la mayor.
En las f-strings, :<12 alinea a la izquierda en 12 caracteres, :>10.2f a la derecha con dos decimales: así los números quedan en columna como en una tabla.
gastos.pyLo nuevo, resaltado
import csv
from collections import defaultdict
def leer_gastos(ruta):
with open(ruta, newline="", encoding="utf-8") as archivo:
return list(csv.DictReader(archivo))
def del_mes(gastos, mes):
"""Solo los gastos de un mes, con el formato «2026-05»."""
return [g for g in gastos if g["fecha"].startswith(mes)]
def resumir(gastos):
por_categoria = defaultdict(float)
for gasto in gastos:
por_categoria[gasto["categoria"]] += float(gasto["importe"])
return dict(por_categoria)
def informe(resumen, titulo):
total = sum(resumen.values())
print(titulo)
print("-" * 36)
# De mayor a menor gasto
for categoria, importe in sorted(resumen.items(), key=lambda par: par[1], reverse=True):
porcentaje = importe / total * 100
print(f"{categoria:<12}{importe:>10.2f} € {porcentaje:>6.1f} %")
print("-" * 36)
print(f"{'Total':<12}{total:>10.2f} €")
gastos = del_mes(leer_gastos("gastos.csv"), "2026-05")
informe(resumir(gastos), "Gastos de mayo de 2026")
Al ejecutarlo
Gastos de mayo de 2026
------------------------------------
Comida 130.15 € 38.1 %
Casa 78.15 € 22.9 %
Transporte 75.00 € 21.9 %
Ocio 58.50 € 17.1 %
------------------------------------
Total 341.80 €
5
Paso 5 de 5
Una herramienta de terminal
argparse convierte el programa en una herramienta con opciones: --mes es obligatoria, --archivo tiene un valor por defecto y --json es opcional. Además genera solo la ayuda con python gastos.py --help.
Si el archivo no existe o el mes no tiene gastos, parser.exit termina con un mensaje claro y un código de error. Con --json resumen.json se guarda también el resumen, con ensure_ascii=False para que las tildes se escriban tal cual.
gastos.pyLo nuevo, resaltado
import argparse
import csv
import json
from collections import defaultdict
def leer_gastos(ruta):
with open(ruta, newline="", encoding="utf-8") as archivo:
return list(csv.DictReader(archivo))
def del_mes(gastos, mes):
"""Solo los gastos de un mes, con el formato «2026-05»."""
return [g for g in gastos if g["fecha"].startswith(mes)]
def resumir(gastos):
por_categoria = defaultdict(float)
for gasto in gastos:
por_categoria[gasto["categoria"]] += float(gasto["importe"])
return dict(por_categoria)
def informe(resumen, titulo):
total = sum(resumen.values())
print(titulo)
print("-" * 36)
for categoria, importe in sorted(resumen.items(), key=lambda par: par[1], reverse=True):
porcentaje = importe / total * 100
print(f"{categoria:<12}{importe:>10.2f} € {porcentaje:>6.1f} %")
print("-" * 36)
print(f"{'Total':<12}{total:>10.2f} €")
parser = argparse.ArgumentParser(description="Resumen de gastos por categoría")
parser.add_argument("--archivo", default="gastos.csv", help="CSV con los gastos")
parser.add_argument("--mes", required=True, help="mes con el formato AAAA-MM, por ejemplo 2026-05")
parser.add_argument("--json", help="guardar también el resumen en este archivo JSON")
args = parser.parse_args()
try:
gastos = del_mes(leer_gastos(args.archivo), args.mes)
except FileNotFoundError:
parser.exit(1, f"No encuentro el archivo {args.archivo}\n")
if not gastos:
parser.exit(1, f"No hay gastos en {args.mes}\n")
resumen = resumir(gastos)
informe(resumen, f"Gastos de {args.mes}")
if args.json:
with open(args.json, "w", encoding="utf-8") as salida:
json.dump({"mes": args.mes, "categorias": resumen}, salida, ensure_ascii=False, indent=2)
print(f"Resumen guardado en {args.json}")
Al ejecutarlo
$ python gastos.py --mes 2026-05 --json resumen.json
Gastos de 2026-05
------------------------------------
Comida 130.15 € 38.1 %
Casa 78.15 € 22.9 %
Transporte 75.00 € 21.9 %
Ocio 58.50 € 17.1 %
------------------------------------
Total 341.80 €
Resumen guardado en resumen.json
Retos para seguir
Has terminado el proyecto. Si quieres ir más allá, prueba a añadirle esto por tu cuenta:
Añade una opción --desde y --hasta para elegir un rango de fechas.
Dibuja un gráfico de barras de las categorías con la biblioteca matplotlib.
Compara el mes elegido con el anterior y muestra en qué categorías has gastado más.
Con tu permiso usamos Google Analytics, que guarda cookies, para saber cuántas personas visitan la web y qué páginas les resultan útiles. No usamos cookies de publicidad y tus archivos nunca salen de tu navegador. Puedes cambiar de opinión cuando quieras en «Configurar cookies», al pie de la página. Más información.