Estrarre Google Flights con Python: dal codice agli avvisi sui prezzi

Ultimo aggiornamento il April 16, 2026

Google ha chiuso la sua API Flights nel 2018, ma i prezzi dei voli continuano a cambiare — per una singola tratta nazionale. Se vuoi accedere a questi dati in modo programmatico, lo scraping è praticamente l’unica strada disponibile.

Negli ultimi tempi ho testato a lungo diversi approcci per recuperare i dati dei voli da Google, e il panorama è cambiato in modo drastico — soprattutto dopo il lancio di SearchGuard da parte di Google nel gennaio 2025. In questa guida ti mostrerò come costruire un crawler Python funzionante per Google Flights con Playwright, come gestire le difese anti-bot che mettono in difficoltà la maggior parte delle persone e come trasformare il tutto in un tracker automatico dei prezzi con avvisi. Se preferisci saltare completamente il codice, ti mostrerò anche una scorciatoia no-code con che porta allo stesso risultato in circa due minuti.

Perché estrarre Google Flights con Python?

Google Flights domina la ricerca dei voli. La sua visibilità su mobile negli Stati Uniti è , superando tutti i principali OTA. Il mercato dei metasearch travel che lo supporta è stato valutato a e cresce con un CAGR del 30,2%. Eppure, da quando la QPX Express API è stata , non esiste un modo ufficiale per accedere a questi dati in modo programmatico.

Nel frattempo, i prezzi dei voli oscillano per lo stesso itinerario, con uno scarto medio di circa 20 dollari tra il prezzo minimo e quello massimo. Compagnie come Delta usano 77 fasce tariffarie per il dynamic pricing. La media dei voli A/R negli Stati Uniti all’inizio del 2026 si attesta a 408 dollari, con tariffe .

Piattaforma dominante, niente API, prezzi volatili. Ecco perché lo scraping di Google Flights con Python è diventato uno dei progetti più popolari su GitHub e nei forum di viaggio.

Ecco chi ne trae vantaggio e in che modo:

Tipo di utenteCaso d'usoVantaggio principale
Viaggiatori singoliMonitorare nel tempo i prezzi di tratte specificheRisparmiare in media $50 per volo
Agenzie di viaggioIntelligence sui prezzi della concorrenzaMonitoraggio in tempo reale della parità tariffaria
Team travel aziendaliOttimizzazione dei costi sulle tratteRisparmi del 10–30% sui viaggi business
SviluppatoriCreare app di confronto tariffeAccesso programmatico ai dati sui prezzi
RicercatoriAnalisi della volatilità dei prezzi delle compagnie aereeRicerca accademica e di mercato

Chi frequenta i forum è molto diretto sul motivo per cui ha scelto lo scraping: "Google Flights API was discontinued and I should use web scraping instead" è un commento che ricorre spesso. E il ritorno sull’investimento è reale — analizzando oltre 5 miliardi di quotazioni al giorno, mentre i dati Expedia del 2026 mostrano che prenotare con 8–15 giorni di anticipo fa risparmiare circa .

Quali dati puoi estrarre da Google Flights?

Una pagina dei risultati di Google Flights contiene una quantità sorprendentemente ricca di informazioni. Ecco cosa è in genere disponibile:

  • Nome della compagnia aerea (e logo)
  • Orario di partenza e codice aeroporto
  • Orario di arrivo e codice aeroporto
  • Durata totale del volo
  • Numero di scali e dettagli sulle coincidenze (aeroporto, durata, overnight sì/no)
  • Prezzo del biglietto (in base alla valuta)
  • Emissioni di CO2 (kg CO2e, con differenza percentuale rispetto ai voli tipici)
  • Classe di viaggio, numero del volo, modello dell’aereo
  • Dati sullo spazio per le gambe
  • Servizi aggiuntivi (Wi‑Fi, prese di corrente, streaming multimediale)
  • Indicatore del livello di prezzo (basso/normale/alto)
  • Avvisi di ritardo ("Often delayed by over 30 min")

La disponibilità dei dati varia in base alla tratta, alla data e al tipo di biglietto (solo andata vs andata e ritorno). Ecco come appare un singolo record di volo estratto in formato JSON:

1{
2  "search_date": "2026-04-16",
3  "route": "SFO-JFK",
4  "departure_date": "2026-05-15",
5  "flights": [
6    {
7      "airline": "United Airlines",
8      "flight_number": "UA123",
9      "departure_time": "08:00",
10      "departure_airport": "SFO",
11      "arrival_time": "16:35",
12      "arrival_airport": "JFK",
13      "duration_minutes": 335,
14      "stops": 0,
15      "price_usd": 287,
16      "price_level": "low",
17      "co2_kg": 156,
18      "co2_vs_typical": "-12%",
19      "travel_class": "Economy"
20    }
21  ]
22}

Configurare l’ambiente Python

Prima di scrivere qualsiasi codice di scraping, devi predisporre alcune cose.

Prerequisiti:

  • Difficoltà: Intermedia
  • Tempo richiesto: circa 1–2 ore per il tutorial completo
  • Cosa ti serve: Python 3.7+, conoscenze base di Python, un browser basato su Chrome

Installa le librerie necessarie

Useremo Playwright per l’automazione del browser (Google Flights è al 100% renderizzato in JavaScript — le richieste HTTP statiche non restituiscono nulla di utile), più alcuni strumenti di supporto:

1pip install playwright playwright-stealth pandas
2playwright install chromium
  • Playwright — automazione browser headless, gestisce il rendering JavaScript e include meccanismi di attesa integrati
  • playwright-stealth — attenua i segnali più comuni di rilevamento bot
  • pandas — per l’analisi dei dati e l’esportazione CSV in seguito

Perché Playwright invece di Selenium o requests

Google Flights non funziona con requests + BeautifulSoup da soli — il contenuto della pagina viene renderizzato interamente via JavaScript. Ti serve un browser vero.

FunzionePlaywrightSeleniumrequests + BS4
Rendering JSSupporto completoSupporto completoNessuno
Velocità42% più veloce in generaleBaseN/D per questo caso d'uso
Supporto asyncNativoSolo sequenzialeN/D
Uso memoria30% in menoPiù altoMinimo
Elusione anti-botBuona (con stealth)Più facile da rilevareN/D

Playwright è più veloce, più moderno e offre un supporto async migliore. Per Google Flights, in particolare, è la scelta più solida.

Passo dopo passo: come estrarre Google Flights con Python

Questa è la parte centrale del tutorial. Costruiremo lo scraper un pezzo alla volta.

google-flights-scraping-workflow.webp

Passo 1: definisci le tue classi dati

Inizia strutturando i parametri di ricerca e i dati dei voli con le dataclass di Python. In questo modo il codice resta pulito ed è più facile ampliarlo in seguito.

1from dataclasses import dataclass, field
2from typing import Optional, List
3@dataclass
4class SearchParams:
5    origin: str          # e.g., "SFO"
6    destination: str     # e.g., "JFK"
7    departure_date: str  # e.g., "2026-05-15"
8    return_date: Optional[str] = None
9    trip_type: str = "one-way"  # "one-way" or "round-trip"
10    travel_class: str = "economy"
11@dataclass
12class FlightData:
13    airline: str = ""
14    departure_time: str = ""
15    arrival_time: str = ""
16    duration: str = ""
17    stops: str = ""
18    price: str = ""
19    co2_emissions: str = ""

Ogni campo corrisponde direttamente a ciò che estrarremo dalla pagina. Avere questa struttura fin dall’inizio evita di dover passare in giro dizionari disordinati più avanti.

Passo 2: capire la struttura dell’URL di Google Flights

Google Flights codifica i parametri di ricerca usando Protobuf codificato in Base64 nel parametro URL tfs. Puoi provare a decodificare questo formato, oppure scegliere l’approccio più semplice: costruire un URL in linguaggio naturale.

Il metodo più semplice è usare il formato della query di ricerca:

1https://www.google.com/travel/flights?q=flights+from+SFO+to+JFK+on+2026-05-15&curr=USD

Per avere più controllo, puoi generare gli URL in modo programmatico:

1def build_flights_url(origin: str, destination: str, date: str) -> str:
2    base = "https://www.google.com/travel/flights"
3    query = f"flights from {origin} to {destination} on {date}"
4    return f"{base}?q={query.replace(' ', '+')}&curr=USD"

L’alternativa — fare reverse engineering della codifica Protobuf — dà un controllo più preciso, ma si rompe ogni volta che Google modifica il formato interno. Librerie come su GitHub usano la decodifica Protobuf per evitare del tutto il parsing dell’HTML, ma si tratta di un approccio più avanzato.

Passo 3: avvia il browser e apri Google Flights

Ecco la configurazione Playwright. Usiamo playwright-stealth per ridurre il rischio di rilevamento fin dall’inizio.

1import asyncio
2from playwright.async_api import async_playwright
3from playwright_stealth import Stealth
4async def scrape_flights(params: SearchParams) -> List[FlightData]:
5    async with Stealth().use_async(async_playwright()) as pw:
6        browser = await pw.chromium.launch(
7            headless=True,
8            args=[
9                "--disable-blink-features=AutomationControlled",
10                "--disable-dev-shm-usage",
11                "--no-first-run",
12            ]
13        )
14        context = await browser.new_context(
15            viewport={"width": 1920, "height": 1080},
16            user_agent=(
17                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
18                "AppleWebKit/537.36 (KHTML, like Gecko) "
19                "Chrome/125.0.0.0 Safari/537.36"
20            ),
21            locale="en-US",
22            timezone_id="America/New_York",
23        )
24        # Pre-imposta il cookie di consenso per saltare il popup
25        await context.add_cookies([{
26            "name": "SOCS",
27            "value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
28            "domain": ".google.com",
29            "path": "/"
30        }])
31        page = await context.new_page()

Stiamo eseguendo tutto in headless per la produzione (passa a headless=False per il debug), impostando un viewport e un user agent realistici, e preconfigurando il cookie SOCS per saltare il popup del consenso — torneremo su questo nella sezione anti-bot.

Passo 4: vai alla pagina dei risultati

Carica l’URL costruito e attendi che appaiano i risultati dei voli:

1        url = build_flights_url(
2            params.origin, params.destination, params.departure_date
3        )
4        await page.goto(url, wait_until="networkidle")
5        # Attendi il caricamento dei risultati dei voli
6        await page.wait_for_selector(
7            "li.pIav2d", timeout=15000
8        )

Se qui vai in timeout, di solito significa che il popup di consenso ha bloccato la pagina (vedi il fix con il cookie nel Passo 3) oppure che Google sta servendo un CAPTCHA. Nella sezione anti-bot vedremo entrambe le situazioni.

Passo 5: carica tutti i risultati dei voli

Google Flights nasconde altri risultati dietro il pulsante "Show more flights". Devi cliccarlo più volte finché tutti i voli non sono visibili:

1        # Clicca "Show more flights" finché tutti i risultati non sono caricati
2        while True:
3            try:
4                more_button = page.locator(
5                    'button:has-text("Show more flights")'
6                )
7                if await more_button.is_visible(timeout=3000):
8                    await more_button.click()
9                    await page.wait_for_timeout(2000)
10                else:
11                    break
12            except Exception:
13                break

Questo ciclo clicca il pulsante, attende 2 secondi che i nuovi risultati vengano renderizzati e si ferma quando il pulsante non è più visibile. Nei miei test, la maggior parte delle tratte mostra 1–3 pagine di risultati.

Passo 6: estrai i dati dei voli con i selettori CSS

Ora analizziamo i dati reali dei voli dalla pagina caricata. Ecco i selettori (verificati ad aprile 2026 — vedi la sezione manutenzione più sotto per capire perché questa data è importante):

1        flights = []
2        cards = await page.query_selector_all("li.pIav2d")
3        for card in cards:
4            flight = FlightData()
5            # Nome compagnia
6            airline_el = await card.query_selector(
7                "div.sSHqwe span:not([class])"
8            )
9            if airline_el:
10                flight.airline = (await airline_el.inner_text()).strip()
11            # Orario di partenza
12            dep_el = await card.query_selector(
13                'span[aria-label*="Departure time"]'
14            )
15            if dep_el:
16                flight.departure_time = (await dep_el.inner_text()).strip()
17            # Orario di arrivo
18            arr_el = await card.query_selector(
19                'span[aria-label*="Arrival time"]'
20            )
21            if arr_el:
22                flight.arrival_time = (await arr_el.inner_text()).strip()
23            # Durata
24            dur_el = await card.query_selector("div.gvkrdb")
25            if dur_el:
26                flight.duration = (await dur_el.inner_text()).strip()
27            # Scali
28            stops_el = await card.query_selector("div.EfT7Ae span")
29            if stops_el:
30                flight.stops = (await stops_el.inner_text()).strip()
31            # Prezzo
32            price_el = await card.query_selector(
33                "div.FpEdX span"
34            )
35            if price_el:
36                flight.price = (await price_el.inner_text()).strip()
37            # Emissioni di CO2
38            co2_el = await card.query_selector("div.O7CXue")
39            if co2_el:
40                flight.co2_emissions = (
41                    await co2_el.get_attribute("aria-label") or ""
42                ).strip()
43            flights.append(flight)
44        await browser.close()
45        return flights

Attenzione: classi come pIav2d, sSHqwe e FpEdX sono generate dal Closure Compiler di Google e possono cambiare da una build all’altra. I selettori basati su aria-label sono più stabili. Più avanti vedremo una strategia completa di manutenzione.

Passo 7: salva i risultati in JSON o CSV

Infine, salva i dati estratti con un timestamp (fondamentale per il price tracking successivo):

1import json
2from datetime import datetime
3from dataclasses import asdict
4async def main():
5    params = SearchParams(
6        origin="SFO",
7        destination="JFK",
8        departure_date="2026-05-15"
9    )
10    flights = await scrape_flights(params)
11    output = {
12        "search_date": datetime.now().isoformat(),
13        "params": asdict(params),
14        "flights": [asdict(f) for f in flights],
15    }
16    with open("flights.json", "w") as f:
17        json.dump(output, f, indent=2)
18    # Salva anche in CSV
19    import pandas as pd
20    df = pd.DataFrame([asdict(f) for f in flights])
21    df["search_date"] = datetime.now().isoformat()
22    df["route"] = f"{params.origin}-{params.destination}"
23    df.to_csv("flights.csv", index=False)
24    print(f"Scraped {len(flights)} flights")
25asyncio.run(main())

Eseguendo questo script dovresti ottenere flights.json e flights.csv con i risultati. Nei miei test, una ricerca SFO-JFK restituisce in genere 30–80 opzioni di volo e richiede circa 15–20 secondi per completarsi.

Guida di sopravvivenza anti-bot per lo scraping di Google Flights

La maggior parte dei tutorial si ferma qui. La maggior parte degli scraper fallisce qui. Google ha introdotto , e questo ha rotto quasi tutti gli scraper SERP da un giorno all’altro. Google lo descrive come "il risultato di decine di migliaia di ore-uomo e milioni di dollari di investimento". Google Flights è classificato con difficoltà per lo scraping.

Nessun articolo concorrente affronta questo aspetto in profondità, eppure è il motivo numero uno per cui gli scraper smettono di funzionare. Ecco contro cosa devi misurarti e come gestirlo.

anti-bot-survival-guide.webp

Ritardi casuali tra le richieste

La difesa più semplice contro il rate limiting. Due righe di codice, efficacia media:

1import time
2import random
3time.sleep(random.uniform(3, 7))

Aggiungile tra una navigazione e l’altra. Intervalli fissi (per esempio sempre 5 secondi esatti) sono un segnale d’allarme — meglio randomizzare.

Rotazione dello User-Agent

Inviare sempre la stessa stringa user-agent è un indizio facile da individuare. Ruotala prendendola da una lista:

1import random
2USER_AGENTS = [
3    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36",
4    "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 Safari/605.1.15",
5    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
6    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
7]
8user_agent = random.choice(USER_AGENTS)

Bypass del rilevamento headless

Google controlla il flag navigator.webdriver e altri segnali di automazione. La libreria playwright-stealth gestisce già gran parte di questi aspetti, ma dovresti comunque impostare anche gli argomenti di avvio mostrati nel Passo 3. I flag chiave:

1args=[
2    "--disable-blink-features=AutomationControlled",
3    "--disable-dev-shm-usage",
4    "--no-first-run",
5]

Questo ti aiuta a superare il rilevamento di base. SearchGuard va più a fondo — monitora velocità del mouse, tempi di pressione dei tasti e pattern di scrolling — ma per scraping a volume moderato la modalità stealth, insieme a ritardi realistici, è di solito sufficiente.

Rotazione proxy: datacenter vs residential

Per qualsiasi cosa oltre poche ricerche, ti serviranno dei proxy. La differenza conta:

FunzioneProxy datacenterProxy residential
Velocità100–1.000 Mbps10–100 Mbps
Tasso di successo (Google)20–40%85–95%
Costo$0,10–$0,50/IP/mese$5–$15/GB
Rischio di rilevamentoAltoMolto basso

I proxy residential sono circa quando si estraggono siti protetti. Prezzi dei provider nel 2026: Smartproxy da $7/GB, Bright Data $8,40/GB, Oxylabs $8/GB.

Puoi aggiungere un proxy a Playwright così:

1browser = await pw.chromium.launch(
2    proxy={"server": "http://proxy-host:port",
3           "username": "user", "password": "pass"}
4)

Gli utenti segnalano in modo ricorrente il popup "I agree to terms" come blocco: "first google will show you the 'I agree to terms and conditions' popup." La soluzione più pulita è preimpostare il cookie SOCS (come visto nel Passo 3). Se non basta, clicca direttamente sul popup:

1try:
2    accept_btn = page.locator('button:has-text("Accept all")')
3    if await accept_btn.is_visible(timeout=3000):
4        await accept_btn.click()
5        await page.wait_for_timeout(1000)
6except Exception:
7    pass  # Nessun popup presente

Nota: il testo del pulsante varia in base alla lingua — "Alle akzeptieren" in tedesco, "Tout accepter" in francese.

Riferimento rapido anti-bot

TecnicaDifficoltàEfficaciaRichiede codice?
Ritardi casuali (2–7s)BassaMedia2 righe
Rotazione user-agentBassaMedia5 righe
Bypass rilevamento headlessMediaAltaArgomenti di avvio Playwright
Plugin playwright-stealthMedia60–80% su siti basepip install
Rotazione proxy (datacenter)MediaMediaConfigurazione
Rotazione proxy (residential)Media85–95% di successoConfigurazione
Preimpostazione consenso cookie (SOCS)BassaRichiesto1 riga

Per le frequenze sicure consigliate: mantieni intervalli di 10–20 secondi tra le richieste con rotazione IP. Le soglie di Google sono circa 100 richieste/minuto per IP prima di ricevere un 429, e volumi sostenuti oltre 1.000 richieste/giorno per IP possono attivare blocchi temporanei.

Perché i selettori di Google Flights continuano a rompersi (e come risolvere)

È il problema n. 1, di gran lunga. I thread nei forum sono pieni di varianti del tipo "all I get back is 14 empty lists." Ogni tutorial ti dà dei selettori. Nessuno spiega perché smettono di funzionare.

Perché i selettori di Google Flights cambiano

Dipende da tre fattori:

  1. Offuscamento tramite Closure Compiler. Google usa per generare nomi di classe come BVAVmf e YMlIz tramite goog.setCssNameMapping(). Questi cambiano a ogni build — a volte ogni settimana.

  2. A/B testing. Utenti diversi vedono contemporaneamente strutture HTML diverse. Il tuo scraper può funzionare sulla tua macchina ma fallire per qualcuno in un’altra regione.

  3. Differenze di localizzazione. Gli utenti UE vedono termini, layout e perfino campi dati diversi rispetto a quelli negli Stati Uniti.

Scrivi selettori più robusti

Preferisci selettori legati al significato invece che all’aspetto:

1# Fragile — si rompe a ogni build
2price_el = await card.query_selector("div.BVAVmf > div.YMlIz")
3# Più resistente — legato alle etichette di accessibilità
4dep_el = await card.query_selector('span[aria-label*="Departure time"]')
5# Anch'esso resistente — matching basato sul testo
6more_btn = page.locator('button:has-text("Show more flights")')

Gerarchia di stabilità dei selettori (dal più stabile al meno stabile):

  1. Attributi aria-label — legati all’accessibilità, raramente modificati
  2. Attributi data-* — aggiunti esplicitamente per la funzionalità
  3. Attributi role — i ruoli ARIA sono semantici
  4. Selettori basati sul testo — corrispondono al contenuto visibile
  5. Matching parziale delle classi — ad es. [class*="price"]
  6. Nomi di classe completi offuscati — da evitare quando possibile

Aggiungi una funzione di validazione

Non lasciare che selettori rotti producano dati vuoti senza accorgertene. Intercettali subito:

1import logging
2logger = logging.getLogger(__name__)
3def validate_flight(data: FlightData) -> bool:
4    required = ["airline", "price", "departure_time",
5                "arrival_time", "duration"]
6    valid = True
7    for field_name in required:
8        if not getattr(data, field_name, ""):
9            logger.warning(
10                f"Missing '{field_name}' — selectors may need updating"
11            )
12            valid = False
13    return valid

Esegui questa verifica su ogni volo estratto. Se inizi a vedere warning, è il momento di ispezionare la pagina e aggiornare i selettori.

Strategia di manutenzione dei selettori

  • Controlla i selettori ogni mese, oppure subito quando la qualità dei risultati cala
  • Tieni i selettori in un dizionario di configurazione separato, così è facile aggiornarli
  • I selettori di questo articolo sono stati verificati l’ultima volta: aprile 2026
  • Valuta la libreria come alternativa — usa la decodifica Protobuf invece dei selettori CSS, aggirando del tutto questo problema (anche se resta comunque esposta quando Google cambia i formati dati interni)

Da scraping una tantum a tracker automatico dei prezzi di Google Flights

La maggior parte dei tutorial finisce con "salva in JSON". Il titolo di questo articolo parla di "Price Alerts". È il momento di mantenere la promessa.

scraper-to-price-tracker-sfo-jfk.webp

Pianifica l’esecuzione automatica dello scraper

Opzione 1: libreria schedule di Python (la più semplice, multipiattaforma):

1import schedule
2import time
3def run_scraper():
4    asyncio.run(main())
5schedule.every().day.at("06:00").do(run_scraper)
6schedule.every().day.at("18:00").do(run_scraper)
7while True:
8    schedule.run_pending()
9    time.sleep(60)

Opzione 2: cron job (Linux/Mac):

1# Esegui alle 6:00 e alle 18:00 ogni giorno
20 6,18 * * * cd /path/to/scraper && python scraper.py

Opzione 3: Utilità di pianificazione di Windows — crea un’attività di base che esegue python scraper.py secondo la frequenza che preferisci.

Il compromesso è che tutte queste soluzioni richiedono una macchina sempre accesa. Se lo fai girare su un laptop che va in sospensione, perderai alcune esecuzioni.

Conserva lo storico dei prezzi

Passa dalla sovrascrittura di un file JSON all’inserimento progressivo in un database SQLite:

1import sqlite3
2from datetime import datetime
3def init_db():
4    conn = sqlite3.connect("flights.db")
5    conn.execute("""
6        CREATE TABLE IF NOT EXISTS flights (
7            id INTEGER PRIMARY KEY AUTOINCREMENT,
8            scrape_date TEXT NOT NULL,
9            route TEXT NOT NULL,
10            airline TEXT,
11            departure_time TEXT,
12            arrival_time TEXT,
13            duration TEXT,
14            stops TEXT,
15            price_usd REAL,
16            co2_emissions TEXT
17        )
18    """)
19    conn.execute(
20        "CREATE INDEX IF NOT EXISTS idx_route_date "
21        "ON flights(route, scrape_date)"
22    )
23    conn.commit()
24    return conn
25def save_flight(conn, route: str, flight: FlightData):
26    price_num = float(
27        flight.price.replace("$", "").replace(",", "")
28    ) if flight.price else None
29    conn.execute(
30        "INSERT INTO flights "
31        "(scrape_date, route, airline, departure_time, "
32        "arrival_time, duration, stops, price_usd, co2_emissions) "
33        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
34        (datetime.now().isoformat(), route, flight.airline,
35         flight.departure_time, flight.arrival_time,
36         flight.duration, flight.stops, price_num,
37         flight.co2_emissions)
38    )
39    conn.commit()

Dopo una settimana di scraping due volte al giorno, avrai abbastanza dati per iniziare a cogliere dei trend.

Analizza l’andamento dei prezzi e imposta avvisi

Trova l’opzione più economica nei dati storici:

1import pandas as pd
2import sqlite3
3conn = sqlite3.connect("flights.db")
4df = pd.read_sql_query(
5    "SELECT * FROM flights WHERE route = 'SFO-JFK'", conn
6)
7summary = df.groupby("scrape_date")["price_usd"].agg(
8    ["min", "max", "mean"]
9)
10cheapest = df.loc[df["price_usd"].idxmin()]
11print(
12    f"Più economico: ${cheapest['price_usd']:.0f} il "
13    f"{cheapest['scrape_date']} ({cheapest['airline']})"
14)

Attiva un avviso email quando il prezzo scende sotto la soglia desiderata:

1import smtplib
2from email.mime.text import MIMEText
3def send_price_alert(route, price, threshold, recipient):
4    msg = MIMEText(
5        f"Price drop alert! {route}: ${price:.0f} "
6        f"(below your ${threshold:.0f} threshold)"
7    )
8    msg["Subject"] = f"Flight Deal: {route} at ${price:.0f}"
9    msg["From"] = "alerts@example.com"
10    msg["To"] = recipient
11    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
12        server.login("alerts@example.com", "your_app_password")
13        server.send_message(msg)
14# Dopo ogni scraping, controlla se ci sono offerte
15min_price = df["price_usd"].min()
16threshold = 250
17if min_price < threshold:
18    send_price_alert("SFO-JFK", min_price, threshold,
19                     "you@email.com")

Per una frequenza di scraping consigliata: due volte al giorno è sufficiente per il monitoraggio personale dei prezzi (la tempistica casuale riduce il rischio di rilevamento). Ogni 4–6 ore se stai monitorando per un’azienda. Ogni ora solo durante i periodi di forte promozione, e solo temporaneamente.

La strada più semplice: lo Scheduled Scraper di Thunderbit

Se gestire cron job, un server sempre acceso e configurazioni proxy ti sembra più infrastruttura di quanta tu ne voglia mantenere, lo copre lo stesso caso d’uso senza quel peso operativo. Descrivi in linguaggio naturale l’intervallo di scraping, inserisci gli URL di Google Flights e lo scraper gira automaticamente sull’infrastruttura cloud di Thunderbit — con gestione anti-bot integrata ed export diretto verso . Non sostituisce completamente l’approccio Python (rinunci alla personalizzazione), ma per il caso d’uso specifico "voglio un foglio di calcolo con il monitoraggio dei prezzi", è la strada più rapida. Puoi provarlo nel .

Quando Python è eccessivo: modi no-code per estrarre Google Flights

Dopo aver costruito tutto questo, lo dico con sincerità: ci sono parecchi pezzi in gioco. Non tutti hanno bisogno di un livello così alto di controllo. I selettori si rompono, i proxy vanno ruotati, i cron job vanno monitorati. Se il tuo obiettivo è "portare regolarmente i prezzi dei voli in un foglio di calcolo", esistono opzioni più veloci.

Confronto: Python fai-da-te vs servizi API vs Thunderbit

ApproccioTempo di configurazioneServe codiceGestione anti-botPianificazioneCosto
Playwright fai-da-te (questo tutorial)1–2 orePython (intermedio)Configurazione manualeManuale (cron)Gratis + costi proxy
Endpoint Google Flights di SerpApi15 minSolo chiamate APIGestitaVia APIcirca $50+/mese
Estensione Chrome di Thunderbit2 minNessunoScraping cloudScheduler integratoPiano gratuito disponibile

Una nota su SerpApi: Google , sostenendo che le loro richieste siano aumentate del 25.000% in due anni. Questa incertezza legale vale la pena considerarla se stai valutando provider API.

Come Thunderbit estrae Google Flights

Apri i risultati di ricerca di Google Flights in Chrome, clicca il pulsante "AI Suggest Fields" di Thunderbit — l’AI legge la pagina e suggerisce colonne come compagnia, prezzo, orario di partenza, scali — controlla i campi proposti e clicca "Scrape." I risultati compaiono in una tabella che puoi esportare in Excel, Google Sheets, Airtable o Notion — tutto nel .

Per il caso d’uso del price tracking, in particolare, lo Scheduled Scraper di Thunderbit e il (capace di gestire 50 pagine in contemporanea) sostituiscono tutta l’infrastruttura fatta di cron, proxy e server.

Python ti dà pieno controllo e personalizzazione illimitata. Thunderbit ti offre velocità e manutenzione zero. Scegli in base al tuo obiettivo reale. Se vuoi approfondire gli approcci no-code allo scraping, dai un’occhiata alla nostra guida sui .

flight-data-options-comparison.webp

È legale fare scraping di Google Flights? Cosa devi sapere

Gli utenti nei forum sollevano spesso questo punto: "scraping Google Flights directly violates the TOS that Google has." Preoccupazione legittima — soprattutto perché l’API è stata dismessa e non esiste un’alternativa ufficialmente autorizzata.

Violazione dei TOS vs responsabilità legale

I Termini di servizio di Google (aggiornati il 22 maggio 2024) stabiliscono che gli utenti non devono "accedere o utilizzare i Servizi o qualsiasi contenuto tramite mezzi automatizzati (come robot, spider o scraper)." La violazione dei ToS è un inadempimento contrattuale (materia civile) — non equivale automaticamente a violare la legge.

Il precedente legale chiave: hiQ v. LinkedIn (Nono Circuito, 2022) ha stabilito che lo scraping di dati pubblicamente disponibili non viola il Computer Fraud and Abuse Act (CFAA). Tuttavia, il caso si è chiuso con un accordo, e la causa di dicembre 2025 contro SerpApi usa una teoria legale diversa — la Sezione 1201 del DMCA (elusione delle misure tecnologiche di protezione) — potenzialmente più seria.

Best practice per uno scraping responsabile

  • Limita la frequenza delle richieste — ritardi di 10–20 secondi con rotazione IP
  • Non estrarre dati personali — i prezzi dei voli sono dati aggregati mostrati pubblicamente
  • Non aggirare i CAPTCHA in modo programmatico (qui entra in gioco il rischio DMCA)
  • Usa i dati per ricerca personale, non per costruire un prodotto commerciale concorrente senza licenza adeguata
  • Valuta le API ufficiali dove disponibili

Fonti dati alternative

Se per il tuo caso d’uso lo scraping ti sembra troppo rischioso, esistono opzioni API legittime:

ProviderCostoPiano gratuitoNote
SerpApi$75–$3.750+/mese250 ricerche/meseJSON di Google Flights diretto (sotto esame legale)
Kiwi TequilaGratis (modello affiliate)IllimitatoOttimo per startup e test
AmadeusPay-as-you-go2.000 richieste/meseOltre 400 compagnie, possibilità di prenotazione
SkyscannerPersonalizzatoRichiede approvazione52 mercati, 30 lingue

Abbiamo scritto un approfondimento più dettagliato sulle se vuoi una panoramica completa.

Conclusione e punti chiave

È stato tanto, lo so. Ecco ciò che conta davvero:

  • Python + Playwright è l’approccio più flessibile per estrarre Google Flights, ma richiede manutenzione continua
  • Le difese anti-bot (ritardi, rotazione user-agent, proxy residential) non sono opzionali — sono fondamentali per l’affidabilità, soprattutto dopo SearchGuard
  • I selettori si rompono spesso — usa aria-label e selettori basati sul testo quando possibile, valida l’output e tieni una routine di manutenzione
  • Automatizza con schedule o cron per trasformare uno scraping una tantum in un vero tracker dei prezzi con storico e avvisi email
  • offre un’alternativa no-code con pianificazione integrata, scraping cloud e gestione anti-bot — ideale se il tuo obiettivo è un foglio di calcolo per il tracking dei prezzi più che un progetto di programmazione
  • Rispetta i limiti legali — limita la frequenza, estrai solo dati pubblici e valuta alternative API per usi commerciali

Scarica il codice di questo tutorial oppure installa l’ per la strada più rapida. In ogni caso, passerai a monitorare i prezzi dei voli invece di aggiornare manualmente Google Flights.

Per altre tecniche di scraping in Python, consulta le nostre guide su e sui .

FAQ

1. Posso estrarre Google Flights senza Python?

Sì. Servizi API come SerpApi e Kiwi Tequila forniscono dati strutturati sui voli tramite chiamate API (senza bisogno di automazione browser). Per un approccio completamente no-code, può estrarre direttamente i risultati di Google Flights dal browser con campi suggeriti dall’AI ed export con un clic.

2. Google blocca lo scraping dei voli?

Google usa sistemi di rilevamento bot (SearchGuard), CAPTCHA e rate limiting. Con misure anti-bot adeguate — ritardi casuali, rotazione user-agent, proxy residential e impostazioni stealth del browser — puoi mantenere uno scraping affidabile a volumi moderati. Consulta la sezione anti-bot sopra per tecniche e soglie specifiche.

3. Con che frequenza dovrei estrarre Google Flights per il price tracking?

Due volte al giorno (in orari casuali) bastano per il monitoraggio personale dei prezzi e mantengono basso il rischio di rilevamento. Per il monitoraggio business, ogni 4–6 ore con rotazione dei proxy. Evita lo scraping orario, salvo brevi periodi di promozione delle tariffe — aumenta molto la probabilità di blocco.

4. Esiste una API gratuita di Google Flights?

L’API ufficiale Google QPX Express è stata . Non esiste un sostituto ufficiale gratuito. L’opzione più vicina e gratuita è la (modello affiliate, ricerche illimitate). SerpApi offre 250 ricerche gratuite al mese. Per la maggior parte degli utenti, lo scraping o uno strumento no-code come Thunderbit è la strada più pratica.

5. Perché i selettori CSS di Google Flights continuano a restituire dati vuoti?

Google usa il Closure Compiler per generare nomi di classe offuscati che cambiano a ogni build. Anche gli A/B test e le differenze di localizzazione fanno variare la struttura HTML tra utenti diversi. La soluzione: usare attributi aria-label e selettori basati sul testo invece dei nomi di classe, aggiungere una funzione di validazione per intercettare presto i problemi e controllare i selettori ogni mese. Vedi la sezione sulla manutenzione dei selettori per una strategia dettagliata.

Scopri di più

Ke
Ke
CTO @ Thunderbit. Ke is the person everyone pings when data gets messy. He's spent his career turning tedious, repetitive work into quiet little automations that just run. If you've ever wished a spreadsheet could fill itself in, Ke has probably already built the thing that does it.
Indice

Prova Thunderbit

Estrai lead e altri dati in soli 2 clic. Potenziato dall’AI.

Scarica Thunderbit È gratis
Estrai dati con l’AI
Trasferisci facilmente i dati a Google Sheets, Airtable o Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week