Scraper Google Flights avec Python : du code aux alertes de prix

Dernière mise à jour le July 8, 2026
Scraper Google Flights avec Python : du code aux alertes de prix

Sur une seule liaison intérieure, le tarif d’un billet peut être réajusté jusqu’à 17 fois en 48 heures. Google a fermé son API Flights en 2018, mais ces prix, eux, n’ont pas cessé de bouger. Résultat : pour récupérer ces données de manière automatisée, il ne reste pratiquement que le scraping.

J’ai consacré pas mal de temps à comparer les méthodes d’extraction des données de vols chez Google, et le terrain a profondément changé — en particulier depuis l’arrivée de SearchGuard en janvier 2025. Ce guide vous accompagne dans la construction d’un scraper Python opérationnel pour Google Flights avec Playwright, dans la gestion des protections anti-bot qui mettent en échec la majorité des tentatives, puis dans le passage à un suivi automatisé des prix avec alertes. Et pour qui ne veut pas toucher au code, je détaille aussi un raccourci no-code avec Thunderbit, capable de produire le même résultat en deux minutes environ.

Pourquoi scraper Google Flights avec Python ?

Quand il s’agit de chercher un vol, Google Flights écrase la concurrence. Aux États-Unis, sa visibilité mobile a grimpé à 47,6 % — devant tous les grands OTA. Et le marché du metasearch voyage sur lequel il s’appuie pesait 8,33 milliards de dollars en 2024 (environ 7,7 milliards d’euros), avec une croissance annuelle composée de 30,2 %. Le hic : depuis la fermeture de l’API QPX Express le 10 avril 2018, plus aucune voie officielle ne permet d’accéder à ces données par programme.

S’ajoute à cela une instabilité tarifaire impressionnante. Sur un même itinéraire, les prix peuvent varier jusqu’à 50 %, avec un écart moyen d’environ 20 $ (environ 18 €) entre le tarif plancher et le tarif plafond. Delta, par exemple, s’appuie sur 77 paliers tarifaires pour son pricing dynamique. Début 2026, un aller-retour intérieur aux États-Unis revient en moyenne à 408 $ (environ 378 €), les tarifs aériens étant en hausse de 14,9 % sur un an.

Une plateforme incontournable, aucune API, des prix qui ne tiennent pas en place : on comprend pourquoi le scraping de Google Flights avec Python est devenu l’un des projets les plus repris sur GitHub et dans les forums voyage.

Voici à qui cela sert concrètement, et pour quelles raisons :

Type d’utilisateurCas d’usagePrincipal avantage
Voyageurs individuelsSuivre les prix d’itinéraires précis dans le tempsÉconomiser en moyenne 50 $ par vol
Agences de voyageVeille tarifaire concurrentielleSuivi en temps réel de la parité des tarifs
Équipes voyages en entrepriseOptimisation des coûts sur plusieurs trajets10 à 30 % d’économies sur les déplacements pro
DéveloppeursCréer des applications de comparaison de tarifsAccès programmatique aux données de prix
ChercheursAnalyse de la volatilité des tarifs aériensRecherche académique et études de marché

Sur les forums, les motivations sont exprimées sans détour : « Google Flights API was discontinued and I should use web scraping instead » revient comme un refrain. Et le jeu en vaut la chandelle — Hopper revendique 95 % de précision dans ses prévisions après analyse de plus de 5 milliards de cotations quotidiennes, tandis que les données Expedia 2026 indiquent qu’une réservation effectuée 8 à 15 jours à l’avance fait économiser environ 25 $ sur les vols intérieurs (environ 23 €).

Quelles données peut-on extraire de Google Flights ?

Une page de résultats Google Flights est étonnamment dense en informations. Voici ce que l’on parvient généralement à récupérer :

  • Nom de la compagnie aérienne (et logo)
  • Heure de départ et code aéroport
  • Heure d’arrivée et code aéroport
  • Durée totale du vol
  • Nombre d’escales et détails des correspondances (aéroport, durée, statut de nuit)
  • Prix du billet (selon la devise)
  • Émissions de CO2 (kg CO2e, avec écart en pourcentage par rapport aux vols typiques)
  • Classe de voyage, numéro de vol, modèle d’avion
  • Espace pour les jambes
  • Services inclus (Wi‑Fi, prises électriques, streaming multimédia)
  • Indicateur de niveau de prix (bas / habituel / élevé)
  • Alertes de retard ("Souvent retardé de plus de 30 min")

Ce qui est réellement disponible dépend de la liaison, de la date et du type de billet (aller simple ou aller-retour). Voici à quoi ressemble un enregistrement de vol extrait, au format JSON :

{
  "search_date": "2026-04-16",
  "route": "SFO-JFK",
  "departure_date": "2026-05-15",
  "flights": [
    {
      "airline": "United Airlines",
      "flight_number": "UA123",
      "departure_time": "08:00",
      "departure_airport": "SFO",
      "arrival_time": "16:35",
      "arrival_airport": "JFK",
      "duration_minutes": 335,
      "stops": 0,
      "price_usd": 287,
      "price_level": "low",
      "co2_kg": 156,
      "co2_vs_typical": "-12%",
      "travel_class": "Economy"
    }
  ]
}

Préparer votre environnement Python

Avant la première ligne de code de scraping, quelques fondations sont à poser.

Prérequis :

  • Niveau requis : intermédiaire
  • Temps nécessaire : environ 1 à 2 heures pour suivre tout le tutoriel
  • À prévoir : Python 3.7+, notions de base en Python, un navigateur basé sur Chrome

Installer les bibliothèques nécessaires

Google Flights étant rendu intégralement en JavaScript — les requêtes HTTP statiques ne renvoient rien d’exploitable —, nous nous appuyons sur Playwright pour piloter le navigateur, accompagné de quelques outils :

pip install playwright playwright-stealth pandas
playwright install chromium
  • Playwright — automatisation de navigateur en mode headless, gère le rendu JavaScript et les mécanismes d’attente intégrés
  • playwright-stealth — atténue plusieurs signaux classiques de détection de bot
  • pandas — pour l’analyse des données et l’export CSV par la suite

Pourquoi choisir Playwright plutôt que Selenium ou requests

Avec requests + BeautifulSoup seuls, vous n’irez nulle part sur Google Flights : tout le contenu est généré par JavaScript. Un vrai navigateur est indispensable.

FonctionnalitéPlaywrightSeleniumrequests + BS4
Rendu JSSupport completSupport completAucun
Vitesse42 % plus rapide au totalRéférenceN/A pour ce cas
Support asynchroneNatifSéquentiel uniquementN/A
Utilisation mémoire30 % de moinsPlus élevéeMinimale
Contournement de détection botBon (avec stealth)Plus facilement détectéN/A

Plus rapide, plus moderne, mieux armé pour l’asynchrone : sur Google Flights en particulier, Playwright s’impose sans hésitation.

Étape par étape : scraper Google Flights avec Python

On entre dans le vif du sujet. Nous allons assembler le scraper brique par brique.

google-flights-scraping-workflow.webp

Étape 1 : définir vos classes de données

Première brique : structurez vos paramètres de recherche et vos données de vol avec les dataclasses Python. Le code y gagne en lisibilité et les évolutions futures s’en trouvent simplifiées.

from dataclasses import dataclass, field
from typing import Optional, List

@dataclass
class SearchParams:
    origin: str          # e.g., "SFO"
    destination: str     # e.g., "JFK"
    departure_date: str  # e.g., "2026-05-15"
    return_date: Optional[str] = None
    trip_type: str = "one-way"  # "one-way" or "round-trip"
    travel_class: str = "economy"

@dataclass
class FlightData:
    airline: str = ""
    departure_time: str = ""
    arrival_time: str = ""
    duration: str = ""
    stops: str = ""
    price: str = ""
    co2_emissions: str = ""

Chaque champ correspond directement à ce que nous irons chercher sur la page. En posant cette structure d’emblée, vous évitez de jongler plus tard avec des dictionnaires brouillons.

Étape 2 : comprendre la structure d’URL de Google Flights

Google Flights encode les paramètres de recherche dans l’URL tfs via un Protobuf encodé en Base64. Deux voies s’offrent à vous : reverse-engineer cet encodage, ou opter pour la simplicité avec une URL de requête en langage naturel.

La méthode la plus directe consiste à utiliser ce format de recherche :

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

Pour un contrôle plus fin, vous pouvez générer l’URL par programme :

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

Décoder le Protobuf à la main offre une précision supérieure, mais l’ensemble casse dès que Google touche à son format interne. Des bibliothèques comme fast-flights sur GitHub passent par ce décodage Protobuf pour se dispenser totalement de l’analyse HTML — une option plus avancée, à réserver aux profils aguerris.

Étape 3 : lancer le navigateur et ouvrir Google Flights

Voici la configuration Playwright. Nous activons playwright-stealth dès le départ pour réduire le risque de détection.

import asyncio
from playwright.async_api import async_playwright
from playwright_stealth import Stealth

async def scrape_flights(params: SearchParams) -> List[FlightData]:
    async with Stealth().use_async(async_playwright()) as pw:
        browser = await pw.chromium.launch(
            headless=True,
            args=[
                "--disable-blink-features=AutomationControlled",
                "--disable-dev-shm-usage",
                "--no-first-run",
            ]
        )
        context = await browser.new_context(
            viewport={"width": 1920, "height": 1080},
            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"
            ),
            locale="en-US",
            timezone_id="America/New_York",
        )
        # Pre-set cookie consent to skip the popup
        await context.add_cookies([{
            "name": "SOCS",
            "value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
            "domain": ".google.com",
            "path": "/"
        }])
        page = await context.new_page()

En production, nous restons en headless (passez à headless=False pour le débogage). Nous fixons une fenêtre et un user-agent crédibles, et nous pré-renseignons le cookie SOCS pour court-circuiter la fenêtre de consentement — un point sur lequel nous revenons dans la section anti-bot.

Étape 4 : ouvrir les résultats de recherche

Chargez l’URL construite, puis attendez l’apparition des résultats :

        url = build_flights_url(
            params.origin, params.destination, params.departure_date
        )
        await page.goto(url, wait_until="networkidle")

        # Wait for flight results to load
        await page.wait_for_selector(
            "li.pIav2d", timeout=15000
        )

Si cette étape arrive à expiration, deux causes sont probables : soit la fenêtre de consentement bloque la page (voir le correctif par cookie à l’étape 3), soit Google vous oppose un CAPTCHA. Les deux cas sont traités dans la partie anti-bot.

Étape 5 : charger tous les résultats de vols

Google Flights cache une partie des résultats derrière le bouton « Show more flights ». Il faut donc cliquer plusieurs fois pour faire apparaître la totalité des vols :

        # Click "Show more flights" until all results are loaded
        while True:
            try:
                more_button = page.locator(
                    'button:has-text("Show more flights")'
                )
                if await more_button.is_visible(timeout=3000):
                    await more_button.click()
                    await page.wait_for_timeout(2000)
                else:
                    break
            except Exception:
                break

Cette boucle clique sur le bouton, laisse 2 secondes aux nouveaux résultats pour s’afficher, puis s’interrompt une fois le bouton disparu. D’après mes essais, la plupart des itinéraires tiennent sur 1 à 3 pages de résultats.

Étape 6 : extraire les données de vol avec des sélecteurs CSS

Place à l’analyse des données réelles de la page chargée. Voici les sélecteurs (vérifiés en avril 2026 — la section maintenance explique pourquoi cette date a son importance) :

        flights = []
        cards = await page.query_selector_all("li.pIav2d")

        for card in cards:
            flight = FlightData()

            # Airline name
            airline_el = await card.query_selector(
                "div.sSHqwe span:not([class])"
            )
            if airline_el:
                flight.airline = (await airline_el.inner_text()).strip()

            # Departure time
            dep_el = await card.query_selector(
                'span[aria-label*="Departure time"]'
            )
            if dep_el:
                flight.departure_time = (await dep_el.inner_text()).strip()

            # Arrival time
            arr_el = await card.query_selector(
                'span[aria-label*="Arrival time"]'
            )
            if arr_el:
                flight.arrival_time = (await arr_el.inner_text()).strip()

            # Duration
            dur_el = await card.query_selector("div.gvkrdb")
            if dur_el:
                flight.duration = (await dur_el.inner_text()).strip()

            # Stops
            stops_el = await card.query_selector("div.EfT7Ae span")
            if stops_el:
                flight.stops = (await stops_el.inner_text()).strip()

            # Price
            price_el = await card.query_selector(
                "div.FpEdX span"
            )
            if price_el:
                flight.price = (await price_el.inner_text()).strip()

            # CO2 emissions
            co2_el = await card.query_selector("div.O7CXue")
            if co2_el:
                flight.co2_emissions = (
                    await co2_el.get_attribute("aria-label") or ""
                ).strip()

            flights.append(flight)

        await browser.close()
        return flights

Méfiance : des classes comme pIav2d, sSHqwe et FpEdX sont générées par le Closure Compiler de Google et peuvent muter à chaque build. Les sélecteurs fondés sur aria-label tiennent mieux dans la durée. Je détaille plus bas une stratégie de maintenance complète.

Étape 7 : enregistrer les résultats en JSON ou CSV

Dernière étape : sauvegardez les données extraites avec un horodatage — indispensable pour le suivi des prix qui suivra :

import json
from datetime import datetime
from dataclasses import asdict

async def main():
    params = SearchParams(
        origin="SFO",
        destination="JFK",
        departure_date="2026-05-15"
    )
    flights = await scrape_flights(params)

    output = {
        "search_date": datetime.now().isoformat(),
        "params": asdict(params),
        "flights": [asdict(f) for f in flights],
    }

    with open("flights.json", "w") as f:
        json.dump(output, f, indent=2)

    # Also save as CSV
    import pandas as pd
    df = pd.DataFrame([asdict(f) for f in flights])
    df["search_date"] = datetime.now().isoformat()
    df["route"] = f"{params.origin}-{params.destination}"
    df.to_csv("flights.csv", index=False)

    print(f"Scraped {len(flights)} flights")

asyncio.run(main())

Lancez ce script : vous devriez obtenir un fichier flights.json et un fichier flights.csv contenant vos résultats. Lors de mes tests, une recherche SFO-JFK remonte généralement 30 à 80 options de vol en 15 à 20 secondes.

Guide de survie anti-bot pour scraper Google Flights

C’est là que s’arrêtent la plupart des tutoriels. Et c’est exactement là que la plupart des scrapers calent. Google a déployé SearchGuard en janvier 2025, mettant à terre presque tous les scrapers de SERP du jour au lendemain. Google le présente comme « le fruit de dizaines de milliers d’heures-homme et de millions de dollars d’investissement ». Côté difficulté de scraping, Google Flights décroche un 4/5.

Aucun article concurrent ne creuse vraiment ce point — alors que c’est la première cause d’arrêt des scrapers. Voici ce qui vous attend, et la parade pour chaque obstacle.

anti-bot-survival-guide.webp

Ajouter des délais aléatoires entre les requêtes

La défense la plus élémentaire contre le rate limiting. Deux lignes de code, efficacité moyenne :

import time
import random

time.sleep(random.uniform(3, 7))

Insérez ces lignes entre les navigations de page. Des intervalles figés (exactement 5 secondes à chaque fois, par exemple) sont suspects — il faut introduire de la variation.

Rotation du User-Agent

Renvoyer la même chaîne user-agent à chaque requête, c’est se signaler à coup sûr. Alternez parmi une liste :

import random

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 Safari/605.1.15",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
]

user_agent = random.choice(USER_AGENTS)

Contourner la détection du mode headless

Google scrute le drapeau navigator.webdriver et d’autres signaux d’automatisation. La bibliothèque playwright-stealth couvre l’essentiel de ces points, mais il faut y ajouter les arguments de lancement vus à l’étape 3. Les drapeaux clés :

args=[
    "--disable-blink-features=AutomationControlled",
    "--disable-dev-shm-usage",
    "--no-first-run",
]

De quoi franchir les détections de base. SearchGuard pousse plus loin — il surveille la vitesse de la souris, le timing clavier et les schémas de défilement —, mais pour des volumes raisonnables, le mode stealth assorti de délais crédibles fait généralement l’affaire.

Rotation des proxys : datacenter vs résidentiels

Dès que vous franchissez le cap de quelques recherches, les proxys deviennent incontournables. Et le choix pèse lourd :

FonctionnalitéProxys datacenterProxys résidentiels
Vitesse100 à 1 000 Mbps10 à 100 Mbps
Taux de réussite (Google)20 à 40 %85 à 95 %
Coût0,10 à 0,50 $/IP/mois5 à 15 $/Go
Risque de détectionÉlevéTrès faible

Rapportés à chaque requête réussie sur les sites protégés, les proxys résidentiels reviennent environ 180 fois moins cher. Tarifs 2026 selon les fournisseurs : Smartproxy à partir de 7 $/Go (environ 6,50 €/Go), Bright Data à 8,40 $/Go, Oxylabs à 8 $/Go.

On ajoute un proxy à Playwright de cette façon :

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

Gérer la fenêtre de consentement des cookies

La fenêtre « I agree to terms » revient sans cesse dans les remontées d’utilisateurs comme point de blocage : « first google will show you the 'I agree to terms and conditions' popup. » La solution la plus nette consiste à pré-définir le cookie SOCS (comme à l’étape 3). Si cela ne suffit pas, cliquez dessus :

try:
    accept_btn = page.locator('button:has-text("Accept all")')
    if await accept_btn.is_visible(timeout=3000):
        await accept_btn.click()
        await page.wait_for_timeout(1000)
except Exception:
    pass  # No popup present

À noter : le libellé du bouton change selon la langue — « Alle akzeptieren » en allemand, « Tout accepter » en français.

Référence rapide anti-bot

TechniqueDifficultéEfficacitéCode nécessaire ?
Délais aléatoires (2–7 s)FaibleMoyenne2 lignes
Rotation du user-agentFaibleMoyenne5 lignes
Contournement du headlessMoyenneÉlevéeArguments de lancement Playwright
Plugin playwright-stealthMoyenne60–80 % sur les sites basiquespip install
Rotation de proxys (datacenter)MoyenneMoyenneConfiguration
Rotation de proxys (résidentiels)Moyenne85–95 % de réussiteConfiguration
Pré-réglage du consentement cookie (SOCS)FaibleRequis1 ligne

Pour des cadences sûres, comptez 10 à 20 secondes entre les requêtes avec rotation d’IP. Côté seuils, Google tolère environ 100 requêtes/minute par IP avant un 429 ; au-delà de 1 000 requêtes/jour par IP en régime soutenu, des bannissements temporaires peuvent tomber.

Pourquoi vos sélecteurs Google Flights cassent sans cesse (et comment y remédier)

C’est, de loin, le problème numéro un. Les forums regorgent de variantes du fameux « tout ce que je récupère, ce sont 14 listes vides. » Tous les tutoriels fournissent des sélecteurs. Aucun n’explique pourquoi ils cassent.

Pourquoi les sélecteurs Google Flights changent

Trois facteurs entrent en jeu :

  1. Obfuscation via Closure Compiler. Google s’appuie sur Closure Stylesheets pour générer des noms de classes comme BVAVmf et YMlIz via goog.setCssNameMapping(). Ils changent à chaque build — parfois chaque semaine.

  2. Tests A/B. Au même moment, différents utilisateurs voient des structures HTML différentes. Votre scraper peut tourner chez vous et échouer pour quelqu’un situé dans une autre région.

  3. Différences de locale. Les utilisateurs européens voient parfois des libellés, des mises en page, voire des champs différents de ceux affichés côté américain.

Écrire des sélecteurs robustes

Privilégiez des sélecteurs ancrés dans le sens plutôt que dans l’apparence :

# Fragile — breaks on every build
price_el = await card.query_selector("div.BVAVmf > div.YMlIz")

# More resilient — tied to accessibility labels
dep_el = await card.query_selector('span[aria-label*="Departure time"]')

# Also resilient — text-based matching
more_btn = page.locator('button:has-text("Show more flights")')

Hiérarchie de stabilité des sélecteurs (du plus stable au moins stable) :

  1. attributs aria-label — liés à l’accessibilité, rarement modifiés
  2. attributs data-* — ajoutés explicitement pour la fonctionnalité
  3. attributs role — les rôles ARIA sont sémantiques
  4. sélecteurs basés sur le texte — correspondent au contenu visible
  5. correspondance partielle sur les classes — par exemple [class*="price"]
  6. noms de classes obfusqués complets — à éviter autant que possible

Ajouter une fonction de validation

Ne laissez pas des sélecteurs cassés produire en silence des données vides. Repérez le problème tôt :

import logging

logger = logging.getLogger(__name__)

def validate_flight(data: FlightData) -> bool:
    required = ["airline", "price", "departure_time",
                "arrival_time", "duration"]
    valid = True
    for field_name in required:
        if not getattr(data, field_name, ""):
            logger.warning(
                f"Missing '{field_name}' — selectors may need updating"
            )
            valid = False
    return valid

Appliquez cette vérification à chaque vol extrait. Si les avertissements commencent à s’accumuler, il est temps d’inspecter la page et de mettre à jour les sélecteurs.

Stratégie de maintenance des sélecteurs

  • Contrôlez les sélecteurs tous les mois, ou sans attendre dès que la qualité des données se dégrade
  • Conservez les sélecteurs dans un dictionnaire de configuration distinct, pour faciliter les mises à jour
  • Les sélecteurs de cet article ont été vérifiés pour la dernière fois en avril 2026
  • Étudiez la bibliothèque fast-flights comme alternative — elle s’appuie sur le décodage Protobuf au lieu des sélecteurs CSS, ce qui contourne le problème (tout en restant fragile lorsque Google modifie ses formats de données internes)

D’un scraping ponctuel à un tracker automatisé des prix Google Flights

La plupart des tutoriels s’en tiennent à « enregistrer en JSON ». Or le titre de cet article promet des « Price Alerts ». L’heure est venue de tenir parole.

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

Planifier l’exécution automatique de votre scraper

Option 1 : bibliothèque Python schedule (la plus simple, multiplateforme) :

import schedule
import time

def run_scraper():
    asyncio.run(main())

schedule.every().day.at("06:00").do(run_scraper)
schedule.every().day.at("18:00").do(run_scraper)

while True:
    schedule.run_pending()
    time.sleep(60)

Option 2 : tâche cron (Linux/Mac) :

# Run at 6 AM and 6 PM daily
0 6,18 * * * cd /path/to/scraper && python scraper.py

Option 3 : Planificateur de tâches Windows — créez une tâche de base qui exécute python scraper.py à la fréquence de votre choix.

Le revers : toutes ces solutions supposent une machine allumée en permanence. Sur un laptop qui bascule en veille, vous raterez des exécutions.

Stocker l’historique des prix

Plutôt que d’écraser un fichier JSON, insérez les données dans une base SQLite :

import sqlite3
from datetime import datetime

def init_db():
    conn = sqlite3.connect("flights.db")
    conn.execute("""
        CREATE TABLE IF NOT EXISTS flights (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            scrape_date TEXT NOT NULL,
            route TEXT NOT NULL,
            airline TEXT,
            departure_time TEXT,
            arrival_time TEXT,
            duration TEXT,
            stops TEXT,
            price_usd REAL,
            co2_emissions TEXT
        )
    """)
    conn.execute(
        "CREATE INDEX IF NOT EXISTS idx_route_date "
        "ON flights(route, scrape_date)"
    )
    conn.commit()
    return conn

def save_flight(conn, route: str, flight: FlightData):
    price_num = float(
        flight.price.replace("$", "").replace(",", "")
    ) if flight.price else None
    conn.execute(
        "INSERT INTO flights "
        "(scrape_date, route, airline, departure_time, "
        "arrival_time, duration, stops, price_usd, co2_emissions) "
        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
        (datetime.now().isoformat(), route, flight.airline,
         flight.departure_time, flight.arrival_time,
         flight.duration, flight.stops, price_num,
         flight.co2_emissions)
    )
    conn.commit()

Au bout d’une semaine de scrapes deux fois par jour, vous disposerez d’assez de matière pour commencer à dégager des tendances.

Analyser les tendances de prix et définir des alertes

Identifiez l’option la moins chère dans votre historique :

import pandas as pd
import sqlite3

conn = sqlite3.connect("flights.db")
df = pd.read_sql_query(
    "SELECT * FROM flights WHERE route = 'SFO-JFK'", conn
)
summary = df.groupby("scrape_date")["price_usd"].agg(
    ["min", "max", "mean"]
)
cheapest = df.loc[df["price_usd"].idxmin()]
print(
    f"Cheapest: ${cheapest['price_usd']:.0f} on "
    f"{cheapest['scrape_date']} ({cheapest['airline']})"
)

Déclenchez une alerte e-mail dès que le prix passe sous votre seuil :

import smtplib
from email.mime.text import MIMEText

def send_price_alert(route, price, threshold, recipient):
    msg = MIMEText(
        f"Price drop alert! {route}: ${price:.0f} "
        f"(below your ${threshold:.0f} threshold)"
    )
    msg["Subject"] = f"Flight Deal: {route} at ${price:.0f}"
    msg["From"] = "alerts@example.com"
    msg["To"] = recipient

    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
        server.login("alerts@example.com", "your_app_password")
        server.send_message(msg)

# After each scrape, check for deals
min_price = df["price_usd"].min()
threshold = 250
if min_price < threshold:
    send_price_alert("SFO-JFK", min_price, threshold,
                     "you@email.com")

Fréquence conseillée : deux passages par jour suffisent pour un suivi personnel des prix (un horaire aléatoire abaisse le risque de détection). Toutes les 4 à 6 heures pour un besoin professionnel. Toutes les heures uniquement pendant les promotions, et de manière temporaire.

Le chemin le plus simple : le Scheduled Scraper de Thunderbit

Si jongler avec cron, un serveur allumé en continu et des configurations de proxy vous paraît trop lourd à entretenir, le Scheduled Scraper de Thunderbit couvre le même besoin sans toute cette infrastructure. Vous décrivez l’intervalle de scraping en langage naturel, vous fournissez vos URLs Google Flights, et le scraper s’exécute tout seul sur l’infrastructure cloud de Thunderbit — avec gestion anti-bot intégrée et export direct vers Google Sheets, Excel ou Airtable. Ce n’est pas un substitut intégral à l’approche Python (vous perdez en personnalisation), mais pour le seul objectif « je veux un tableau de suivi des prix », c’est la voie la plus rapide. Vous pouvez l’essayer avec le forfait gratuit.

Quand Python est excessif : des solutions no-code pour scraper Google Flights

Une fois tout l’édifice ci-dessus construit, soyons lucides : cela fait beaucoup de rouages. Tout le monde n’a pas besoin d’un tel niveau de contrôle. Les sélecteurs cassent, les proxys doivent tourner, les cron jobs réclament une surveillance. Si votre seul objectif est « récupérer régulièrement les prix des vols dans un tableur », des options plus rapides existent.

Comparaison : Python maison vs services d’API vs Thunderbit

ApprocheTemps de mise en placeCode requisGestion anti-botPlanificationCoût
Playwright DIY (ce tutoriel)1 à 2 heuresPython (intermédiaire)Configuration manuelleManuelle (cron)Gratuit + coûts des proxys
Endpoint Google Flights de SerpApi15 minAppels API uniquementGéréVia APIEnviron 50 $+/mois
Extension Chrome Thunderbit2 minAucunScraping cloudPlanificateur intégréForfait gratuit disponible

À propos de SerpApi : Google a déposé une plainte DMCA contre SerpApi en décembre 2025, reprochant à ce dernier une explosion de 25 000 % de son volume de requêtes en deux ans. Cette incertitude juridique mérite réflexion si vous évaluez des fournisseurs d’API.

Comment Thunderbit scrape Google Flights

Ouvrez vos résultats Google Flights dans Chrome, cliquez sur le bouton « AI Suggest Fields » de Thunderbit — l’IA lit la page et propose des colonnes comme compagnie aérienne, prix, heure de départ, escales —, vérifiez les champs proposés, puis cliquez sur « Scrape ». Les résultats s’affichent dans un tableau que vous exportez vers Excel, Google Sheets, Airtable ou Notion — le tout sur le forfait gratuit.

Pour le cas précis du suivi des prix, le Scheduled Scraper et le Cloud Scraping de Thunderbit (capable de traiter 50 pages en parallèle) remplacent à eux seuls toute l’infrastructure cron + proxy + serveur.

Python vous donne un contrôle total et une personnalisation sans limite. Thunderbit vous apporte la vitesse et zéro maintenance. À chacun de trancher selon son objectif réel. Pour approfondir les approches no-code, consultez notre guide sur les meilleurs web scrapers no-code.

flight-data-options-comparison.webp

Le scraping de Google Flights est-il légal ? Ce qu’il faut savoir

Sur les forums, la question revient souvent : « scraper Google Flights directement viole les conditions d’utilisation de Google ». L’inquiétude se comprend — d’autant qu’aucune API officielle n’existe plus.

Violation des CGU vs responsabilité juridique

Les conditions d’utilisation de Google (mises à jour le 22 mai 2024) précisent que les utilisateurs ne doivent pas « accéder aux Services ou à tout contenu ou les utiliser via des moyens automatisés (tels que robots, spiders ou scrapers) ». Enfreindre les CGU relève de la rupture contractuelle (domaine civil) — ce qui n’a rien à voir avec une violation du droit pénal.

Le précédent juridique majeur : hiQ v. LinkedIn (neuvième circuit, 2022) a établi que le scraping de données publiques ne contrevient pas au Computer Fraud and Abuse Act (CFAA). L’affaire s’est toutefois soldée par un accord, et la plainte de Google contre SerpApi en décembre 2025 repose sur un autre fondement — la section 1201 du DMCA (contournement de mesures techniques de protection) — potentiellement plus lourd de conséquences.

Bonnes pratiques pour un scraping responsable

  • Limitez le rythme des requêtes — délais de 10 à 20 secondes avec rotation d’IP
  • Ne scrapez pas de données personnelles — les prix des vols sont des informations publiques et agrégées
  • Ne contournez pas les CAPTCHA par programme (c’est la zone de risque liée au DMCA)
  • Réservez les données à de la recherche personnelle, et non à un produit commercial concurrent dépourvu de licence appropriée
  • Privilégiez les API officielles quand elles existent

Sources de données alternatives

Si le scraping vous semble trop risqué pour votre cas d’usage, des options API légitimes existent :

FournisseurCoûtForfait gratuitRemarques
SerpApi75 à 3 750 $+/mois250 recherches/moisJSON Google Flights direct (sous surveillance juridique)
Kiwi TequilaGratuit (modèle d’affiliation)IllimitéIdéal pour les startups et les tests
AmadeusPaiement à l’usage2 000 requêtes/moisPlus de 400 compagnies, possibilité de réservation
SkyscannerPersonnaliséSur approbation52 marchés, 30 langues

Nous avons par ailleurs publié une analyse plus poussée des implications juridiques du web scraping, pour qui souhaite une vue d’ensemble.

Conclusion et points clés

Le sujet était dense. Voici l’essentiel :

  • Python + Playwright offre l’approche la plus souple pour scraper Google Flights, au prix d’une maintenance continue
  • Les protections anti-bot (délais, rotation du user-agent, proxys résidentiels) ne relèvent pas du superflu — elles conditionnent la fiabilité, surtout depuis SearchGuard
  • Les sélecteurs cassent souvent — appuyez-vous autant que possible sur aria-label et sur des sélecteurs basés sur le texte, validez vos sorties et prévoyez une maintenance régulière
  • Automatisez avec schedule ou cron pour faire passer un scraping ponctuel au rang de véritable tracker de prix, avec historique et alertes e-mail
  • Thunderbit propose une alternative no-code avec planification intégrée, scraping cloud et gestion anti-bot — parfait quand l’objectif est un tableau de suivi des prix plutôt qu’un projet de développement
  • Respectez le cadre légal — limitez le rythme, ne scrapez que des données publiques et envisagez des API alternatives pour un usage commercial

Récupérez le code de ce tutoriel, ou installez l’extension Chrome Thunderbit pour gagner du temps. Dans les deux cas, vous suivrez les prix des vols au lieu d’actualiser Google Flights à la main.

Pour d’autres techniques de scraping en Python, jetez un œil à nos guides sur comment scraper le web avec Python et sur les meilleurs outils Python de web scraping.

FAQ

1. Peut-on scraper Google Flights sans Python ?

Oui. Des services API comme SerpApi et Kiwi Tequila fournissent des données structurées sur les vols via des appels API (sans automatisation de navigateur). Pour une approche entièrement no-code, l’extension Chrome Thunderbit extrait directement les résultats Google Flights depuis votre navigateur, avec des champs suggérés par l’IA et un export en un clic.

2. Google bloque-t-il le scraping des vols ?

Google déploie des mécanismes de détection de bots (SearchGuard), des CAPTCHA et du rate limiting. Avec des mesures anti-bot adaptées — délais aléatoires, rotation du user-agent, proxys résidentiels et réglages de navigateur stealth —, vous pouvez maintenir un scraping fiable à volume modéré. Reportez-vous à la section anti-bot ci-dessus pour les techniques et seuils précis.

3. À quelle fréquence faut-il scraper Google Flights pour suivre les prix ?

Deux fois par jour (à des heures aléatoires) suffisent pour un suivi personnel et maintiennent le risque de détection au plancher. Pour un usage professionnel, toutes les 4 à 6 heures avec rotation de proxys. Évitez le scraping horaire, sauf pendant de courtes fenêtres de promotions tarifaires — il fait grimper en flèche le risque de blocage.

4. Existe-t-il une API Google Flights gratuite ?

L’API officielle Google QPX Express a été arrêtée en avril 2018. Aucun remplacement officiel gratuit n’a vu le jour. L’option gratuite la plus proche reste l’API Kiwi Tequila (modèle d’affiliation, recherches illimitées). SerpApi, de son côté, offre 250 recherches gratuites par mois. Pour la plupart des utilisateurs, le scraping ou un outil no-code comme Thunderbit demeure la solution la plus pratique.

5. Pourquoi mes sélecteurs CSS Google Flights renvoient-ils sans cesse des données vides ?

Google s’appuie sur Closure Compiler pour générer des noms de classes obfusqués qui changent à chaque build. Les tests A/B et les différences de locale font, eux aussi, varier la structure HTML d’un utilisateur à l’autre. La parade : utiliser des attributs aria-label et des sélecteurs basés sur le texte plutôt que sur les classes, ajouter une fonction de validation pour repérer les cassures tôt, et contrôler les sélecteurs chaque mois. Voir la section dédiée à la maintenance des sélecteurs pour une stratégie détaillée.

En savoir plus

Ke
Ke
CTO chez Thunderbit | Data Scientist senior et expert en ML Forte d’une expérience de près de dix ans en machine learning et en data science, Ke Shen est diplômé de Columbia University et ancien Data Scientist senior chez Walmart Labs. Grâce à une expertise approfondie, reconnue par ses pairs, en Python, R, Java et statistiques, il partage des analyses éprouvées sur le passage d’algorithmes d’IA complexes de la théorie à une architecture prête pour la production.
Table des matières

Récupère une page web en la demandant simplement

Dis ce qu’il te faut en français courant. Ou mieux encore, ne dis rien du tout.

Essayer Thunderbit gratuit
Extraire des données avec l’IA
Transfère facilement des données vers Google Sheets, Airtable ou Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week