Exporta tus guardados de Reddit a CSV y JSON con Python

Exporta tus guardados de Reddit a CSV y JSON con Python

Un script de Python puede guardar tus elementos de Reddit en CSV y JSON, pero no puede saltarse por sí solo el límite de lo que Reddit muestra. Los listados rondan los 1.000 elementos: PRAW puede recorrer lo que la API le entrega, no recuperar desde ese listado los guardados que ya quedaron fuera.

Para intentarlo con publicaciones antiguas, empieza también una solicitud oficial de datos desde un navegador de ordenador, en reddit.com/settings/data-request. Reddit dice que preparar el archivo puede tardar hasta 30 días. El archivo puede incluir saved_posts.csv y saved_comments.csv; la guía de Reddit los describe como listas de los elementos guardados. No des por hecho que contienen el texto de las publicaciones: varios análisis de exportaciones describen filas con un ID y un enlace permanente.

Dos rutas, dos resultados distintos

El flujo de este tutorial usa PRAW para exportar a tu equipo lo que aún devuelve el listado de guardados. El script crea dos archivos: uno JSON, útil para conservar campos estructurados, y otro CSV, práctico para abrir en una hoja de cálculo. Si buscas guardados anteriores al límite, la solicitud oficial es la ruta que conviene probar en paralelo, aunque Reddit no confirma explícitamente que el archivo cubra esos elementos en todos los casos.

flowchart TD
A["Crear solicitud oficial"] --> B["Descargar archivo"]
A --> C["Preparar acceso a PRAW"]
C --> D["Leer guardados visibles"]
D --> E["Escribir CSV y JSON"]
B --> F["Comprobar filas y enlaces"]
E --> F

1. Instala PRAW y prepara el acceso

PRAW es la biblioteca de Python que recorre listados de Reddit. Instálala en el entorno donde ejecutarás el script:

python -m pip install praw

Necesitarás las credenciales de una aplicación de Reddit. La guía de DEV Community describe una aplicación de tipo script y el flujo de usuario y contraseña de PRAW; Marqly advierte que el acceso a la API puede requerir aprobación explícita. Comprueba que tienes autorización para usarla y no pegues las credenciales dentro del código. El siguiente ejemplo las lee del entorno.

En una terminal de macOS o Linux, sustituye los valores de ejemplo por los tuyos. Estos comandos solo afectan a la sesión actual de la terminal:

export REDDIT_CLIENT_ID="tu_client_id"
export REDDIT_CLIENT_SECRET="tu_client_secret"
export REDDIT_USERNAME="tu_usuario"
export REDDIT_PASSWORD="tu_contrasena"
export REDDIT_USER_AGENT="saved-export/1.0 by u/tu_usuario"

Si tu cuenta usa autenticación en dos pasos, PRAW espera que añadas el código de seis cifras a la contraseña, separado por dos puntos, según la guía de DEV Community. No guardes ese valor en un archivo que vayas a compartir.

2. Lee los guardados y genera los dos formatos

Guarda este programa como reddit_saved_export.py. Usa limit=None para pedir a PRAW todos los elementos disponibles en el listado; no significa que Reddit vaya a devolver un historial ilimitado. El código distingue comentarios de publicaciones y deja vacíos los campos que no existen en un tipo de elemento.

import csv
import json
import os
from datetime import datetime, timezone

import praw

# Las credenciales llegan desde variables de entorno.
reddit = praw.Reddit(
    client_id=os.environ["REDDIT_CLIENT_ID"],
    client_secret=os.environ["REDDIT_CLIENT_SECRET"],
    username=os.environ["REDDIT_USERNAME"],
    password=os.environ["REDDIT_PASSWORD"],
    user_agent=os.environ["REDDIT_USER_AGENT"],
)

rows = []
for item in reddit.user.me().saved(limit=None):
    is_comment = item.name.startswith("t1_")
    title = getattr(item, "title", None) or getattr(item, "link_title", "") or ""
    body = getattr(item, "selftext", None)
    if body is None:
        body = getattr(item, "body", "") or ""

    rows.append({
        "id": item.id,
        "type": "comment" if is_comment else "post",
        "title": title,
        "subreddit": str(item.subreddit),
        "author": str(item.author) if item.author else "",
        "permalink": f"https://www.reddit.com{item.permalink}",
        "url": getattr(item, "url", "") or "",
        "created_utc": datetime.fromtimestamp(
            item.created_utc, tz=timezone.utc
        ).isoformat(),
        "score": item.score,
        "body": body,
    })

# JSON conserva los campos y el texto completo disponible.
with open("reddit_saved.json", "w", encoding="utf-8") as file:
    json.dump(rows, file, ensure_ascii=False, indent=2)

# CSV puede abrirse en programas de hojas de cálculo.
fieldnames = [
    "id", "type", "title", "subreddit", "author", "permalink",
    "url", "created_utc", "score", "body",
]
with open("reddit_saved.csv", "w", encoding="utf-8", newline="") as file:
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(rows)

print(f"Exportados {len(rows)} elementos visibles.")

Ejecuta el programa desde la misma terminal donde definiste las variables:

python reddit_saved_export.py

Los dos archivos aparecen en el directorio actual. Los campos de texto corresponden a contenido que siga disponible y que Reddit devuelva para ese elemento; el script no crea una copia de publicaciones borradas. Tampoco conserva una fecha de guardado: el campo created_utc corresponde a la fecha de creación del elemento.

3. Comprueba qué se escribió

Antes de tratar el archivo como una copia completa, verifica que ambos formatos se pueden leer y compara el número de filas. Este segundo programa no contacta con Reddit; solo revisa los archivos locales.

import csv
import json
from pathlib import Path

json_path = Path("reddit_saved.json")
csv_path = Path("reddit_saved.csv")

with json_path.open(encoding="utf-8") as file:
    json_rows = json.load(file)

with csv_path.open(encoding="utf-8", newline="") as file:
    csv_rows = list(csv.DictReader(file))

print(f"JSON: {len(json_rows)} elementos")
print(f"CSV:  {len(csv_rows)} elementos")
print(f"Filas coincidentes: {len(json_rows) == len(csv_rows)}")

if json_rows:
    print("Primer enlace:", json_rows[0]["permalink"])

Si el recuento se acerca a 1.000, no interpretes ese resultado como el total de tus guardados históricos. La documentación de PRAW indica que la mayoría de los listados de Reddit tienen un máximo de 1.000 elementos y se entregan en páginas de hasta 100.

4. Prueba la vía oficial para el historial antiguo

Desde un navegador de ordenador, inicia sesión en la cuenta correcta y visita reddit.com/settings/data-request. Sigue el formulario y, si permite seleccionar un intervalo, DEV Community recomienda elegir el historial completo. Reddit indica que la preparación puede tardar hasta 30 días; el enlace de descarga puede llegar a la bandeja de entrada de Reddit o, en algunos casos, al correo verificado.

Cuando descargues el archivo, busca saved_posts.csv y saved_comments.csv. Stashr y Marqly describen esos CSV como listas de ID y permalink, sin título ni texto. La cobertura de elementos que ya no aparecen en el listado no está garantizada: Reddit los describe como los elementos guardados, pero no publica una confirmación expresa de que la exportación siempre supere el límite. Cuenta las filas y conserva el archivo original antes de transformarlo.

Errores que conviene evitar

limit=None no elimina el tope. Solo pide a PRAW que recorra todo lo que el listado permita. Un bucle más largo o repetir la descarga no hará reaparecer elementos que Reddit ya no entrega por esa vía.

No confundas la fecha de creación con la fecha en que guardaste algo. El ejemplo exporta created_utc, no una marca temporal del acto de guardar. Y el CSV oficial puede ser apenas un conjunto de enlaces: si necesitas títulos o texto, el script los obtiene para los elementos que aún puede leer, no los incorpora retrospectivamente a la exportación oficial.

Por último, trata los archivos y las credenciales como datos privados. Un listado de guardados puede incluir contenido que no querrías dejar en una carpeta compartida. No subas el archivo ni publiques las variables de acceso.

El resultado es una exportación local de los guardados visibles en el momento de ejecutar el script, más una solicitud oficial que puede aportar enlaces adicionales. Mantén ambas copias: PRAW ofrece más contexto para el listado actual; la solicitud de datos es la opción que queda por comprobar para los elementos antiguos.

Fuentes: DEV Community, PRAW, Stashr, Marqly