Google Flights est le point de départ de la plupart des voyageurs lorsqu'ils comparent les tarifs avant de réserver. La plateforme affiche côte à côte les compagnies concurrentes avec leurs prix, heures de départ et d'arrivée, durées et nombre d'escales, ce qui en fait une source publique précieuse pour quiconque surveille les tarifs dans le temps, effectue des recherches sur un itinéraire ou observe l'évolution des prix entre compagnies. Les chiffres qu'un voyageur lit sur la page sont exactement ceux qu'un outil de suivi de tarifs veut trouver dans un tableau structuré.

Ce guide vous montre comment extraire Google Flights avec Python de manière fiable. Vous construirez un petit script opérationnel qui récupère une page de résultats Google Flights rendue via la Crawling API, analyse chaque vol avec BeautifulSoup et exporte les enregistrements en JSON et CSV pour le suivi de tarifs. L'ensemble du tutoriel se limite aux données de résultats de vol publiques visibles par quiconque sans compte, et la section sur la légalité en fin d'article n'est pas un simple remplissage, alors lisez-la avant d'utiliser ce script à grande échelle.

Ce que vous allez construire

Un script Python qui prend une URL de recherche Google Flights publique, récupère le HTML rendu via la Crawling API et extrait un enregistrement structuré pour chaque vol listé sur la page. Nous utiliserons un exemple d'itinéraire comme fil conducteur et extrairons ces champs pour chaque vol :

  • Compagnie aérienne le ou les transporteurs opérant le vol indiqués dans le résultat.
  • Prix le tarif affiché pour l'itinéraire.
  • Heure de départ l'heure de départ locale du vol.
  • Heure d'arrivée l'heure d'arrivée locale, y compris les indicateurs +1 / +2 jour.
  • Durée la durée totale du trajet pour l'itinéraire.
  • Escales si le vol est direct ou combien d'escales il comporte.

Pourquoi une requête ordinaire échoue sur Google Flights

Si vous envoyez une requête HTTP brute à une URL Google Flights depuis un script, vous n'obtenez pas la page que vous voyez dans votre navigateur. Google Flights construit sa liste de résultats côté client : le HTML initial est essentiellement une coquille vide, et les cartes de vol sont remplies par JavaScript une fois la page chargée. Un simple requests.get n'exécute jamais ce JavaScript, il renvoie donc du balisage sans vols, et chaque sélecteur que vous écrivez revient vide.

De plus, Google surveille le trafic automatisé. Les requêtes qui ne ressemblent pas à un vrai navigateur sont mises au défi, reçoivent une page de vérification ou sont bloquées avant d'atteindre les annonces. Un scraper Google Flights fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend réellement la page et une adresse IP que Google considère comme un visiteur réel. Vous pouvez assembler cela vous-même avec un navigateur headless et un pool de proxies résidentiels rotatifs, mais maintenir cet ensemble en bon état représente l'essentiel du travail. La Crawling API combine les deux en un seul appel : vous lui envoyez l'URL, elle rend la page dans un vrai navigateur depuis une IP résidentielle de confiance et vous retourne le HTML final prêt à analyser.

Le rendu n'est pas optionnel ici

Contrairement à une page de résultats rendue côté serveur classique, Google Flights ne contient presque rien d'utile dans son HTML brut. Les cartes de vol n'existent qu'après l'exécution de JavaScript. C'est pourquoi ce tutoriel utilise le token de rendu JavaScript dès le départ, et non comme un ajout secondaire. Si votre récupération retourne une page avec zéro résultat, le rendu est presque toujours la pièce manquante.

Prérequis

Vous avez besoin de quelques éléments en place avant d'écrire du code. Aucun ne prend longtemps.

Python de base. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des paquets avec pip. Si BeautifulSoup est nouveau pour vous, notre guide d'utilisation de BeautifulSoup en Python couvre les bases de l'analyse que ce tutoriel suppose acquises.

Python 3.8 ou supérieur. Confirmez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda.

Un compte et un token Crawlbase. Inscrivez-vous, ouvrez votre tableau de bord et copiez vos tokens depuis la page de documentation du compte. Google Flights nécessite le rendu, vous utiliserez donc le token JavaScript plutôt que le token normal. Vous obtenez jusqu'à 20 000 requêtes gratuites : 1 000 à l'inscription, et davantage à mesure que vous complétez les étapes d'onboarding. Traitez le token comme un mot de passe : il authentifie vos requêtes, ne le mettez donc pas sous contrôle de version.

Configurer le projet

Créez un environnement virtuel pour isoler les dépendances du projet, puis installez les bibliothèques dont le scraper a besoin.

bash
python --version

python -m venv google_flights_env
source google_flights_env/bin/activate

pip install crawlbase beautifulsoup4

Sous Windows, activez l'environnement avec google_flights_env\Scripts\activate à la place de la ligne source. Deux dépendances effectuent le travail : crawlbase est la bibliothèque Python officielle qui appelle la Crawling API et gère le rendu, et beautifulsoup4 analyse le HTML retourné pour extraire les champs individuels par sélecteur CSS.

Étape 1 : récupérer les résultats rendus

Commencez par obtenir la page finale. Importez la classe CrawlingAPI, initialisez-la avec votre token JavaScript et demandez l'URL de recherche avec le rendu activé. Vérifier le code de statut avant d'analyser permet de rendre les échecs visibles plutôt que silencieux, ce qui compte beaucoup sur une cible qui limite les requêtes.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    status = response["headers"].get("cb_status")
    if status == "200":
        return response["body"].decode("utf-8")
    print(f"Request failed: {status}")
    return None

if __name__ == "__main__":
    page_url = "https://www.google.com/travel/flights/search?tfs=CBwQAhopEgoyMDI0LTA3LTE0ag0IAxIJL20vMDFmMDhycgwIAxIIL20vMDZ5NTcaKRIKMjAyNC0wNy0yMGoMCAMSCC9tLzA2eTU3cg0IAxIJL20vMDFmMDhy&hl=en-US&curr=EUR"
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

Les deux options d'attente sont importantes pour une cible rendue côté client comme celle-ci. ajax_wait indique à l'API d'attendre que le contenu asynchrone ait fini de se charger, et page_wait maintient un nombre fixe de millisecondes après le chargement pour que les cartes de vol à rendu tardif apparaissent avant la capture de la page. Cinq secondes est un point de départ raisonnable ; augmentez si la liste de vols revient incomplète ou vide. Notez l'en-tête cb_status (legacy pc_status) : c'est le statut propre à Crawlbase pour la requête sous-jacente, et c'est la valeur à utiliser pour décider si une récupération a réussi. Exécutez le script et vous devriez voir le balisage réel des annonces, et non la coquille vide qu'une récupération ordinaire retourne.

Crawlbase Crawling API

Google Flights nécessite une page rendue depuis une IP de confiance, en un seul appel. C'est exactement ce que vous offre le token JavaScript dans l'extrait ci-dessus : la Crawling API exécute la page dans un vrai navigateur, attend que les cartes de vol se remplissent, fait tourner les IPs résidentielles côté serveur et vous remet le HTML final, vous évitant ainsi de gérer vous-même une flotte headless et un pool de proxies. Commencez par une URL de recherche publique sur le niveau gratuit.

Étape 2 : analyser chaque vol avec BeautifulSoup

Une fois le HTML rendu en main, chargez-le dans BeautifulSoup et extrayez chaque vol depuis son élément d'annonce. Google encapsule chaque vol dans un élément de liste, vous trouvez donc tous les éléments, puis lisez la compagnie, la durée, le prix, les heures de départ et d'arrivée, et les escales pour chacun. Les sélecteurs ci-dessous proviennent directement de la mise en page actuelle ; inspectez une page dans les outils de développement de votre navigateur (clic droit, puis Inspecter) pour confirmer les noms de classes actuels avant un long cycle d'exécution.

python
from bs4 import BeautifulSoup

def text_or_none(listing, selector):
    el = listing.select_one(selector)
    return el.get_text(strip=True) if el else None

def scrape_dates(listing):
    dep = listing.select_one(
        'span.mv1WYe span:first-child [jscontroller="cNtv4b"] span')
    arr = listing.select_one(
        'span.mv1WYe span:last-child [jscontroller="cNtv4b"] span')
    departure = dep.get_text(strip=True) if dep else None
    arrival = arr.get_text(strip=True) if arr else None
    return departure, arrival

def scrape_flights(html):
    soup = BeautifulSoup(html, "html.parser")
    flights = []
    for listing in soup.select("li.pIav2d"):
        departure, arrival = scrape_dates(listing)
        flights.append({
            "airline": text_or_none(listing, "div.Ir0Voe div.sSHqwe"),
            "price": text_or_none(listing, "div.U3gSDe div.FpEdX span"),
            "departure": departure,
            "arrival": arrival,
            "duration": text_or_none(listing, "div.AdWm1c.gvkrdb"),
            "stops": text_or_none(listing, "div.EfT7Ae span.ogfYpf"),
        })
    return flights

Le sélecteur enveloppant li.pIav2d correspond à chaque annonce de vol sur la page, et chaque champ est lu depuis un sélecteur à l'intérieur de cette annonce. div.Ir0Voe div.sSHqwe contient le nom de la compagnie aérienne, div.U3gSDe div.FpEdX span le prix, div.AdWm1c.gvkrdb la durée, et div.EfT7Ae span.ogfYpf le texte des escales (par exemple "Nonstop" ou "1 stop"). Les heures de départ et d'arrivée se trouvent dans un bloc span.mv1WYe partagé, où le premier enfant est le départ et le dernier enfant est l'arrivée, chacun encapsulé dans un span [jscontroller="cNtv4b"]. Le petit utilitaire text_or_none fait qu'un champ manquant retourne None au lieu de planter la boucle, ce qui est souhaitable sur une page où toutes les cartes ne portent pas tous les champs.

Les sélecteurs évoluent

Les noms de classes de Google, comme pIav2d et sSHqwe, sont obfusqués et tournent lorsque Google redéploie son interface. Considérez les sélecteurs ci-dessus comme un modèle de départ, non comme un contrat. Lorsqu'un champ revient vide pour chaque vol, inspectez une page en direct dans les outils de développement de votre navigateur et mettez à jour le sélecteur. La maintenance périodique des sélecteurs est normale pour tout scraper en production, et non le signe que quelque chose est cassé.

Étape 3 : tout assembler et exporter

Maintenant, combinez la récupération et l'analyse en un seul script opérationnel, puis écrivez la sortie structurée en JSON et CSV. Le JSON est pratique pour le traitement ultérieur ; le CSV s'importe directement dans un tableur ou une feuille de suivi de tarifs que vous comparez jour après jour.

python
import csv
import json
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["headers"].get("cb_status") == "200":
        return response["body"].decode("utf-8")
    return None

def text_or_none(listing, selector):
    el = listing.select_one(selector)
    return el.get_text(strip=True) if el else None

def scrape_dates(listing):
    dep = listing.select_one(
        'span.mv1WYe span:first-child [jscontroller="cNtv4b"] span')
    arr = listing.select_one(
        'span.mv1WYe span:last-child [jscontroller="cNtv4b"] span')
    departure = dep.get_text(strip=True) if dep else None
    arrival = arr.get_text(strip=True) if arr else None
    return departure, arrival

def scrape_flights(html):
    soup = BeautifulSoup(html, "html.parser")
    flights = []
    for listing in soup.select("li.pIav2d"):
        departure, arrival = scrape_dates(listing)
        flights.append({
            "airline": text_or_none(listing, "div.Ir0Voe div.sSHqwe"),
            "price": text_or_none(listing, "div.U3gSDe div.FpEdX span"),
            "departure": departure,
            "arrival": arrival,
            "duration": text_or_none(listing, "div.AdWm1c.gvkrdb"),
            "stops": text_or_none(listing, "div.EfT7Ae span.ogfYpf"),
        })
    return flights

def save_json(flights, path="google_flights.json"):
    with open(path, "w", encoding="utf-8") as f:
        json.dump(flights, f, ensure_ascii=False, indent=2)

def save_csv(flights, path="google_flights.csv"):
    fields = ["airline", "price", "departure",
              "arrival", "duration", "stops"]
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        writer.writerows(flights)

def main():
    page_url = "https://www.google.com/travel/flights/search?tfs=CBwQAhopEgoyMDI0LTA3LTE0ag0IAxIJL20vMDFmMDhycgwIAxIIL20vMDZ5NTcaKRIKMjAyNC0wNy0yMGoMCAMSCC9tLzA2eTU3cg0IAxIJL20vMDFmMDhy&hl=en-US&curr=EUR"
    html = crawl(page_url)
    if not html:
        print("No HTML returned")
        return
    flights = scrape_flights(html)
    save_json(flights)
    save_csv(flights)
    print(f"Saved {len(flights)} flights")

if __name__ == "__main__":
    main()

Exécutez le script complet avec python main.py. Il récupère la page de résultats rendus pour l'itinéraire exemple, extrait un enregistrement pour chaque vol et écrit google_flights.json et google_flights.csv. Pour surveiller un tarif, exécutez-le selon un planning et ajoutez le CSV de chaque exécution à un fichier d'historique : la colonne prix dans le temps est votre courbe tarifaire. Pour analyser un autre itinéraire ou une autre date, remplacez la valeur tfs et les dates dans l'URL ; le parseur gère ce qui revient.

À quoi ressemble la sortie

Vous obtenez une liste propre d'enregistrements de vol, un objet par annonce, prêt à écrire en JSON, CSV ou dans une base de données. Voici un exemple JSON tronqué pour un itinéraire long-courrier :

json
[
  {
    "airline": "Cebu Pacific",
    "price": "€924",
    "departure": "10:10 PM",
    "arrival": "9:45 AM+2",
    "duration": "29 hr 35 min",
    "stops": "1 stop"
  },
  {
    "airline": "Etihad",
    "price": "€2,038",
    "departure": "10:25 PM",
    "arrival": "6:10 PM+1",
    "duration": "13 hr 45 min",
    "stops": "Nonstop"
  },
  {
    "airline": "Emirates",
    "price": "€2,215",
    "departure": "9:30 PM",
    "arrival": "5:20 PM+1",
    "duration": "13 hr 50 min",
    "stops": "Nonstop"
  }
]

Les mêmes enregistrements se retrouvent dans google_flights.csv avec une ligne d'en-tête (airline,price,departure,arrival,duration,stops) et une ligne par vol, ce qui est la forme idéale pour le suivi de tarifs : ajoutez l'exécution de chaque jour et la colonne prix devient une série temporelle que vous pouvez tracer ou surveiller.

Mise à l'échelle sur plusieurs itinéraires et dates

Un itinéraire sur un jour est une démonstration ; un outil de suivi de tarifs exécute la même analyse sur plusieurs routes et une plage de dates de départ. La structure reste la même : construisez chaque URL de recherche, récupérez-la via la Crawling API avec le rendu activé, et analysez-la avec la même fonction scrape_flights. La bonne habitude qui maintient un cycle d'exécution long en bonne santé est le rythme, alors faites des pauses entre les requêtes plutôt que de les envoyer en rafale.

python
import time

route_urls = [
    "https://www.google.com/travel/flights/search?tfs=...&curr=EUR",
    "https://www.google.com/travel/flights/search?tfs=...&curr=USD",
]

all_flights = []
for url in route_urls:
    html = crawl(url)
    if html:
        all_flights.extend(scrape_flights(html))
    time.sleep(3)

print(f"Collected {len(all_flights)} flights across {len(route_urls)} routes")

Toute réponse 5XX de l'API est gratuite, donc réessayer une URL bloquée ou expirée ne vous coûte rien. Comme chaque récupération Google Flights nécessite le rendu, chaque requête utilise le niveau du token JavaScript ; consultez la page de tarification pour savoir comment les requêtes rendues sont comptabilisées avant de faire monter en charge un outil de surveillance. Si vous préférez acheminer votre propre trafic via un pool rotatif au lieu d'utiliser l'API gérée, le Smart AI Proxy (également appelé AI Proxy) vous offre la même rotation d'IP résidentielles comme point d'entrée proxy intégrable.

Rester non bloqué

Même avec le rendu et une IP de confiance pris en charge, Google surveille le trafic à l'allure d'un scraper plus durement que la plupart des sites. Quelques habitudes maintiennent un cycle d'exécution en bonne santé.

  • Rythmez vos requêtes. Bombarder les pages de recherche en rafale est le moyen le plus rapide d'être mis au défi. Espacez les requêtes et variez vos itinéraires au lieu de parcourir une recherche à toute vitesse.
  • Misez sur la rotation. Un pool d'IPs résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels pour qu'aucune ne dépasse une limite. La Crawling API gère cela pour vous ; si vous construisez votre propre pile, c'est la partie à soigner.
  • Donnez le temps à la page de se rendre. Si les vols reviennent vides, augmentez page_wait avant de supposer que les sélecteurs sont cassés. Les cartes ont besoin d'un moment pour se remplir.
  • Réinspectez quand des champs deviennent vides. Google fait tourner ses noms de classes obfusqués. Si les résultats ne s'analysent plus, ouvrez une page en direct dans les outils de développement et mettez à jour les sélecteurs.

Pour le guide de pratiques plus large, consultez comment extraire des sites web sans être bloqué. Comme Google Flights est entièrement rendu côté client, notre guide sur l'exploration de sites JavaScript explique pourquoi le rendu est important et comment l'activer, et si vous suivez également les prix d'hébergement, extraire Google Hotels avec Python utilise le même schéma de récupération rendue.

Est-il légal d'extraire Google Flights ?

La possibilité d'extraire Google Flights dépend des conditions d'utilisation de Google, de votre juridiction et de ce que vous faites des données. Les conditions de Google limitent l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions, quelle que soit la prudence de vos outils. Aucun des codes ici ne change cela ; il rend simplement la partie technique fonctionnelle. Lisez les conditions de Google et son robots.txt, et traitez les deux comme la frontière de ce que vous collectez.

Quelques lignes à respecter. Collectez uniquement les données publiques de résultats de vol : les compagnies, prix, horaires, durées et nombres d'escales que tout le monde peut voir sur une page de résultats sans compte. Maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger les serveurs de Google, et rythmez votre exploration plutôt que de la lancer à pleine vitesse. Ce guide est délibérément limité à ces annonces publiques car c'est la ligne qui rend le travail défendable. Il ne couvre rien derrière une connexion, les données de compte ou personnelles, les flux de paiement ou de réservation, ni le contenu tarifaire que vous redistribueriez comme le vôtre.

Il vaut aussi la peine de savoir que les tarifs sous-jacents proviennent des compagnies aériennes et des systèmes de distribution mondiaux, et de nombreuses compagnies publient des API officielles ou des programmes partenaires pour ce type d'accès. Si votre projet nécessite des données tarifaires garanties, à grand volume et redistribuables, un accord officiel de données est la bonne voie, pas un scraper plus astucieux. Utilisez le scraping pour la surveillance et la recherche sur des annonces publiques, et optez pour une API agréée quand vous dépassez ce cadre.

Récapitulatif

Points clés

  • Google Flights est entièrement rendu côté client. Une simple récupération retourne une coquille vide, donc le rendu est essentiel, non optionnel, pour voir les moindres cartes de vol.
  • Vous avez besoin du rendu et d'une IP de confiance ensemble. La Crawling API prend un token JavaScript, exécute la page dans un vrai navigateur, fait tourner les IPs résidentielles côté serveur et retourne le HTML final.
  • BeautifulSoup effectue l'extraction. Sélectionnez chaque annonce li.pIav2d, lisez compagnie, prix, départ, arrivée, durée et escales, et attendez-vous à ce que les noms de classes obfusqués évoluent.
  • Exportez en JSON et CSV. Le CSV avec une ligne par vol est la forme pour le suivi de tarifs : ajoutez chaque exécution et la colonne prix devient une série temporelle.
  • Restez sur les données publiques. Respectez les CGU de Google et robots.txt, maintenez un volume faible, et préférez une API officielle de compagnie ou GDS pour un accès à grand volume et redistribuable.

Foire aux questions

Pourquoi une requête ordinaire ne retourne-t-elle aucun vol sur Google Flights ?

Google Flights construit ses cartes de résultats côté client avec JavaScript, donc le HTML brut qu'un simple requests.get télécharge est essentiellement une coquille vide sans vols. Pour voir les annonces, vous devez rendre la page dans un vrai navigateur. Récupérer via la Crawling API avec le token JavaScript effectue ce rendu pour vous et retourne le HTML final, c'est pourquoi chaque sélecteur dans ce guide suppose une page rendue.

Puis-je extraire Google Flights avec Python ?

Oui. Avec la bibliothèque crawlbase et BeautifulSoup, vous pouvez récupérer une page de résultats rendue et extraire la compagnie, le prix, les heures de départ et d'arrivée, la durée et les escales. La Crawling API agit comme le pont qui rend la page et achemine votre requête vers Google depuis une IP de confiance, pour que les requêtes soient traitées sans blocage. Pour un tutoriel Python plus général, consultez notre guide sur l'extraction de sites web avec Python.

Quels champs puis-je extraire d'une annonce Google Flights ?

Ce tutoriel extrait six champs pour chaque vol : la compagnie, le prix, l'heure de départ, l'heure d'arrivée (y compris les indicateurs +1 / +2 jour), la durée totale et le texte des escales comme "Nonstop" ou "1 stop". Vous pouvez l'étendre à d'autres champs visibles comme les aéroports de correspondance ou les estimations carbone en ajoutant des sélecteurs. Restez dans les données publiques de résultats de vol et évitez tout ce qui est derrière une connexion ou un flux de réservation.

Ai-je besoin du rendu JavaScript pour extraire Google Flights ?

Oui, et c'est la différence clé avec de nombreuses autres cibles. Google Flights n'a presque rien dans son HTML brut ; les cartes de vol n'apparaissent qu'après l'exécution de JavaScript. La Crawling API offre un token de rendu JavaScript ainsi que les options ajax_wait et page_wait qui récupèrent la page comme le ferait un vrai navigateur. Notre guide sur l'exploration de sites JavaScript explique quand le rendu est nécessaire et comment il fonctionne.

Comment surveiller un tarif dans le temps ?

Exécutez le scraper selon un planning pour un itinéraire et une date fixes, et ajoutez la ligne CSV de chaque exécution (avec un horodatage) à un fichier d'historique. La colonne prix sur plusieurs exécutions est votre courbe tarifaire, que vous pouvez tracer ou relier à une alerte quand elle descend sous un seuil. Maintenez une cadence modeste, quelques vérifications par jour et par itinéraire suffisent, pour rester dans le cadre des recommandations de rythme ci-dessus.

Mes sélecteurs ne retournent rien. Qu'est-ce qui a changé ?

Presque certainement le balisage de Google. Les noms de classes comme pIav2d et sSHqwe sont obfusqués et tournent lorsque Google redéploie son interface, donc les sélecteurs qui fonctionnaient le mois dernier peuvent cesser de marcher. Commencez par confirmer que la page s'est bien rendue en augmentant page_wait ; si c'est le cas et que les champs sont toujours vides, réinspectez une page de résultats en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs. La maintenance périodique des sélecteurs est normale pour tout scraper en production.

Commencer à construire

Crawlez n'importe quel site à grande échelle, sans combattre l'infrastructure.

Crawlbase gère les proxies, les empreintes et les CAPTCHA afin que votre équipe livre des pipelines de données au lieu de maintenir la plomberie de crawl. 1 000 requêtes gratuites, sans carte requise.

En libre-service · Sans appel commercial requis · Volumes de crawl entreprise disponibles