Cómo scrapear datos públicos de Reddit con proxies en 2025

Guía práctica para extraer datos públicos de Reddit usando proxies residenciales: desde la evolución de la API hasta ejemplos en Python y Node.js, manejo de rate limits y mejores prácticas éticas.

Cómo scrapear datos públicos de Reddit con proxies en 2025

Si tu equipo de datos necesita analizar tendencias, sentimiento o conversaciones en Reddit, te habrás encontrado con un problema: la API oficial de Reddit se volvió cara, los rate limits son agresivos, y scrapear sin proxies es un camino directo al bloqueo. Esta guía explica cómo acceder a datos públicos de Reddit de forma legítima y eficiente usando proxies residenciales, con ejemplos de código listos para ejecutar.

Aviso legal y ético

Este artículo cubre técnicas para acceder exclusivamente a datos públicos de Reddit. Debes respetar los Términos de Servicio de Reddit, el archivo robots.txt, y las leyes aplicables — incluyendo la CFAA (EE.UU.) y el GDPR (UE). Si Reddit ofrece una API oficial para tus necesidades y tu presupuesto lo permite, úsala. El scraping es una alternativa para equipos con restricciones de costo, pero nunca debe usarse para acceder a datos privados, eludir autenticación, ni violar acuerdos legales. Consulta siempre a tu equipo legal antes de implementar proyectos de scraping a escala.

El paisaje cambiante de la API de Reddit

Los cambios de precios de 2023

En julio de 2023, Reddit implementó su nuevo modelo de precios para la API oficial: 0,24 USD por cada 1.000 solicitudes para uso comercial. Para contexto, un proyecto que analiza 10 subreddits activos puede generar fácilmente millones de solicitudes al mes. El coste mensual rápidamente supera los miles de dólares — insostenible para equipos de investigación, startups y proyectos académicos con presupuesto limitado.

Antes de este cambio, la API de Reddit era gratuita con cuotas generosas. La transición dejó a muchos equipos buscando alternativas.

Cuotas actuales de la API oficial

Reddit mantiene una capa gratuita para clientes no comerciales, pero con límites estrictos:

  • OAuth rate limits: 100 solicitudes/minuto para aplicaciones OAuth
  • Rate limits por IP: 60 solicitudes/minuto sin autenticar
  • Términos restrictivos: prohibición de uso comercial sin acuerdo previo
  • Restricciones de redistribución: no se pueden compartir datasets derivados

Para equipos que necesitan datos de Reddit a escala, estos límites hacen que el scraping de contenido público sea la única opción viable desde el punto de vista económico.

El auge del scraping para proyectos sensibles al costo

La combinación de precios elevados y cuotas restrictivas ha impulsado el interés en Reddit data scraping como alternativa. Los datos públicos — posts, comentarios, metadatos de subreddits — están disponibles en la interfaz web sin login. Acceder a ellos mediante scraping no es intrínsecamente diferente a visitar la página en tu navegador, pero la automatización a escala requiere infraestructura de proxies para evitar bloqueos.

Qué datos de Reddit son accesibles públicamente

No todos los datos de Reddit son iguales. Es fundamental distinguir entre lo que es públicamente accesible y lo que requiere autenticación.

Datos accesibles sin login

  • Feeds de subreddits: posts ordenados por hot, new, top, rising
  • Páginas de posts: título, cuerpo, autor, puntuación, timestamp, flair
  • Hilos de comentarios: comentarios anidados con sus metadatos
  • Búsquedas: resultados de búsqueda pública por subreddit, keyword, o autor
  • Perfiles de usuario: posts y comentarios públicos del usuario
  • Metadatos de subreddit: descripción, reglas, número de suscriptores

Datos que requieren login (fuera del alcance ético)

  • Mensajes privados y notificaciones
  • Contenido en subreddits privados o restringidos
  • Historial de votos personal
  • Listas de usuarios bloqueados

No scrapees datos que requieren autenticación. Esto viola los ToS de Reddit y potencialmente la CFAA. Esta guía se limita estrictamente a datos públicos.

old.reddit.com: la alternativa más amigable para scraping

old.reddit.com es una joya para scrapers. Es la interfaz legacy de Reddit que mantiene el diseño original:

  • HTML más simple y predecible — mucho más fácil de parsear
  • Menos JavaScript dinámico — el contenido está en el HTML inicial
  • Menos mecanismos anti-bot comparado con la nueva UI
  • Mismo contenido, misma base de datos — no pierdes datos

Cualquier URL de Reddit puede convertirse a old.reddit.com simplemente cambiando el dominio: https://www.reddit.com/r/pythonhttps://old.reddit.com/r/python.

Selección de proxy: datacenter vs residencial

Elegir el tipo de proxy correcto es crucial para el éxito de tu proyecto de Reddit data scraping.

CaracterísticaDatacenterResidencial
VelocidadAlta (baja latencia)Media
Costo por GBBajoMedio-alto
Riesgo de bloqueoAltoBajo
Diversidad de IPsLimitada (rangos conocidos)Alta (IPs de ISPs reales)
Ideal paraVolumen bajo, tareas puntualesScraping intensivo, geo-distribuido
Rotación de IPManual o por rotación periódicaPor solicitud o sesión sticky

Cuándo usar datacenter

Los proxies datacenter funcionan para proyectos pequeños: monitorizar un subreddit, verificar puntuaciones de posts, o extracciones puntuales. Si tu volumen es menor a ~1.000 solicitudes/hora y no necesitas geo-targeting, datacenter puede ser suficiente.

Cuándo usar residenciales

Los Reddit residential proxies son necesarios cuando:

  • Scrapeas múltiples subreddits simultáneamente
  • Necesitas sesiones prolongadas sin interrupción
  • Ya has experimentado bloqueos con datacenter
  • Requieres geo-targeting (algunos subreddits regionales muestran contenido diferente)
  • Tu volumen supera las 5.000 solicitudes/hora

Las IPs residenciales provienen de ISPs reales, haciendo que tu tráfico sea indistinguible del de un usuario normal — exactamente lo que necesitas para scrapear Reddit con proxies de forma sostenible.

Ejemplo en Python: requests + old.reddit.com + pool residencial rotativo

Aquí tienes un ejemplo completo y ejecutable que extrae posts de un subreddit usando proxies residenciales de ProxyHat:

import requests
from bs4 import BeautifulSoup
import time
import json
import random

# Configuración de ProxyHat - pool residencial rotativo
PROXY_URL = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                 "AppleWebKit/537.36 (KHTML, like Gecko) "
                 "Chrome/125.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.5",
    "Connection": "keep-alive",
}

def create_session():
    """Crea una sesión con proxy residencial rotativo."""
    session = requests.Session()
    session.proxies = {"http": PROXY_URL, "https": PROXY_URL}
    session.headers.update(HEADERS)
    return session

def scrape_subreddit(subreddit, sort="hot", limit=25):
    """Extrae posts públicos de un subreddit vía old.reddit.com."""
    url = f"https://old.reddit.com/r/{subreddit}/{sort}/"
    session = create_session()

    try:
        response = session.get(url, timeout=15)
        response.raise_for_status()
    except requests.exceptions.HTTPError as e:
        if response.status_code == 429:
            print("Rate limited. Esperando antes de reintentar...")
            time.sleep(60)
            return scrape_subreddit(subreddit, sort, limit)
        elif response.status_code == 403:
            print("Bloqueado (403). Rotando IP...")
            return scrape_subreddit(subreddit, sort, limit)
        raise

    soup = BeautifulSoup(response.text, "html.parser")
    posts = []

    for thing in soup.select("div.thing.link"):
        title_el = thing.select_one("a.title")
        score_el = thing.select_one("div.score.unvoted")
        author_el = thing.select_one("a.author")
        time_el = thing.select_one("time")
        comments_el = thing.select_one("a.comments")

        if not title_el:
            continue

        posts.append({
            "title": title_el.text.strip(),
            "url": title_el.get("href", ""),
            "score": score_el.text.strip() if score_el else "0",
            "author": author_el.text if author_el else "[deleted]",
            "created_utc": time_el.get("datetime", "") if time_el else "",
            "comments_link": comments_el.get("href", "") if comments_el else "",
        })

        if len(posts) >= limit:
            break

    return posts

# Uso
if __name__ == "__main__":
    results = scrape_subreddit("python", sort="hot", limit=10)
    for post in results:
        print(json.dumps(post, indent=2, ensure_ascii=False))

Este script usa old.reddit.com para obtener HTML simple, parsea los posts con BeautifulSoup, y enruta todo el tráfico a través de un proxy residencial rotativo de ProxyHat. La rotación ocurre automáticamente — cada solicitud puede obtener una IP diferente del pool residencial.

Versión con sesiones sticky y geo-targeting

Si necesitas mantener la misma IP para múltiples solicitudes (por ejemplo, para paginar dentro de un hilo), usa sesiones sticky:

# Sesión sticky: misma IP durante la sesión
SESSION_ID = f"mysession{random.randint(1000,9999)}"
PROXY_STICKY = f"http://user-session-{SESSION_ID}-country-US:PASSWORD@gate.proxyhat.com:8080"

def scrape_post_with_comments(subreddit, post_id):
    """Extrae un post y sus comentarios usando sesión sticky."""
    session = requests.Session()
    session.proxies = {"http": PROXY_STICKY, "https": PROXY_STICKY}
    session.headers.update(HEADERS)

    url = f"https://old.reddit.com/r/{subreddit}/comments/{post_id}/"
    response = session.get(url, timeout=15)
    response.raise_for_status()

    soup = BeautifulSoup(response.text, "html.parser")

    # Extraer post principal
    post = {}
    post_area = soup.select_one("div.thing.id-{}".format(f"t3_{post_id}"))
    if post_area:
        title = post_area.select_one("a.title")
        post["title"] = title.text.strip() if title else ""

    # Extraer comentarios
    comments = []
    for comment in soup.select("div.comment"):
        author = comment.select_one("a.author")
        text = comment.select_one("div.md")
        score = comment.select_one("span.score")
        comments.append({
            "author": author.text if author else "[deleted]",
            "text": text.text.strip() if text else "",
            "score": score.text.strip() if score else "0",
        })

    post["comments"] = comments[:50]  # Limitar a 50 comentarios
    return post

Ejemplo en Node.js: axios + cheerio

Para equipos que trabajan con JavaScript, aquí tienes el equivalente en Node.js:

const axios = require('axios');
const cheerio = require('cheerio');
const HttpsProxyAgent = require('https-proxy-agent');

const PROXY_URL = 'http://user-country-US:PASSWORD@gate.proxyhat.com:8080';
const agent = new HttpsProxyAgent(PROXY_URL);

const HEADERS = {
  'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
    'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en-US,en;q=0.5',
};

async function scrapeSubreddit(subreddit, sort = 'hot') {
  const url = `https://old.reddit.com/r/${subreddit}/${sort}/`;

  try {
    const response = await axios.get(url, {
      httpsAgent: agent,
      headers: HEADERS,
      timeout: 15000,
    });

    const $ = cheerio.load(response.data);
    const posts = [];

    $('div.thing.link').each((i, el) => {
      if (i >= 25) return false;
      const title = $(el).find('a.title').text().trim();
      const score = $(el).find('div.score.unvoted').text().trim();
      const author = $(el).find('a.author').text() || '[deleted]';
      const timeAttr = $(el).find('time').attr('datetime') || '';

      posts.push({ title, score, author, created_utc: timeAttr });
    });

    return posts;
  } catch (error) {
    if (error.response?.status === 429) {
      console.log('Rate limited. Reintentando en 60s...');
      await new Promise(r => setTimeout(r, 60000));
      return scrapeSubreddit(subreddit, sort);
    }
    throw error;
  }
}

// Uso
scrapeSubreddit('datascience').then(posts => {
  console.log(JSON.stringify(posts, null, 2));
});

Ejemplo con curl para pruebas rápidas

# Probar un proxy residencial con curl
curl -x http://user-country-US:PASSWORD@gate.proxyhat.com:8080 \
     -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
     -H "Accept: text/html" \
     "https://old.reddit.com/r/python/hot/" \
     -o reddit_page.html

# Verificar que la IP está rotando
curl -x http://user-country-US:PASSWORD@gate.proxyhat.com:8080 \
     https://httpbin.org/ip

Manejo de rate limits: la progresión 429 → 403

Reddit implementa un sistema de rate limiting en capas que todo scraper debe entender:

Capa 1: Rate limit por IP (HTTP 429)

La primera señal es un código 429 Too Many Requests. Reddit limita por IP, con umbrales que varían según el endpoint:

  • Páginas de subreddit: ~60 solicitudes/minuto por IP
  • Páginas de comentarios: ~30 solicitudes/minuto por IP
  • Búsqueda: ~10 solicitudes/minuto por IP

La respuesta incluye headers x-ratelimit-remaining y x-ratelimit-reset que debes monitorizar.

Capa 2: Rate limit por User-Agent

Reddit también aplica cuotas por User-Agent. Si muchas IPs comparten el mismo User-Agent genérico, el límite se comparte. Esto es relevante cuando usas proxies — rotar IPs no ayuda si tu User-Agent es el mismo y está saturado.

Capa 3: Escalada a 403 Forbidden

Si persistes después de múltiples 429 sin respetar los rate limits, Reddit escala a 403 Forbidden. Esto significa que la IP (o el rango) ha sido bloqueada temporal o permanentemente. Con proxies datacenter, este bloqueo es más probable porque los rangos de IPs están identificados. Con proxies residenciales, la rotación natural del pool mitiga este riesgo.

Estrategia de manejo

import time
import requests

def fetch_with_retry(session, url, max_retries=3):
    """Realiza solicitudes con reintentos inteligentes."""
    for attempt in range(max_retries):
        try:
            response = session.get(url, timeout=15)

            # Monitorizar rate limits
            remaining = response.headers.get("x-ratelimit-remaining")
            reset = response.headers.get("x-ratelimit-reset")

            if remaining and int(remaining) < 5:
                wait_time = int(reset) if reset else 60
                print(f"Rate limit bajo ({remaining} restantes). "
                      f"Esperando {wait_time}s...")
                time.sleep(wait_time)

            if response.status_code == 200:
                return response
            elif response.status_code == 429:
                wait = min(2 ** attempt * 30, 300)  # Backoff exponencial
                print(f"429 recibido. Esperando {wait}s...")
                time.sleep(wait)
            elif response.status_code == 403:
                print("403 - IP bloqueada. Rotando...")
                time.sleep(10)
                return None  # El pool residencial rotará la IP
            else:
                response.raise_for_status()

        except requests.exceptions.RequestException as e:
            print(f"Error en intento {attempt + 1}: {e}")
            if attempt < max_retries - 1:
                time.sleep(5 * (attempt + 1))

    return None

Mejores prácticas para scrapear Reddit con proxies

1. Configura un User-Agent realista e informativo

No uses el User-Agent por defecto de tu librería HTTP. Reddit lo bloquea rápidamente. Usa un formato que incluya información de contacto:

# Bien: User-Agent descriptivo
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(compatible; ResearchBot/1.0; contact@ejemplo.com)"

# Mal: User-Agent genérico
"python-requests/2.31.0"

Para scraping de HTML (no API), usa un User-Agent de navegador real y completo, como en los ejemplos anteriores.

2. Respeta los rate limits

Monitoriza los headers x-ratelimit-* y ajusta tu velocidad. Una regla práctica: mantén un mínimo de 2 segundos entre solicitudes por IP. Con un pool de proxies residenciales, puedes paralelizar múltiples IPs, pero cada IP individual debe respetar este ritmo.

3. Cachea agresivamente

Los datos de Reddit no cambian cada segundo. Implementa un sistema de caché para evitar solicitudes redundantes:

import hashlib
import os
import json
from datetime import datetime, timedelta

cache_dir = "reddit_cache"
os.makedirs(cache_dir, exist_ok=True)

def cache_key(url):
    return hashlib.md5(url.encode()).hexdigest()

def cached_get(session, url, ttl_hours=6):
    """GET con caché local."""
    key = cache_key(url)
    filepath = os.path.join(cache_dir, f"{key}.json")

    # Verificar caché
    if os.path.exists(filepath):
        with open(filepath, "r") as f:
            cached = json.load(f)
        cached_time = datetime.fromisoformat(cached["timestamp"])
        if datetime.now() - cached_time < timedelta(hours=ttl_hours):
            return cached["data"]

    # Solicitar
    response = fetch_with_retry(session, url)
    if response is None:
        return None

    # Guardar en caché
    with open(filepath, "w") as f:
        json.dump({
            "timestamp": datetime.now().isoformat(),
            "data": response.text,
        }, f)

    return response.text

4. Usa old.reddit.com de forma consistente

La interfaz old.reddit.com ofrece HTML más limpio y estable. Los selectores CSS cambian con menos frecuencia y el contenido se carga sin JavaScript dinámico. Esto reduce la fragilidad de tus scrapers.

5. Distribuye las solicitudes en el tiempo

No hagas ráfagas de solicitudes. Usa delays aleatorios entre solicitudes para simular comportamiento humano:

import random
import time

def human_delay(min_sec=1.5, max_sec=4.0):
    """Pausa aleatoria entre solicitudes."""
    time.sleep(random.uniform(min_sec, max_sec))

# Entre cada solicitud:
human_delay()

6. Rotación inteligente de proxies

Con ProxyHat, puedes controlar la rotación mediante el formato del username:

  • Rotación por solicitud: Cada request obtiene una IP nueva — ideal para scraping masivo de feeds
  • Sesión sticky: Mantén la misma IP durante una sesión — necesario para paginación y hilos de comentarios
  • Geo-targeting: Usa country-US, country-DE, etc., para acceder a contenido regional

Para más detalles sobre configuración de proxies, consulta nuestra guía de configuración de proxies residenciales.

Casos de uso reales

Análisis de sentimiento de marca

Monitoriza menciones de tu marca en subreddits relevantes. Extrae posts y comentarios, analiza el sentimiento con NLP, y genera alertas cuando el sentimiento cambie significativamente.

Investigación de mercado

Identifica tendencias emergentes en subreddits de tu industria. Los posts en r/programming, r/datascience, o contienen opiniones tempranas sobre productos y tecnologías.

Tracking de memes y cultura

Los memes nacen en Reddit antes de llegar a otras plataformas. Rastrea subreddits como r/memes, r/dankmemes, o nichos específicos para detectar tendencias virales antes que la competencia.

Monitoreo de competencia

Sigue conversaciones sobre productos competidores. Los usuarios de Reddit son notablemente honestos en sus críticas — datos valiosos para product teams.

Cuándo usar la API oficial en lugar de scraping

El scraping no siempre es la mejor opción. Considera la API oficial cuando:

  • Presupuesto disponible: Si puedes pagar los costos de la API, obtienes datos estructurados en JSON, sin necesidad de parsear HTML
  • Datos en tiempo real: La API de WebSocket de Reddit ofrece actualizaciones en tiempo real que el scraping no puede igualar
  • Proyectos pequeños: Para menos de 100 solicitudes/minuto, la capa gratuita puede ser suficiente
  • Datos de moderación: Si necesitas datos de moderación (reportes, acciones de mods), la API es la única opción legítima
  • Fiabilidad crítica: Los cambios en el HTML de Reddit pueden romper tus scrapers; la API ofrece estabilidad contractual

Para proyectos de investigación, análisis de sentimiento a escala, o monitorización de múltiples subreddits, el scraping con proxies residenciales sigue siendo la opción más económica y flexible.

Puntos clave

Recuerda estos principios al scrapear Reddit con proxies:

  • Los cambios de precios de la API de Reddit en 2023 hicieron que el scraping de datos públicos sea la opción viable para equipos con presupuesto limitado
  • old.reddit.com es tu mejor aliado: HTML simple, estable, y fácil de parsear
  • Usa proxies residenciales para scraping intensivo; datacenter solo para volúmenes bajos
  • Respeta siempre los rate limits: monitoriza headers, implementa backoff exponencial, y pausa entre solicitudes
  • La progresión 429 → 403 es real: ignora los rate limits y tu IP será bloqueada
  • Cachea agresivamente — los datos de Reddit no cambian cada segundo
  • Solo accede a datos públicos; nunca evites autenticación ni scrapees contenido privado
  • Cuando la API oficial sea suficiente para tu caso de uso y presupuesto, úsala

Si necesitas extraer datos públicos de Reddit a escala, los planes de proxies residenciales de ProxyHat ofrecen la infraestructura necesaria con rotación automática de IPs y geo-targeting por país y ciudad. Para explorar qué ubicaciones están disponibles, consulta nuestra página de ubicaciones.

¿Listo para empezar?

Accede a más de 50M de IPs residenciales en más de 148 países con filtrado impulsado por IA.

Ver preciosProxies residenciales
← Volver al Blog