Quando iniziamo a estrarre dati dal web per i nostri progetti, l’entusiasmo di vedere i primi grafici si scontra spesso con un ostacolo frustrante: i server si stancano di noi.
È esattamente quello che succede quando proviamo a interrogare Google Trends tramite script.
Un attimo prima stai analizzando tranquillamente l’andamento di una parola chiave e, l’attimo dopo, lo script si blocca sputando un errore “429 Too Many Requests”.
In questa guida metteremo le mani sul codice usando pytrends, la libreria Python più usata per dialogare con Google Trends.
Vedremo come scaricare e visualizzare i dati, ma soprattutto capiremo come scrivere un codice robusto.
Invece di far crollare il programma al primo errore, gli insegneremo ad aspettare pazientemente e riprovare, affrontando concetti essenziali come il rate-limiting e l’idempotenza.
Esercizio
Titolo: Prime ricerche con pytrends
Testo:
Devi analizzare l’andamento delle ricerche per “intelligenza artificiale” in Italia negli ultimi 12 mesi. Scrivi uno script Python che:
-
Si connetta a Google Trends tramite
pytrends. -
Scarichi i dati giornalieri per la parola chiave specificata.
-
Mostri le prime 5 righe del DataFrame ottenuto.
Soluzione commentata:
# Esercizio 1
from pytrends.request import TrendReq
import pandas as pd
import time
print("Connessione a Google Trends...")
pytrends = TrendReq(hl='it-IT', tz=360)
kw_list = ["intelligenza artificiale"]
pytrends.build_payload(kw_list, timeframe='today 12-m', geo='IT')
print("Scaricamento dati (potrebbe richiedere qualche secondo)...")
time.sleep(3) # pausa precauzionale
df = pytrends.interest_over_time()
print("\nPrime 5 righe dei dati:")
print(df.head())
print(f"\nFormato del dataset: {df.shape}")
print(f"Periodo coperto: dalla {df.index.min()} alla {df.index.max()}")
Output atteso (esempio):
Connessione a Google Trends...
Scaricamento dati (potrebbe richiedere qualche secondo)...
Prime 5 righe dei dati:
intelligenza artificiale isPartial
date
2025-04-13 94 False
2025-04-20 81 False
2025-04-27 80 False
2025-05-04 92 False
2025-05-11 83 False
Formato del dataset: (53, 2)
Periodo coperto: dalla 2025-04-13 00:00:00 alla 2026-04-12 00:00:00
Analisi dei risultati ottenuti
I dati che vedi mostrano l’interesse settimanale (non giornaliero) per “intelligenza artificiale” in Italia:
Cosa significano questi numeri?
-
94 81, 80, 92, 83 = interesse relativo (scala 0-100)
-
100 = massimo interesse nel periodo considerato
-
83 significa che in quella settimana l’interesse era all’83% del picco
-
-
isPartial = False = dati definitivi (non provvisori)
⚠️ Perché hai dati settimanali invece che giornalieri?
Google Trends restituisce dati giornalieri solo per periodi ≤ 9 mesi.
Abbiamo richiesto 'today 12-m' (12 mesi), quindi automaticamente restituisce dati settimanali.
💡 Osservazione: Se vuoi dati giornalieri, usa timeframe='today 3-m' o 'today 6-m'.
Visualizziamo i dati
import matplotlib.pyplot as plt
# L'output che hai ottenuto
df = pd.DataFrame({
'intelligenza artificiale': [83, 73, 72, 80, 79],
'isPartial': [False, False, False, False, False]
}, index=pd.to_datetime(['2025-04-13', '2025-04-20', '2025-04-27', '2025-05-04', '2025-05-11']))
# Grafico
plt.figure(figsize=(10, 5))
plt.plot(df.index, df['intelligenza artificiale'], marker='o', linewidth=2, markersize=8)
plt.title('Interesse per "Intelligenza Artificiale" in Italia (aprile-maggio 2025)')
plt.xlabel('Settimana')
plt.ylabel('Interesse relativo (0-100)')
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# Statistiche di base
print(f"Valore massimo: {df['intelligenza artificiale'].max()}")
print(f"Valore minimo: {df['intelligenza artificiale'].min()}")
print(f"Media nel periodo: {df['intelligenza artificiale'].mean():.1f}")
print(f"Trend: {'in aumento' if df['intelligenza artificiale'].iloc[-1] > df['intelligenza artificiale'].iloc[0] else 'in diminuzione'}")

Valore massimo: 83
Valore minimo: 72
Media nel periodo: 77.4
Trend: in diminuzione
Perché Google Trends cambia automaticamente la granularità dei dati (giornaliera vs settimanale vs mensile) in base al periodo selezionato?
Google Trends adotta questa strategia per tre motivi principali:
-
Privacy: Dati troppo granulari su periodi lunghi potrebbero identificare singoli utenti
-
Stabilità statistica: Su periodi lunghi, i dati giornalieri avrebbero troppa varianza casuale
-
Performance: Ridurre la quantità di dati rende le API più veloci e scalabili
Regole di Google Trends:
-
≤ 9 mesi → dati giornalieri
-
9-24 mesi → dati settimanali
-
> 24 mesi → dati mensili
Tutto bellissimo finché funziona… ma se Google si arrabbia?
A questo punto abbiamo uno script che si connette, scarica i dati e ci disegna persino un bel grafico.
Perfetto, no? Sì, finché facciamo solo un paio di prove.
Ma cosa succede se chiediamo a Google troppi dati troppo in fretta, magari inserendo il nostro codice in un ciclo per analizzare decine di parole chiave diverse? Il server si accorge del traffico e ci chiude la porta in faccia, bloccando l’esecuzione e restituendo il temutissimo errore 429: Too Many Requests.
Invece di far crollare l’intero programma (costringendoci a riavviarlo a mano e perdere il lavoro fatto fino a quel momento), possiamo insegnare al nostro script a essere paziente e a “ripararsi da solo”.
Vediamo come gestire questa situazione in modo professionale.
Gestione dell’errore 429 con retry e attesa
Il codice che abbiamo usato
max_retries = 3
for attempt in range(max_retries):
try:
df = pytrends.interest_over_time()
break # se funziona, esce dal ciclo
except Exception as e:
if "429" in str(e) or "TooManyRequests" in str(e):
wait_time = (attempt + 1) * 30 # aspetta 30s, poi 60s, poi 90s
print(f"Troppe richieste. Attendo {wait_time} secondi...")
time.sleep(wait_time)
else:
raise e # se è un altro errore, lo rilancia
Analisi
Impostazione del numero massimo di tentativi
-
Proviamo massimo 3 volte prima di arrenderci
-
Se dopo 3 tentativi fallisce ancora, l’errore verrà propagato
Ciclo for con contatore dei tentativi
-
attempt = 0→ primo tentativo -
attempt = 1→ secondo tentativo -
attempt = 2→ terzo tentativo
Blocco try-except
try:
df = pytrends.interest_over_time()
break # successo! esco dal ciclo
-
Provo a scaricare i dati
-
Se riesco (
break), esco dal ciclo e continuo
Gestione specifica dell’errore 429
except Exception as e:
if "429" in str(e) or "TooManyRequests" in str(e):
-
Controllo se l’errore è proprio il 429
-
Converto l’errore in stringa e cerco “429” o “TooManyRequests”
Attesa progressiva (Linear Backoff) (strategia intelligente)
wait_time = (attempt + 1) * 30
Questa è la parte geniale:
| Tentativo (attempt) | Calcolo | Tempo attesa |
|---|---|---|
| 0 (primo fallimento) | (0+1) × 30 = 30 | 30 secondi |
| 1 (secondo fallimento) | (1+1) × 30 = 60 | 60 secondi |
| 2 (terzo fallimento) | (2+1) × 30 = 90 | 90 secondi |
💡 Perché attesa crescente?
-
Se Google è sovraccarico, attendere più a lungo aumenta le probabilità di successo
-
Evita di “infastidire” il server con troppi tentativi ravvicinati
Attesa effettiva
time.sleep(wait_time)
-
time.sleep()blocca l’esecuzione per X secondi -
Poi il ciclo riprova automaticamente
Rilancio di altri errori
else:
raise e
Schema del flusso di esecuzione
Tentativo 1 → Prova a scaricare
↓
Fallisce con 429?
↓ SÌ
Aspetta 30 secondi
↓
Tentativo 2 → Riprova a scaricare
↓
Fallisce con 429?
↓ SÌ
Aspetta 60 secondi
↓
Tentativo 3 → Riprova a scaricare
↓
Successo! → break ed esce
💡 Concetti teorici importanti
Rate Limiting
-
I server limitano il numero di richieste per IP in un intervallo di tempo
-
Protegge il server da overload e abusi
-
Google Trends permette circa 1 richiesta ogni 10-15 secondi
Backoff esponenziale
-
Strategia classica nelle API
-
Aumenta gradualmente il tempo di attesa
-
Riduce il carico sul server e migliora le chance di successo
Idempotenza
-
La richiesta
interest_over_time()è idempotente (stesso risultato se ripetuta) -
Perfetto per retry!
Idempotenza: concetto fondamentale per le API e i retry
L’idempotenza è un concetto cruciale nell’informatica, specialmente quando lavoriamo con API e retry automatici.
Definizione
Idempotenza = un’operazione che può essere eseguita più volte producendo sempre lo stesso risultato della prima esecuzione.
In altre parole: fare un’operazione 1 volta o 100 volte porta allo stesso effetto finale.
Esempio pratico con interest_over_time()
df1 = pytrends.interest_over_time() # Seconda chiamata (dopo 30 secondi) df2 = pytrends.interest_over_time() # Terza chiamata df3 = pytrends.interest_over_time() # Risultato: df1, df2, df3 sono IDENTICI (stessi dati, stessi valori)
Perché sono identici?
Perché stai solo LEGGENDO dati storici. Google Trends non cambia i dati del passato tra una richiesta e l’altra.
Confronto: operazioni idempotenti vs non idempotenti
| Tipo | Operazione | 1ª esecuzione | 2ª esecuzione | È idempotente? |
|---|---|---|---|---|
| LETTURA | interest_over_time() |
Legge dati | Legge stessi dati | ✅ SÌ |
| LETTURA | SELECT * FROM users |
Legge utenti | Legge stessi utenti | ✅ SÌ |
| SCRITTURA | UPDATE user SET saldo=100 |
Imposta saldo a 100 | Imposta di nuovo a 100 | ✅ SÌ |
| SCRITTURA | DELETE FROM user WHERE id=5 |
Cancella user 5 | User 5 già cancellato (nessun effetto) | ✅ SÌ |
| SCRITTURA | INSERT INTO log (msg) |
Aggiunge riga | Aggiunge SECONDA riga | ❌ NO |
| SCRITTURA | saldo += 50 (incremento) |
Aumenta di 50 | Aumenta di altri 50 | ❌ NO |
Esempio: transazione bancaria
Operazione NON idempotente (PERICOLOSA per retry)
def trasferisci_soldi(importo):
# SOTTRAE 50€ dal conto (MODIFICA lo stato)
conto.saldo -= importo
banca.invia_soldi(destinatario, importo)
Problema: Se la richiesta fallisce per timeout e riprovi automaticamente:
-
1° tentativo: sottrae 50€ ✅ (ma la risposta non arriva mai per un problema di rete)
-
2° tentativo: sottrae altri 50€ 💸
-
Risultato: Il cliente perde 100€ invece di 50€!
Operazione idempotente (SICURA per retry)
def imposta_saldo_a(importo, id_transazione):
# IMPOSTA il saldo a un valore specifico
if not transazione_gia_eseguita(id_transazione):
conto.saldo = importo
segna_transazione_come_eseguita(id_transazione)
Perché è sicuro?
-
La seconda volta che chiami
imposta_saldo_a(1000), il saldo rimane 1000 -
Puoi ritentare 100 volte senza conseguenze negative
Tabella riassuntiva delle operazioni HTTP comuni
| Metodo HTTP | È idempotente? | Esempio | Perché? |
|---|---|---|---|
| GET | ✅ SÌ | GET /trends/ai |
Legge solo dati |
| PUT | ✅ SÌ | PUT /user/5 nome=Mario |
Sostituisce l’intera risorsa |
| DELETE | ✅ SÌ | DELETE /user/5 |
Cancella (la seconda volta non cambia nulla) |
| POST | ❌ NO | POST /orders |
Crea un NUOVO ordine ogni volta |
| PATCH | ❌ NO | PATCH /user/5 saldo+50 |
Aggiornamento parziale (es. incremento) |
Perché le API bancarie spesso richiedono un idempotency_key (chiave di idempotenza) nelle richieste POST?
Perché i POST non sono idempotenti (creano nuove risorse).
Se un pagamento fallisce per timeout di rete e il client riprova, si rischierebbe di addebitare il cliente due volte.
La idempotency_key è un ID unico che il client invia. Il server memorizza le chiavi già processate: se riceve la stessa chiave due volte, restituisce lo stesso risultato senza eseguire l’operazione una seconda volta.
Esempio:
POST /pagamento
Idempotency-Key: abc-123
Corpo: {"importo": 50, "da": "Mario", "a": "Luigi"}
# Se la richiesta fallisce e viene reinviata con stessa chiave...
POST /pagamento
Idempotency-Key: abc-123 # STESSA CHIAVE!
Corpo: {"importo": 50, "da": "Mario", "a": "Luigi"}
# Il server risponde con lo stesso risultato senza riaddebitare
Domanda di riflessione
Cosa succederebbe se non gestissimo l’errore 429?
Il programma si interromperebbe immediatamente con un’eccezione non gestita, mostrando:
L’utente dovrebbe riavviare manualmente lo script, perdendo tutto il lavoro precedente.
Con la gestione dei retry, il programma si “ripara da solo” in modo automatico!
Riepilogo: cosa abbiamo imparato
-
Try-except per catturare errori specifici
-
Ciclo for con contatore per limitare i tentativi
-
Attesa progressiva (backoff) per rispettare i limiti del server
-
Break per uscire dal ciclo in caso di successo
-
Rilancio condizionale degli errori non gestibili
Questa è una best practice universale per qualsiasi API esterna (Twitter, Reddit, Google, ecc.)!
Commento Applicativo: Perché questo esercizio è prezioso
Dal punto di vista applicativo, questo esercizio non è un semplice tutorial su come fare un grafico a linee. È uno spaccato reale di Data Engineering applicata al Marketing e alla SEO. Le aziende usano script identici a questo per monitorare l’interesse verso i propri brand rispetto ai competitor, alimentando dashboard automatizzate che guidano le decisioni di business.
Le peculiarità che lo rendono interessante:
La granularità dinamica dei dati (Il tranello di Google): La particolarità più affascinante (e spesso frustrante per i data analyst) affrontata nell’articolo è il comportamento adattivo di Google Trends. Il fatto che passando da 9 a 12 mesi i dati cambino da giornalieri a settimanali costringe chi analizza i dati a prendere decisioni di design a monte: mi serve il quadro a lungo termine o il dettaglio quotidiano?
Passaggio da Script a “Sistema”: Lo script inizia come un compito scolastico (connetti, scarica, stampa) ma evolve rapidamente. L’implementazione del meccanismo di retry trasforma un banale script procedurale in un sistema resiliente. Insegna che nel mondo reale, scrivere codice funzionante è solo metà del lavoro; l’altra metà è scrivere codice che sappia gestire il fallimento dei sistemi esterni con cui comunica. L’incursione teorica sull’idempotenza fornisce infine la base teorica del perché possiamo permetterci di automatizzare questi ritentativi senza fare danni.
Percorso di studio: Web Scraping e Automazione
Dalla raccolta dati alle architetture scalabili: costruisci competenze reali e subito applicabili.
-
-
Step 1: Comprendi il web scraping
👉 Guida completa al web scraping e analisi dati
Capisci davvero cosa stai facendo: raccolta, pulizia e analisi in un unico flusso.
-
-
-
Step 2: Impara gli strumenti base
👉BeautifulSoup: primo progetto pratico
Parti dal parsing HTML: è la base di tutto il web scraping moderno.
-
-
-
Step 3: Scala le performance
👉Asyncio in Python (guida pratica)
Da lento a veloce: esegui richieste in parallelo e moltiplica l’efficienza del tuo scraper.
-
-
Step 4: Gestisci siti dinamici
👉Selenium: automazione web completa
Quando il sito usa JavaScript, serve un browser: qui entri nel livello avanzato.
💡 Strategia: BeautifulSoup → Asyncio → Selenium = stack completo per qualsiasi scenario.





