Come Estrarre Dati Pubblici da Reddit con i Proxy: Guida Completa 2025

Scopri come fare scraping di Reddit usando proxy residenziali, gestire i limiti di rate e accedere a post, commenti e subreddit dopo i cambiamenti API del 2023. Guida pratica con esempi Python e Node.js.

Come Estrarre Dati Pubblici da Reddit con i Proxy: Guida Completa 2025

Dal 2023, l'accesso ai dati di Reddit è cambiato radicalmente. I nuovi prezzi dell'API ufficiale hanno reso proibitivo per molti team raccogliere dati su larga scala, spingendo data engineer, analisti di sentiment e ricercatori di mercato verso lo scraping diretto delle pagine pubbliche. Se la tua applicazione ha bisogno di feed di subreddit, thread di commenti o pagine utente — e il budget per l'API non è illimitato — questa guida ti mostrerà come farlo in modo affidabile, responsabile e con l'infrastruttura proxy corretta.

Nota legale ed etica: Questa guida copre esclusivamente l'accesso a dati pubblici su Reddit. Rispetta sempre i Termini di Servizio di Reddit, il file robots.txt e le leggi applicabili (CFAA negli Stati Uniti, GDPR nell'UE). Non tentare mai di accedere a contenuti dietro login, di aggirare misure di sicurezza o di raccogliere dati personali sensibili. Quando esiste un'API ufficiale che soddisfa le tue esigenze e il tuo budget, usala.

Il Paesaggio API di Reddit: Cosa È Cambiato nel 2023

Fino al 2023, l'API di Reddit era essenzialmente gratuita per volumi ragionevoli. App di terze parti come Apollo, Rif is Fun e strumenti di ricerca potevano accedere a post, commenti e metadati senza costi significativi. A partire da luglio 2023, Reddit ha introdotto un modello a pagamento che addebita circa $0,24 per 1.000 richieste per l'accesso ai dati, con tier gratuiti limitati a 100 richieste al minuto e 10 milioni di richieste mensili per app non commerciali.

Per un team che analizza il sentiment su centinaia di subreddit o monitora migliaia di post al giorno, il costo può facilmente raggiungere migliaia di dollari al mese. Questo ha creato un divario: i progetti con budget ridotto o a scopo di ricerca si trovano di fronte a una scelta difficile — pagare tariffa piena, limitare drasticamente il volume, o cercare alternative.

Il Tier Gratuito vs. Il Tier a Pagamento

CaratteristicaTier GratuitoTier a Pagamento
Richieste/minuto100Variable (fino a 5.000+)
Richieste/mese10M (solo non commerciale)Contrattuale
Accesso contenuti NSFWNoSu richiesta
SupportoCommunityDedicato
Costo$0Da $0,24/1K richieste

È importante notare che il tier gratuito vieta esplicitamente l'uso commerciale. Se la tua azienda fa sentiment analysis per clienti o monitora brand per fini di marketing, il tier gratuito non è un'opzione legale secondo i ToS di Reddit.

Dati Pubblici Accessibili: Cosa Puoi Estrarre

Reddit ospita una quantità enorme di dati pubblici. Ecco cosa è accessibile senza autenticazione, navigando il sito come un utente anonimo:

  • Feed di subreddit: Post ordinati per hot, new, top, rising. L'URL https://old.reddit.com/r/programming/hot/ restituisce HTML pulito.
  • Pagine dei post: Titolo, autore, timestamp, score, numero di commenti, testo del post.
  • Thread di commenti: Commenti annidati con autore, testo, score, timestamp.
  • Pagine utente (pubbliche): Overview, post e commenti di un utente — se il profilo è pubblico.
  • Ricerca: https://old.reddit.com/search?q=keyword&sort=new — funziona senza login per query pubbliche.

Perché old.reddit.com È il Tuo Miglior Alleato

Il sito moderno di Reddit (www.reddit.com) è un'applicazione JavaScript pesante che carica contenuti dinamicamente. Il rendering richiede un browser headless o un'attesa complicata per i dati JSON inline. old.reddit.com, invece, serve HTML statico, leggero e facile da analizzare. Per lo scraping di dati pubblici, è quasi sempre la scelta migliore:

  • Nessun JavaScript necessario per il contenuto principale.
  • HTML strutturato in modo prevedibile con classi CSS stabili.
  • Meno richieste secondarie (no bundle JS, no API di tracking).
  • Minore probabilità di innescare meccanismi anti-bot aggressivi.
Usa sempre old.reddit.com come endpoint primario per lo scraping HTML. Riserva l'API ufficiale solo se hai bisogno di dati strutturati (JSON) e il budget lo permette.

Selezione del Proxy: Datacenter vs Residenziali vs Mobile

Reddit non filtra aggressivamente gli IP nella stessa misura di piattaforme come Instagram o Ticketmaster, ma ha meccanismi di rate limiting per IP e per User-Agent. La scelta del proxy dipende dal volume e dal pattern di accesso:

Tipo ProxyVolumi ConsigliatiProContro
DatacenterLow (<500 req/h)Economico, veloceIP facilmente identificabili, più soggetti a ban
Residenziali (rotanti)Medium-High (500-5000 req/h)IP reali, basso rischio di ban, geo-targetingCosto maggiore, latenza leggermente superiore
MobileSpecializzatoMassima affidabilità per app mobileCosto più alto, pool più piccolo

Per la maggior parte dei progetti di Reddit data scraping, i proxy residenziali rotanti rappresentano il miglior rapporto qualità-prezzo. Forniscono IP reali di ISP, rendendo ogni richiesta indistinguibile dal traffico di un utente normale. Per volumi molto bassi (monitoraggio di qualche subreddit), i datacenter possono bastare — ma monitora attentamente i tassi di errore.

Se hai bisogno di accedere a contenuti con restrizioni geografiche (alcuni subreddit o contenuti multimediali possono variare), il geo-targeting diventa essenziale. Con ProxyHat puoi specificare il paese dell'IP direttamente nello username:

  • user-country-US:pass@gate.proxyhat.com:8080 — IP statunitense
  • user-country-DE:pass@gate.proxyhat.com:8080 — IP tedesco
  • user-country-GB:pass@gate.proxyhat.com:8080 — IP britannico

Esempio Pratico: Scraping di Reddit con Python e Proxy Residenziali

Vediamo un esempio completo che estrae i post più recenti da un subreddit usando old.reddit.com, la libreria requests e un pool di proxy residenziali rotanti ProxyHat.

Configurazione Base

import requests
from bs4 import BeautifulSoup
import time
import json

# Configurazione ProxyHat — proxy residenziale rotante
PROXY_HOST = "gate.proxyhat.com"
PROXY_PORT = 8080
PROXY_USER = "USERNAME-country-US"  # sostituisci con le tue credenziali
PROXY_PASS = "PASSWORD"

PROXY_URL = f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
PROXIES = {
    "http": PROXY_URL,
    "https": PROXY_URL,
}

HEADERS = {
    "User-Agent": "ResearchBot/1.0 (+https://example.com/bot-info)",
    "Accept": "text/html,application/xhtml+xml",
    "Accept-Language": "en-US,en;q=0.9",
}

def fetch_subreddit(subreddit, sort="new", limit=25):
    """Scarica i post da un subreddit via old.reddit.com."""
    url = f"https://old.reddit.com/r/{subreddit}/{sort}/"
    
    try:
        response = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=15)
        response.raise_for_status()
    except requests.exceptions.HTTPError as e:
        print(f"HTTP Error {response.status_code}: {e}")
        return []
    except requests.exceptions.RequestException as e:
        print(f"Request Error: {e}")
        return []
    
    soup = BeautifulSoup(response.text, "html.parser")
    posts = []
    
    for thing in soup.select("div.thing"):
        title_el = thing.select_one("a.title")
        if not title_el:
            continue
        
        author_el = thing.select_one("a.author")
        score_el = thing.select_one("div.score.unvoted")
        comments_el = thing.select_one("a.comments")
        time_el = thing.select_one("time")
        
        posts.append({
            "title": title_el.get_text(strip=True),
            "url": title_el["href"],
            "author": author_el.get_text(strip=True) if author_el else "[deleted]",
            "score": score_el.get_text(strip=True) if score_el else "0",
            "comments": comments_el.get_text(strip=True) if comments_el else "0",
            "timestamp": time_el["datetime"] if time_el and time_el.has_attr("datetime") else None,
        })
    
    return posts[:limit]

# Utilizzo
data = fetch_subreddit("python", sort="hot")
for post in data:
    print(f"{post['score']} pts — {post['title'][:80]}")

Estrazione dei Commenti da un Thread

I commenti sono dove risiede il valore per l'analisi del sentiment. Ecco come estrarli:

def fetch_comments(subreddit, post_id, limit=100):
    """Estrae i commenti di un post da old.reddit.com."""
    url = f"https://old.reddit.com/r/{subreddit}/comments/{post_id}/"
    
    response = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=20)
    if response.status_code != 200:
        print(f"Errore {response.status_code} per post {post_id}")
        return []
    
    soup = BeautifulSoup(response.text, "html.parser")
    comments = []
    
    for comment in soup.select("div.comment"):
        author_el = comment.select_one("a.author")
        text_el = comment.select_one("div.md")
        score_el = comment.select_one("span.score")
        
        if not text_el:
            continue
        
        comments.append({
            "author": author_el.get_text(strip=True) if author_el else "[deleted]",
            "text": text_el.get_text(strip=True),
            "score": score_el.get_text(strip=True) if score_el else "0",
        })
        
        if len(comments) >= limit:
            break
    
    return comments

# Utilizzo — l'ID del post si trova nell'URL o nell'attributo data-fullname
comments = fetch_comments("python", "1d4f3xk")
print(f"Estratti {len(comments)} commenti")

Rotazione degli IP con Sessioni Sticky

Se hai bisogno di mantenere lo stesso IP per una sessione di navigazione (ad esempio, paginare attraverso un subreddit), usa il flag session nello username ProxyHat:

import uuid

def get_session_proxy(session_id=None):
    """Crea un URL proxy con sessione sticky."""
    if not session_id:
        session_id = str(uuid.uuid4())[:12]
    
    proxy_user = f"USERNAME-country-US-session-{session_id}"
    proxy_url = f"http://{proxy_user}:PASSWORD@{PROXY_HOST}:{PROXY_PORT}"
    return proxy_url, session_id

# Mantieni la stessa sessione per paginare
proxy_url, sid = get_session_proxy()
session_proxies = {"http": proxy_url, "https": proxy_url}

for page in range(1, 4):  # 3 pagine
    url = f"https://old.reddit.com/r/python/new/?count=25&after=t3_xxx"
    r = requests.get(url, headers=HEADERS, proxies=session_proxies, timeout=15)
    print(f"Pagina {page}: status {r.status_code}")
    time.sleep(2)  # rispetta i rate limit

Gestione dei Rate Limit: Il Pattern 429→403

Reddit applica rate limiting su più dimensioni. Comprendere come funzionano è essenziale per evitare blocchi:

Rate Limiting per IP

Ogni IP ha un limite di richieste per minuto. Per gli utenti anonimi (senza login), il limite è tipicamente di 30-60 richieste al minuto. Superare questo limite restituisce uno status 429 Too Many Requests.

Il Pattern 429→403

Ecco il comportamento pericoloso: se continui a inviare richieste dopo un 429, Reddit può escalare la risposta a 403 Forbidden. Questo significa che l'IP è stato temporaneamente o permanentemente bloccato. Non è solo un rate limit — è un ban.

La progressione tipica è:

  1. Richieste nel range normale → 200 OK
  2. Superato il rate limit per un breve periodo → 429 Too Many Requests
  3. Persistenza dopo il 429 → 403 Forbidden (IP bannato)

Per evitare questa escalation:

  • Non riprovare immediatamente dopo un 429. Attendi almeno 60 secondi.
  • Implementa un backoff esponenziale con jitter.
  • Ruota l'IP dopo un 429 se stai usando un pool di proxy.
  • Mai ignorare un 429 — è un avvertimento, non un suggerimento.
import random

def fetch_with_retry(url, max_retries=3):
    """Fetch con backoff esponenziale e gestione 429."""
    for attempt in range(max_retries):
        try:
            response = requests.get(
                url, headers=HEADERS, proxies=PROXIES, timeout=15
            )
            
            if response.status_code == 200:
                return response
            
            elif response.status_code == 429:
                # NON riprovare subito — attendi e ruota IP
                wait = (2 ** attempt) + random.uniform(0, 2)
                print(f"Rate limited (429). Attesa {wait:.1f}s, tentativo {attempt+1}")
                time.sleep(wait)
                continue  # il pool rotante assegnerà un nuovo IP
            
            elif response.status_code == 403:
                print("IP bannato (403). Interrompo per questo IP.")
                return None
            
            else:
                print(f"Status inaspettato: {response.status_code}")
                return None
                
        except requests.exceptions.RequestException as e:
            print(f"Errore di rete: {e}")
            time.sleep(2 ** attempt)
    
    return None

Rate Limiting per User-Agent

Reddit limita anche per User-Agent. Un User-Agent generico come python-requests/2.31 riceverà limiti molto più stringenti rispetto a un User-Agent descrittivo e unico. Questo è il motivo per cui devi sempre impostare un User-Agent personalizzato che identifichi il tuo bot e fornisca un modo per contattarti.

Esempio in Node.js: Monitoraggio di un Subreddit

Per i team che lavorano in JavaScript/TypeScript, ecco un esempio equivalente con axios e cheerio:

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

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

const HEADERS = {
  'User-Agent': 'ResearchBot/1.0 (+https://example.com/bot-info)',
  'Accept': 'text/html,application/xhtml+xml',
  'Accept-Language': 'en-US,en;q=0.9',
};

async function fetchSubreddit(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').each((_, thing) => {
      const title = $(thing).find('a.title').text().trim();
      const author = $(thing).find('a.author').text().trim() || '[deleted]';
      const score = $(thing).find('div.score.unvoted').text().trim() || '0';
      
      if (title) {
        posts.push({ title, author, score });
      }
    });
    
    return posts;
  } catch (error) {
    if (error.response?.status === 429) {
      console.log('Rate limited — attesa necessaria');
    }
    return [];
  }
}

fetchSubreddit('datascience').then(posts => {
  console.log(`Estratti ${posts.length} post`);
  posts.forEach(p => console.log(`${p.score} pts — ${p.title}`));
});

Best Practices per lo Scraping di Reddit

1. Imposta un User-Agent Realistico e Descrittivo

Mai usare l'User-Agent predefinito della libreria. Segui il formato suggerito da Reddit:

User-Agent: <platform>:<app-name>:<version> (by /u/reddit_username)
# Esempio:
User-Agent: linux:market-sentiment-bot:v1.2 (by /u/research_account)

Un User-Agent descrittivo riduce la probabilità di rate limiting aggressivo e dimostra buona fede.

2. Rispetta i Rate Limit — Con Margine

Se il limite è 60 richieste/minuto, punta a 30-40. Lascia margine per errori di rete, ritardi nel processing e variazioni nel rate limiting di Reddit. Un buon pattern è:

  • 1-2 secondi di pausa tra le richieste allo stesso IP.
  • Mai più di 10 richieste al secondo dal pool complessivo.
  • Backoff esponenziale su errori 429.

3. Cache in Modo Aggressivo

I dati di Reddit non cambiano ogni secondo. Un post popolare riceverà gli stessi voti per minuti. Implementa una cache locale:

import hashlib
import os
import json

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

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

def cached_get(url, ttl_seconds=300):
    """GET con cache su filesystem, TTL di default 5 minuti."""
    key = cache_key(url)
    cache_file = os.path.join(CACHE_DIR, f"{key}.json")
    
    if os.path.exists(cache_file):
        age = time.time() - os.path.getmtime(cache_file)
        if age < ttl_seconds:
            with open(cache_file) as f:
                return json.load(f)
    
    response = fetch_with_retry(url)
    if response and response.status_code == 200:
        data = {"url": url, "html": response.text, "timestamp": time.time()}
        with open(cache_file, "w") as f:
            json.dump(data, f)
        return data
    
    return None

4. Evita Scraping Durante le Ore di Puntezza

Se il tuo progetto non è time-critical, programma lo scraping nelle ore di minore traffico del server target (per subreddit statunitensi, le prime ore del mattino EST sono spesso meno cariche).

5. Monitora il Tasso di Successo

Tieni traccia di questi indicatori:

  • Success rate: percentuale di richieste con status 200. Sotto il 95%? Riduci la velocità.
  • Tasso di 429: se superi il 2-3%, stai andando troppo veloce.
  • Tasso di 403: qualsiasi 403 è un segnale di allarme — un IP è stato bannato.

Quando Usare l'API Ufficiale Invece dello Scraping

Lo scraping non è sempre la risposta. Considera l'API ufficiale di Reddit quando:

  • Hai un budget sufficiente e i costi sono giustificati dal valore dei dati.
  • Hai bisogno di dati strutturati in JSON — l'API restituisce JSON pulito, molto più facile da elaborare dell'HTML.
  • Hai bisogno di dati in tempo reale — l'API WebSocket di Reddit offre aggiornamenti in tempo reale.
  • Il tuo volume è basso — il tier gratuito copre fino a 100 req/min per usi non commerciali.
  • Stai costruendo un prodotto commerciale che verrà redistribuito — i ToS di Reddit richiedono un accordo commerciale.

Per approfondire l'uso dei proxy per lo scraping in generale, consulta la nostra guida ai proxy residenziali per il web scraping.

Considerazioni Etiche e Legali

Lo scraping di dati pubblici solleva questioni etiche importanti:

  • rispetta robots.txt: Controlla sempre https://www.reddit.com/robots.txt prima di iniziare. Attualmente, Reddit permette l'accesso a molte pagine pubbliche, ma le policy possono cambiare.
  • Non raccogliere dati personali oltre lo stretto necessario. Il contenuto dei post è pubblico, ma i pattern comportamentali degli utenti meritano attenzione.
  • GDPR e CCPA: Se elabori dati di utenti europei o californiani, devi rispettare le normative sulla privacy, anche se i dati sono pubblicamente accessibili.
  • Non sovraccaricare l'infrastruttura: Lo scraping responsabile non dovrebbe degradare l'esperienza degli utenti normali di Reddit.
  • Considera l'impatto: Se il tuo progetto potrebbe essere usato per manipolazione, disinformazione o stalking, non farlo.

Punti Chiave (Key Takeaways)

  • old.reddit.com è la via migliore per lo scraping HTML — HTML statico, nessun JavaScript, parsing prevedibile.
  • I proxy residenziali sono essenziali per volumi oltre le 500 richieste/ora — i datacenter vengono identificati e limitati più facilmente.
  • Il pattern 429→403 è reale — mai ignorare un 429, sempre implementare backoff esponenziale.
  • L'User-Agent personalizzato è obbligatorio — un UA descrittivo riduce i rate limit e dimostra buona fede.
  • Cache aggressivamente — i dati di Reddit non cambiano ogni secondo, e la cache riduce il carico su entrambi i lati.
  • L'API ufficiale è preferibile quando il budget lo permette — JSON pulito, stabilità, conformità ai ToS.
  • La conformità legale ed etica non è opzionale — rispetta robots.txt, GDPR, CCPA e i Termini di Servizio di Reddit.

Se sei pronto a iniziare con proxy residenziali ottimizzati per lo scraping, scopri i piani ProxyHat — pool rotanti con geo-targeting in oltre 190 paesi e sessioni sticky per flussi di navigazione continui.

Pronto per iniziare?

Accedi a oltre 50M di IP residenziali in oltre 148 paesi con filtraggio AI.

Vedi i prezziProxy residenziali
← Torna al Blog