Leer y escribir CSV en Python — Guía práctica con csv y pandas

Si trabajas con datos en Python, antes o después te toca pelearte con un CSV. Y aunque suene a tema básico, te garantizo que el 90% de los problemas con CSV no vienen de Python — vienen del propio fichero: encoding raro, separador con punto y coma en vez de coma, comillas mal puestas, valores con saltos de línea dentro…
Esta entrada es la guía práctica para leer y escribir CSV en Python sin sufrir. Con la librería estándar csv (cuando quieres control total) y con pandas (cuando quieres velocidad y no reinventar la rueda). Y con la lista de los problemas típicos para que sepas qué buscar cuando algo no funciona.
Contenido
Qué es exactamente un CSV
CSV (Comma-Separated Values) es un formato de texto plano para tablas. Cada línea es una fila; cada columna está separada por un delimitador, normalmente la coma. Ejemplo:
nombre,edad,ciudad
Ana,30,Madrid
Pedro,25,Barcelona
Luis,40,Valencia
Suena simple. No lo es. En la práctica te encuentras:
- Comas dentro de un valor:
"Ramírez, Oscar",35,Madrid(van entre comillas) - Separador
;en vez de,(típico de Excel en español) - Encoding diferente: UTF-8, latin-1, cp1252 (Windows)
- Saltos de línea dentro de un campo
- Cabecera o no
- Comillas mezcladas
Por eso Python tiene una librería entera dedicada al tema (csv) y pandas añade encima muchísima inteligencia.
Leer un CSV con la librería estándar (csv)
Esta es la opción de la librería estándar: viene con Python, no instalas nada.
Caso simple: csv.reader
import csv
with open("usuarios.csv", encoding="utf-8") as archivo:
lector = csv.reader(archivo)
for fila in lector:
print(fila)
Salida:
['nombre', 'edad', 'ciudad']
['Ana', '30', 'Madrid']
['Pedro', '25', 'Barcelona']
['Luis', '40', 'Valencia']
Cada fila es una lista de strings. Atención: '30' no es un número — es un string. Si quieres usarlo como entero, conviértelo tú con int(fila[1]).
Saltar la cabecera
Lo más típico: la primera fila son los nombres de columna y no la quieres tratar como dato.
import csv
with open("usuarios.csv", encoding="utf-8") as archivo:
lector = csv.reader(archivo)
cabecera = next(lector) # ← consume la primera fila
print("Columnas:", cabecera)
for fila in lector:
nombre, edad, ciudad = fila # unpacking
print(f"{nombre} ({int(edad)}) vive en {ciudad}")
Trabajar con diccionarios: csv.DictReader
Mucho más cómodo cuando hay varias columnas:
import csv
with open("usuarios.csv", encoding="utf-8") as archivo:
lector = csv.DictReader(archivo)
for fila in lector:
print(f"{fila['nombre']} vive en {fila['ciudad']}")
DictReader lee la primera fila como cabecera automáticamente y te da cada fila como un dict. Más legible, especialmente si el CSV tiene 10+ columnas.
Separador ; o tabulador
Cuando el CSV no usa coma:
# Con punto y coma (típico Excel español)
csv.reader(archivo, delimiter=";")
# Con tabulador (TSV)
csv.reader(archivo, delimiter="\t")
💡 Tip-friki: si no sabes qué delimitador trae el fichero,
csv.Snifferlo detecta solo:
python
dialecto = csv.Sniffer().sniff(archivo.read(1024))
archivo.seek(0)
lector = csv.reader(archivo, dialect=dialecto)
Escribir un CSV con csv
Idem, pero al revés. Punto importante: en Windows y para evitar líneas en blanco entre filas, usa newline="" al abrir el fichero.
Caso simple: csv.writer
import csv
filas = [
["nombre", "edad", "ciudad"],
["Ana", 30, "Madrid"],
["Pedro", 25, "Barcelona"],
["Luis", 40, "Valencia"],
]
with open("salida.csv", "w", encoding="utf-8", newline="") as archivo:
escritor = csv.writer(archivo)
escritor.writerows(filas)
writerows escribe varias filas. Para una sola: escritor.writerow(["Ana", 30, "Madrid"]).
Con diccionarios: csv.DictWriter
import csv
usuarios = [
{"nombre": "Ana", "edad": 30, "ciudad": "Madrid"},
{"nombre": "Pedro", "edad": 25, "ciudad": "Barcelona"},
{"nombre": "Luis", "edad": 40, "ciudad": "Valencia"},
]
with open("salida.csv", "w", encoding="utf-8", newline="") as archivo:
columnas = ["nombre", "edad", "ciudad"]
escritor = csv.DictWriter(archivo, fieldnames=columnas)
escritor.writeheader() # escribe la cabecera
escritor.writerows(usuarios)
Si en algún diccionario falta una clave, DictWriter la deja vacía. Si sobra, da error (a menos que pases extrasaction="ignore").
📥 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.
Y ahora con pandas (cuando hay datos serios)
pandas es la librería estándar de facto para análisis de datos en Python. Para CSV de más de unas pocas filas, es lo que vas a querer usar.
pip install pandas
Leer
import pandas as pd
df = pd.read_csv("usuarios.csv")
print(df)
Salida:
nombre edad ciudad
0 Ana 30 Madrid
1 Pedro 25 Barcelona
2 Luis 40 Valencia
pd.read_csv ya hace por ti:
- Lee la cabecera automáticamente.
- Convierte los tipos:
edadya esint, no string. - Devuelve un
DataFrame(la estructura tabular de pandas) sobre la que puedes filtrar, agrupar, ordenar y todo lo que se te ocurra.
Argumentos típicos de read_csv
pd.read_csv(
"datos.csv",
sep=";", # delimitador (por defecto ",")
encoding="utf-8", # o "latin-1", "cp1252"
header=0, # qué fila es la cabecera (None si no hay)
names=["a", "b", "c"], # nombres de columna manuales
skiprows=2, # saltar las primeras N filas
nrows=1000, # leer solo las primeras N filas
na_values=["NA", "-"], # qué strings tratar como NaN
parse_dates=["fecha"], # columnas que son fechas
dtype={"id": str}, # forzar tipo de una columna
)
Los dos argumentos que vas a tocar el 80% del tiempo: sep y encoding. Si tu CSV viene de Excel español, casi seguro: sep=";" y encoding="latin-1" o "cp1252".
Operaciones rápidas en el DataFrame
import pandas as pd
df = pd.read_csv("usuarios.csv")
print(df.head()) # primeras 5 filas
print(df.shape) # (filas, columnas)
print(df.dtypes) # tipos de cada columna
print(df["edad"].mean()) # media de la columna edad
print(df[df["edad"] > 28]) # filtrar filas
print(df.groupby("ciudad").size()) # contar por ciudad
print(df.sort_values("edad")) # ordenar por edad
Todo eso en una línea cada cosa. Si lo hicieras con la librería csv puro, son bucles + diccionarios manuales. Por eso pandas existe.
Escribir
df.to_csv("salida.csv", index=False, encoding="utf-8")
index=False es crítico — si no lo pones, pandas escribe una columna extra con el índice (0, 1, 2…) que casi nunca quieres.
Encoding: el problema número 1 con CSV
Si abres un CSV español y ves cosas como Mar�a, Andalu��a, o errores tipo UnicodeDecodeError — es encoding. Tu fichero no está en UTF-8 (lo más común hoy), sino en una codificación más antigua.
Probables sospechosos por orden de probabilidad:
# 1. UTF-8 (estándar moderno)
encoding="utf-8"
# 2. UTF-8 con BOM (Excel a veces añade un marker)
encoding="utf-8-sig"
# 3. Latin-1 (ISO-8859-1, español clásico)
encoding="latin-1"
# 4. Windows-1252 (Excel en Windows español)
encoding="cp1252"
Si no sabes cuál es, prueba en este orden. Y si te aburre adivinar:
import chardet # pip install chardet
with open("misterio.csv", "rb") as f:
detect = chardet.detect(f.read(10000))
print(detect) # {'encoding': 'utf-8', 'confidence': 0.99, ...}
Casos reales de uso
1. Filtrar un CSV grande y guardar el resultado
import pandas as pd
df = pd.read_csv("ventas.csv")
ventas_2024 = df[df["año"] == 2024]
ventas_2024.to_csv("ventas_2024.csv", index=False)
2. Combinar dos CSV con la misma estructura
import pandas as pd
df1 = pd.read_csv("enero.csv")
df2 = pd.read_csv("febrero.csv")
combinado = pd.concat([df1, df2], ignore_index=True)
combinado.to_csv("ene_feb.csv", index=False)
3. Limpiar un CSV: quitar duplicados, normalizar texto
import pandas as pd
df = pd.read_csv("usuarios.csv")
df["nombre"] = df["nombre"].str.strip().str.title()
df = df.drop_duplicates(subset=["email"])
df.to_csv("usuarios_limpios.csv", index=False)
4. Procesar un CSV gigante por trozos (cuando no cabe en memoria)
import pandas as pd
for chunk in pd.read_csv("gigante.csv", chunksize=100_000):
procesar(chunk)
chunksize te devuelve un iterador de DataFrames de N filas cada uno. Útil para CSVs de 1 GB+ que no entran en RAM.
csv vs pandas: ¿cuál elijo?
| Situación | Mejor opción |
|---|---|
| Script ligero, pocos requisitos, sin dependencias | csv |
| Procesar CSV de pocas líneas en memoria | csv |
| Análisis de datos, filtrado, agrupado | pandas |
| Archivos grandes con muchas columnas | pandas |
| Leer una fila a la vez en streaming | csv |
| Joins entre varios CSV | pandas |
| Lo voy a meter en una base de datos | pandas (lee CSV, escribe a SQL en una línea) |
Regla mental: si vas a hacer cualquier análisis o transformación seria, pandas. Si solo lees fila a fila para meterlas en otro sitio, csv.
Errores típicos
# 1. No usar newline="" al escribir en Windows
# → Líneas en blanco entre filas
# 2. Olvidar index=False con pandas
df.to_csv("x.csv") # MAL: añade columna 0,1,2,3,...
df.to_csv("x.csv", index=False) # BIEN
# 3. Confundir encoding al leer un CSV español de Excel
pd.read_csv("ventas.csv") # explota
pd.read_csv("ventas.csv", encoding="latin-1", sep=";") # bien
# 4. Comillas dentro de campos sin escapar
# Si tu CSV tiene texto con comas y mal-escapado, csv.reader puede leer mal.
# Solución: pandas con sep=",", quotechar='"', engine="python".
¿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 aquí, ya manejas CSV en Python mejor que la mayoría de la gente que se pelea con datos a diario. La clave es saber cuándo usar cada herramienta: csv para scripts pequeños y autosuficientes, pandas cuando hay que hacer algo más serio.
En El Pythonista te enseño a manejar archivos, datos y proyectos reales desde la base. Cuando dominas estos fundamentos, todo lo demás (APIs, web, dashboards, IA) viene rodado.
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
