Expedia est l'une des plus grandes marketplaces de voyage en ligne, et les données d'hôtels, de vols et d'activités qu'elle affiche sont genuinement utiles pour faire de la recherche tarifaire, surveiller un marché ou construire un produit de voyage. Le problème est qu'Expedia rend ses résultats côté client et se défend activement contre les bots, de sorte qu'une simple requête HTTP vous renvoie une coquille vide. Ce guide vous montre comment scraper Expedia avec JavaScript : une petite application Node fonctionnelle qui rend la page, analyse les annonces d'hôtels avec Cheerio, gère la pagination et écrit les résultats en CSV.

Pour rester honnête et défendable, l'ensemble du tutoriel se limite aux données publiques : les annonces d'hôtels et de vols, les prix par nuit, la disponibilité, les notes et le nombre d'avis que tout le monde peut voir sans se connecter. Il ne touche pas aux comptes utilisateurs, aux contenus réservés à une connexion, aux actions de réservation ni aux données personnelles. La section éthique et CGU près de la fin n'est pas du remplissage, lisez-la avant de pointer ceci sur un volume de production.

Pourquoi scraper Expedia pour des données de voyage

Les prix publics de voyage bougent en permanence, et une seule page vue vous dit seulement ce qu'un tarif affiche en ce moment. Scraper les annonces publiques d'Expedia vous permet de suivre les tarifs par nuit et la disponibilité dans le temps, ce dont dépendent la surveillance des prix, l'analyse concurrentielle et la recherche sur la demande. Pour un ingénieur, la valeur est dans les données structurées : transformer une page de recherche rendue en lignes propres que vous pouvez interroger, visualiser ou alimenter dans un modèle.

C'est la même forme de problème que n'importe quel travail de scraping web e-commerce. La différence est que la pile anti-bot d'Expedia est plus agressive que celle d'une boutique typique, donc l'approche doit en tenir compte dès la première requête.

Comprendre la cible : l'URL de recherche d'hôtels Expedia

Expedia expose plusieurs surfaces de recherche (vols, voitures, croisières, activités), mais la recherche d'hôtels est la plus propre à travailler et celle que ce guide utilise. Ses résultats se trouvent à une URL prévisible dont les paramètres de requête correspondent directement au formulaire de recherche, vous pouvez donc construire n'importe quelle recherche par programmation sans piloter l'interface utilisateur.

Voici un exemple concret : deux adultes, une chambre, à Dubaï, pour un séjour de quatre nuits.

bash
https://www.expedia.com/Hotel-Search?adults=2&rooms=1&destination=Dubai&startDate=2026-07-10&endDate=2026-07-14

Les paramètres importants :

  • destination le lieu de recherche, encodé en URL (une ville, une région ou un nom de propriété).
  • adults le nombre d'adultes.
  • rooms le nombre de chambres à tarifer.
  • startDate et endDate la fenêtre de séjour, au format YYYY-MM-DD.

Construisez l'URL avec les paramètres qui vous intéressent et vous avez une cible répétable. Variez la destination et les dates dans une boucle et vous avez un job de surveillance des prix.

Pourquoi une simple requête échoue ici

Si vous demandez cette URL avec un client HTTP brut, vous obtenez une réponse avec le statut 200 et presque aucune donnée d'hôtel dans le corps. Deux choses jouent contre vous. Premièrement, Expedia rend ses annonces dans le navigateur avec JavaScript, donc le HTML initial est une coquille qui n'est remplie qu'après l'exécution des scripts de la page dans un navigateur. Deuxièmement, Expedia détecte rapidement le trafic automatisé : les IP de centres de données et les schémas de requêtes qui ne ressemblent pas à un vrai navigateur sont challengés ou bloqués avant d'accéder au contenu rendu.

Un scraper Expedia fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend réellement la page, et une IP que la plateforme considère comme un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur sans interface graphique et un pool de proxies résidentiels rotatifs, mais les assembler et les maintenir en bon état représente l'essentiel du travail. La Crawling API Crawlbase regroupe les deux en un seul appel : vous lui envoyez l'URL avec un token JavaScript, elle rend la page derrière une IP de confiance, et vous retourne le HTML final prêt à analyser.

Why a JS token

Crawlbase propose deux types de tokens. Le token normal récupère le HTML statique ; le token JavaScript (JS) rend d'abord la page dans un vrai navigateur. Expedia est rendu côté client, donc vous avez besoin du token JS ici. Utiliser le token normal renvoie la même coquille vide qu'une simple requête.

Configurer le projet

Vous avez besoin de Node.js et npm installés. Confirmez les deux, puis créez un projet et installez les bibliothèques.

bash
node --version
npm --version

mkdir expedia-scraper && cd expedia-scraper
npm init -y
npm install cheerio crawlbase csv-writer

Trois dépendances font le travail : crawlbase est le client pour la Crawling API, cheerio analyse le HTML renvoyé avec une API similaire à jQuery côté serveur, et csv-writer sérialise vos résultats. Si vous souhaitez un stockage interrogeable plutôt que des fichiers plats, ajoutez sqlite3 et écrivez les lignes dans une table ; le chemin CSV ci-dessous couvre le cas courant.

Vous avez également besoin d'un compte Crawlbase et d'un token JS, que vous obtenez depuis le tableau de bord après l'inscription. Insérez-le dans le code partout où vous voyez YOUR_CRAWLBASE_JS_TOKEN.

Récupérer le HTML rendu

Commencez par obtenir la page finale. Vous passez deux options importantes pour un site comme Expedia : ajax_wait indique à l'API d'attendre le chargement du contenu asynchrone, et page_wait maintient l'attente un nombre fixe de millisecondes après le chargement pour que les annonces à rendu tardif aient le temps d'apparaître. Cinq secondes est un bon point de départ ; augmentez si les résultats reviennent maigres.

javascript
const { CrawlingAPI } = require('crawlbase')

const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_JS_TOKEN' })

const options = {
  ajax_wait: true,
  page_wait: 5000,
}

const expediaURL =
  'https://www.expedia.com/Hotel-Search?adults=2&rooms=1&destination=Paris&startDate=2026-07-10&endDate=2026-07-14'

async function fetchExpediaHTML(url) {
  const response = await api.get(url, options)
  return response.body
}

fetchExpediaHTML(expediaURL).then((html) => console.log(html))

Exécutez avec node expedia-scraper.js et vous devriez voir le vrai balisage avec des cartes d'hôtels, pas la coquille vide qu'une simple requête renvoie. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.

Crawlbase Expedia Scraper

Expedia nécessite une page rendue derrière une IP de confiance, en un seul appel. La Crawling API prend un token JS, exécute la page dans un vrai navigateur, fait tourner les IP résidentielles côté serveur, et vous remet le HTML final, pour que vous évitiez de gérer une flotte sans interface et un pool de proxies. Testez-la sur une recherche publique d'hôtels avec le niveau gratuit.

Analyser les annonces d'hôtels avec Cheerio

Le HTML en main, chargez-le dans Cheerio et parcourez les cartes de propriétés. Chaque carte porte les champs souhaités : nom de l'hôtel, prix par nuit, prix total, note et nombre d'avis. Inspectez la page en direct dans les outils de développement de votre navigateur pour trouver les sélecteurs actuels, puis associez chaque champ à l'un d'eux.

javascript
const cheerio = require('cheerio')

function extractHotelDetails(html) {
  const $ = cheerio.load(html)
  const hotels = []

  $('div[data-stid="property-listing-results"] .uitk-card').each((i, el) => {
    hotels.push({
      name: $(el).find('h3.uitk-heading').text().trim(),
      pricePerNight: $(el).find('[data-test-id="price-summary"] .uitk-text').first().text().trim(),
      totalPrice: $(el).find('[data-test-id="price-summary"] .uitk-text').last().text().trim(),
      rating: $(el).find('.uitk-badge .uitk-badge-base-text').text().trim(),
      reviews: $(el).find('.uitk-text[aria-hidden="false"]').last().text().trim(),
    })
  })

  return hotels
}
Selectors drift

Les noms de classes d'Expedia (les préfixes uitk-* et les attributs data-stid) changent sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, non comme un contrat. Quand l'extraction renvoie des chaînes vides, ré-inspectez la page en direct et mettez à jour les sélecteurs. C'est une maintenance normale pour tout scraper en production, pas le signe que quelque chose est cassé.

Reliez la récupération et l'analyse dans une fonction main pour avoir un script exécutable unique.

javascript
async function main() {
  const html = await fetchExpediaHTML(expediaURL)
  const hotels = extractHotelDetails(html)
  console.log(hotels)
}

main().catch((err) => console.error('Scrape failed:', err))

À quoi ressemble la sortie

Exécutez le script complet et vous obtenez un tableau d'objets d'hôtels structurés. Un échantillon réduit :

json
[
  {
    "name": "OKKO Hotels Paris Rueil-Malmaison",
    "pricePerNight": "$118",
    "totalPrice": "$542 total",
    "rating": "9.0",
    "reviews": "286 reviews"
  },
  {
    "name": "Grand Hotel Leveque",
    "pricePerNight": "$174",
    "totalPrice": "$782 total",
    "rating": "8.4",
    "reviews": "1,004 reviews"
  },
  {
    "name": "citizenM Paris Opera",
    "pricePerNight": "$192",
    "totalPrice": "$871 total",
    "rating": "9.6",
    "reviews": "76 reviews"
  }
]

Gérer la pagination

Expedia charge son premier lot de résultats puis en révèle davantage derrière un bouton "Afficher plus de résultats" plutôt qu'une liste de pages numérotées. La Crawling API peut cliquer ce bouton pour vous avec l'option css_click_selector : passez un sélecteur CSS valide et encodé en URL, et l'API le clique après le rendu de la page, de sorte que les annonces supplémentaires se chargent avant que le HTML ne soit renvoyé.

javascript
const options = {
  ajax_wait: true,
  page_wait: 10000,
  css_click_selector: encodeURIComponent('button[data-stid="show-more-results"]'),
}

Substituez cela dans votre objet options et la prochaine exécution retournera plus d'hôtels qu'auparavant. Accordez un peu plus de marge à page_wait lors du clic, puisque les résultats supplémentaires ont besoin de temps pour se rendre après le déclenchement du clic.

Stocker les résultats en CSV

Loguer dans la console convient pendant l'itération, mais vous souhaitez les données sur disque. La bibliothèque csv-writer mappe chaque clé d'objet sur une colonne et ajoute vos lignes en quelques lignes de code.

javascript
const createCsvWriter = require('csv-writer').createObjectCsvWriter

function saveToCSV(data) {
  const writer = createCsvWriter({
    path: 'hotels.csv',
    header: [
      { id: 'name', title: 'Name' },
      { id: 'pricePerNight', title: 'Price Per Night' },
      { id: 'totalPrice', title: 'Total Price' },
      { id: 'rating', title: 'Rating' },
      { id: 'reviews', title: 'Reviews' },
    ],
  })

  return writer.writeRecords(data).then(() => console.log('Saved hotels.csv'))
}

Appelez saveToCSV(hotels) depuis main à la place du log, et chaque exécution écrit un fichier hotels.csv soigné que vous pouvez ouvrir dans n'importe quel tableur ou charger dans un pipeline. Si vous préférez interroger les données avec SQL, écrivez les mêmes lignes dans une table SQLite avec sqlite3 ; l'analyse reste identique.

Rester non bloqué

Même avec le rendu géré, Expedia surveille le trafic ressemblant à un scraper. Quelques habitudes maintiennent une exécution saine, et elles s'appliquent à toute cible commerciale difficile.

  • Pacez vos requêtes. Bombarder la même recherche dans une boucle serrée est le moyen le plus rapide d'être limité. Répartissez les requêtes et variez vos paramètres de recherche.
  • Misez sur la rotation. Un pool de proxies résidentiels répartit les requêtes sur de nombreuses IP d'utilisateurs réels, de sorte qu'aucune adresse ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre infrastructure, c'est la partie à bien faire.
  • Lisez les codes de statut. Une exécution qui commence à renvoyer des challenges ou des erreurs vous indique que le taux actuel ou le niveau d'IP n'est plus suffisant. Traitez les codes d'erreur de statut de proxy comme un signal, pas comme du bruit.

Pour le guide plus complet à ce sujet, voir comment scraper des sites web sans être bloqué. Si vous souhaitez comparer cette approche avec une construction basée sur un navigateur sans interface, le scraping web avec Python et Selenium décrit cette pile.

La partie honnête : CGU et légalité

Scraper un grand site de voyage commercial se situe dans une zone grise juridique, et la réponse à "est-ce autorisé" dépend des conditions d'utilisation de la plateforme, de votre juridiction et de ce que vous faites des données. Les conditions d'Expedia restreignent l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quelle que soit la prudence de vos outils. Aucun code ici ne change cela ; il ne fait que rendre la partie technique fonctionnelle.

Quelques lignes à respecter. Collectez uniquement des données publiques : les annonces, les prix, la disponibilité et les notes que tout le monde peut voir sans compte. Respectez le robots.txt du site et ses attentes en matière de débit déclarées, et gardez votre volume de requêtes assez bas pour ne pas surcharger les serveurs de qui que ce soit. Si vous prévoyez de réutiliser les données commercialement, obtenez une autorisation ou un accord de données officiel plutôt que de supposer que le silence est un consentement. Et ne collectez jamais de données personnelles, y compris tout ce qui est lié à des comptes utilisateurs individuels.

Ce guide est délibérément limité aux données d'annonces publiques parce que c'est la ligne qui rend le travail défendable. Il ne couvre rien derrière une connexion, ni les données de compte ou de profil, ni les avis liés à des personnes identifiables, ni les actions de réservation d'aucune sorte. Si votre projet nécessite plus que des annonces publiques, la bonne démarche est une API officielle ou un accord de données avec Expedia, pas un scraper plus ingénieux. Pour en savoir plus sur la façon dont l'accès géré diffère du scraping brut, qu'est-ce qu'un proxy API est une lecture utile.

Récapitulatif

Points clés

  • Expedia est rendu côté client. Une simple requête renvoie une coquille vide, vous devez donc rendre la page avant de l'analyser.
  • Vous avez besoin du rendu et d'une IP de confiance ensemble. La Crawling API avec un token JS fait les deux en un seul appel ; ajax_wait et page_wait contrôlent la durée d'attente du contenu.
  • Cheerio effectue l'extraction. Associez le nom, le prix, la note et les avis aux sélecteurs actuels, et anticipez leur dérive dans le temps.
  • La pagination est un clic. Utilisez css_click_selector pour déclencher "afficher plus de résultats" avant que le HTML ne soit renvoyé.
  • Restez sur des données publiques. Respectez les CGU et le robots.txt d'Expedia ; pas de comptes, pas de données personnelles, pas d'actions de réservation.

Foire aux questions

Pourquoi une simple requête ne renvoie-t-elle aucune donnée d'hôtel depuis Expedia ?

Parce qu'Expedia rend ses annonces côté client avec JavaScript. Le HTML initial est une coquille qui n'est remplie qu'après l'exécution des scripts de la page dans un navigateur, donc une requête HTTP brute renvoie le statut 200 avec les champs d'hôtels vides. Pour obtenir de vraies données, vous devez d'abord rendre la page, ce que le token JS de la Crawling API gère pour vous.

Ai-je besoin du token normal ou du token JS pour Expedia ?

Le token JS. Le token normal récupère le HTML statique, qui sur Expedia est la même coquille vide qu'une simple requête renvoie. Le token JS rend la page dans un vrai navigateur avant de retourner le HTML, de sorte que les annonces sont présentes lorsque Cheerio les analyse.

Mes sélecteurs Cheerio renvoient des chaînes vides. Qu'est-ce qui a changé ?

Presque certainement le balisage d'Expedia. Ses noms de classes uitk-* et ses attributs data-stid changent sans préavis, donc des sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Ré-inspectez une page de recherche 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.

Comment obtenir plus que la première page de résultats ?

Expedia révèle des hôtels supplémentaires derrière un bouton "Afficher plus de résultats" plutôt que des pages numérotées. Passez le sélecteur CSS de ce bouton à l'option css_click_selector de la Crawling API, encodé en URL, et l'API le clique après le rendu pour que les annonces supplémentaires se chargent avant le retour du HTML. Accordez un peu plus de temps à page_wait quand vous faites cela.

Comment éviter d'être bloqué lors du scraping d'Expedia ?

Gardez votre taux de requêtes par IP bas, variez vos paramètres de recherche plutôt que de boucler sur la même URL, et routez via des IP résidentielles rotatives pour qu'aucune adresse ne déclenche une limite de débit. La Crawling API gère la rotation et un pool d'IP de confiance pour vous ; si vous construisez votre propre infrastructure, c'est la partie dans laquelle investir. Surveillez les codes de statut et reculez quand vous commencez à voir des challenges.

Est-il légal de scraper Expedia ?

Cela dépend des conditions d'utilisation d'Expedia, de votre juridiction et de votre objectif, et leurs conditions restreignent l'accès automatisé. Limitez-vous strictement aux données d'annonces publiques, respectez robots.txt et les attentes de débit, et ne touchez jamais aux comptes, aux données personnelles ni aux actions de réservation. Pour une réutilisation commerciale, obtenez une autorisation ou un accord de données officiel plutôt que de vous fier à un scraper.

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