Expedia ist einer der größten Online-Reisemarktplätze, und die Hotel-, Flug- und Aktivitätsdaten, die er bereitstellt, sind wirklich nützlich, wenn Sie Preisrecherche betreiben, einen Markt überwachen oder ein Reiseprodukt aufbauen. Der Haken ist, dass Expedia seine Ergebnisse clientseitig rendert und Bots gegenüber stark abschirmt, sodass ein einfacher HTTP-Abruf nur eine leere Hülle liefert. Diese Anleitung zeigt, wie man Expedia mit JavaScript scraped: ein kleines, lauffähiges Node-Build, das die Seite rendert, Hotel-Listings mit Cheerio parst, paginiert und die Ergebnisse in CSV schreibt.
Um dies fair und vertretbar zu halten, beschränkt sich die gesamte Anleitung auf öffentliche Daten: Hotel- und Flugbuchungen, Übernachtungspreise, Verfügbarkeit, Bewertungen und Rezensionsanzahlen, die jeder ohne Anmeldung sehen kann. Sie berührt keine Benutzerkonten, login-gesperrte Inhalte, Buchungsaktionen oder personenbezogene Daten. Der Abschnitt zu Ethik und Nutzungsbedingungen gegen Ende ist kein Standardtext, also lesen Sie ihn, bevor Sie das auf Produktionsvolumen anwenden.
Warum man Expedia für Reisedaten scraped
Öffentliche Reisepreise ändern sich ständig, und eine einzelne Seitenansicht zeigt nur, wie ein Tarif gerade jetzt aussieht. Das Scrapen von Expedia-Listings erlaubt es, Übernachtungspreise und Verfügbarkeit im Zeitverlauf zu verfolgen, was Preisüberwachung, Konkurrenzanalyse und Nachfrageforschung allesamt erfordern. Für Entwickler liegt der Wert in strukturierten Daten: eine gerenderte Suchseite in saubere Zeilen umzuwandeln, die man abfragen, visualisieren oder in ein Modell einspeisung kann.
Das ist dieselbe Art Problem wie jeder E-Commerce-Web-Scraping-Auftrag. Der Unterschied ist, dass Expedia' Anti-Bot-Stack aggressiver ist als ein typischer Shop, sodass der Ansatz das von der ersten Anfrage an berücksichtigen muss.
Das Ziel verstehen: Expedia' Hotel-Such-URL
Expedia bietet mehrere Suchoberflächen (Flüge, Mietwagen, Kreuzfahrten, Aktivitäten), aber die Hotelsuche ist die sauberste zum Arbeiten und die, die diese Anleitung verwendet. Ihre Ergebnisse liegen unter einer vorhersagbaren URL, deren Abfrageparameter direkt dem Suchformular zugeordnet sind, sodass Sie jede Suche programmatisch konstruieren können, ohne die Benutzeroberfläche zu bedienen.
Hier ist ein konkretes Beispiel: zwei Erwachsene, ein Zimmer, in Dubai, für einen viernächtigen Aufenthalt.
https://www.expedia.com/Hotel-Search?adults=2&rooms=1&destination=Dubai&startDate=2026-07-10&endDate=2026-07-14
Die relevanten Parameter:
- destination der Suchort, URL-kodiert (eine Stadt, Region oder Unterkunftsname).
- adults die Anzahl der erwachsenen Gäste.
- rooms wie viele Zimmer zu bepreisen sind.
-
startDate und endDate das Aufenthaltsfenster im Format
YYYY-MM-DD.
Erstellen Sie die URL mit den gewünschten Parametern und Sie haben ein wiederholbares Ziel. Variieren Sie Ziel und Datum in einer Schleife und Sie haben einen Preisüberwachungsauftrag.
Warum ein einfacher Abruf hier scheitert
Wenn Sie diese URL mit einem einfachen HTTP-Client anfragen, erhalten Sie eine Antwort mit Status 200 und fast keine Hoteldaten im Body. Zwei Dinge wirken gegen Sie. Erstens rendert Expedia seine Listings im Browser mit JavaScript, sodass das anfängliche HTML eine Hülle ist, die erst nach dem Ausführen der Seitenskripte befüllt wird. Zweitens erkennt Expedia automatisierten Traffic schnell: Datacenter-IPs und Anfragemuster, die nicht wie ein echter Browser aussehen, werden herausgefordert oder blockiert, bevor sie jemals den gerenderten Inhalt sehen.
Ein funktionsfähiger Expedia-Scraper braucht also zwei Dinge in einer Anfrage: einen Browser, der die Seite tatsächlich rendert, und eine IP, die die Plattform als echten Besucher liest. Sie können das selbst mit einem Headless-Browser und einem Pool aus rotierenden Residential-Proxys zusammenstellen, aber diese zu verknüpfen und in Schuss zu halten, ist der größte Teil der Arbeit. Die Crawlbase Crawling API bündelt beides in einem einzigen Aufruf: Sie übergeben ihr die URL mit einem JavaScript-Token, sie rendert die Seite hinter einer vertrauenswürdigen IP und liefert das fertige HTML zum Parsen zurück.
Crawlbase bietet zwei Token-Typen an. Der normale Token ruft statisches HTML ab; der JavaScript-Token (JS) rendert die Seite zuerst in einem echten Browser. Expedia wird clientseitig gerendert, also brauchen Sie hier den JS-Token. Die Verwendung des normalen Tokens liefert dieselbe leere Hülle wie ein einfacher Abruf.
Projekt einrichten
Sie benötigen Node.js und npm installiert. Bestätigen Sie beides, erstellen Sie dann ein Projekt und installieren Sie die Bibliotheken.
node --version npm --version mkdir expedia-scraper && cd expedia-scraper npm init -y npm install cheerio crawlbase csv-writer
Drei Abhängigkeiten erledigen die Arbeit: crawlbase ist der Client für die Crawling API, cheerio parst das zurückgegebene HTML mit einer jQuery-ähnlichen API auf dem Server, und csv-writer serialisiert Ihre Ergebnisse. Wenn Sie einen abfragbaren Speicher statt Flat-Files möchten, fügen Sie sqlite3 hinzu und schreiben Sie Zeilen in eine Tabelle; der unten beschriebene CSV-Pfad deckt den häufigen Anwendungsfall ab.
Sie benötigen außerdem ein Crawlbase-Konto und einen JS-Token, den Sie vom Dashboard nach der Registrierung erhalten. Fügen Sie ihn überall dort in den Code ein, wo Sie YOUR_CRAWLBASE_JS_TOKEN sehen.
Das gerenderte HTML abrufen
Beginnen Sie damit, die fertige Seite zu erhalten. Sie übergeben zwei Optionen, die für eine Site wie Expedia wichtig sind: ajax_wait weist die API an, auf das Laden asynchroner Inhalte zu warten, und page_wait hält für eine feste Anzahl Millisekunden nach dem Laden an, damit spät rendernde Listings Zeit haben zu erscheinen. Fünf Sekunden sind ein vernünftiger Ausgangspunkt; erhöhen Sie den Wert, wenn Ergebnisse dünn zurückkommen.
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))
Führen Sie es mit node expedia-scraper.js aus und Sie sollten echtes Markup mit Hotel-Karten darin sehen, nicht die leere Hülle, die ein einfacher Abruf zurückgibt. Das bestätigt, dass das Rendering funktioniert, bevor Sie einen einzigen Selektor schreiben.
Expedia benötigt eine gerenderte Seite hinter einer vertrauenswürdigen IP, in einem Aufruf. Die Crawling API nimmt einen JS-Token, führt die Seite in einem echten Browser aus, rotiert serverseitig durch Residential-IPs und liefert fertiges HTML, damit Sie selbst keine Headless-Flotte und keinen Proxy-Pool betreiben müssen. Testen Sie es zuerst auf einer öffentlichen Hotelsuche im kostenlosen Kontingent.
Hotel-Listings mit Cheerio parsen
Mit dem HTML in der Hand laden Sie es in Cheerio und gehen die Hotel-Karten durch. Jede Karte enthält die gewünschten Felder: Hotelname, Preis pro Nacht, Gesamtpreis, Bewertung und Anzahl der Rezensionen. Inspizieren Sie die Live-Seite in den Entwicklertools Ihres Browsers, um die aktuellen Selektoren zu finden, und ordnen Sie dann jedes Feld einem zu.
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 }
Expedias Klassennamen (die uitk-*-Präfixe und data-stid-Attribute) ändern sich ohne Vorankündigung. Betrachten Sie die obigen Selektoren als Ausgangspunkt, nicht als Vertrag. Wenn die Extraktion leere Zeichenketten zurückgibt, inspizieren Sie die Live-Seite erneut und aktualisieren Sie die Selektoren. Das ist normale Wartung für jeden Produktions-Scraper, kein Zeichen, dass etwas kaputt ist.
Verbinden Sie Abruf und Parse in einer main-Funktion, damit Sie ein lauffähiges Skript haben.
async function main() { const html = await fetchExpediaHTML(expediaURL) const hotels = extractHotelDetails(html) console.log(hotels) } main().catch((err) => console.error('Scrape failed:', err))
Wie die Ausgabe aussieht
Führen Sie das vollständige Skript aus und Sie erhalten ein Array strukturierter Hotel-Objekte. Ein gekürztes Beispiel:
[ { "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" } ]
Paginierung verwalten
Expedia lädt seine erste Ergebnischarge und zeigt dann weitere hinter einem "Weitere Ergebnisse anzeigen"-Button statt einer nummerierten Seitenliste. Die Crawling API kann diesen Button für Sie mit der Option css_click_selector klicken: Übergeben Sie einen gültigen, URL-kodierten CSS-Selektor und die API klickt ihn nach dem Rendern der Seite, sodass die zusätzlichen Listings laden, bevor das HTML zurückkommt.
const options = { ajax_wait: true, page_wait: 10000, css_click_selector: encodeURIComponent('button[data-stid="show-more-results"]'), }
Tauschen Sie das in Ihr Options-Objekt ein und der nächste Lauf gibt mehr Hotels zurück als zuvor. Geben Sie page_wait etwas mehr Spielraum beim Klicken, da die zusätzlichen Ergebnisse Zeit brauchen, um nach dem Klick zu rendern.
Ergebnisse in CSV speichern
Die Ausgabe auf der Konsole ist gut beim Iterieren, aber Sie möchten die Daten auf der Festplatte. Die Bibliothek csv-writer ordnet jeden Objektschlüssel einer Spalte zu und fügt Ihre Zeilen in wenigen Zeilen ein.
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')) }
Rufen Sie saveToCSV(hotels) aus main statt der Protokollierung auf, und jeder Lauf schreibt eine ordentliche hotels.csv, die Sie in jeder Tabellenkalkulation öffnen oder in eine Pipeline laden können. Wenn Sie die Daten lieber mit SQL abfragen möchten, schreiben Sie dieselben Zeilen mit sqlite3 in eine SQLite-Tabelle; das Parsen bleibt identisch.
Geblockt bleiben verhindern
Auch wenn das Rendering gehandhabt wird, beobachtet Expedia scrapertypischen Traffic. Einige Gewohnheiten halten einen Lauf gesund, und sie gelten für jedes schwierige kommerzielle Ziel.
- Dosieren Sie Ihre Anfragen. Dieselbe Suche in einem engen Loop zu hämmern, ist der schnellste Weg zur Drosselung. Verteilen Sie Anfragen und variieren Sie Ihre Suchparameter.
- Setzen Sie auf Rotation. Ein Pool aus Residential-Proxys verteilt Anfragen über viele echte Nutzer-IPs, sodass keine einzelne ein Rate-Limit auslöst. Die Crawling API erledigt das für Sie; wenn Sie Ihren eigenen Stack aufbauen, ist das der Teil, den Sie richtig machen müssen.
- Lesen Sie die Statuscodes. Ein Lauf, der Herausforderungen oder Fehler zurückzugeben beginnt, zeigt Ihnen, dass die aktuelle Rate oder IP-Stufe nicht mehr ausreicht. Behandeln Sie Proxy-Statuscodes als Signal, nicht als Rauschen.
Das umfassende Playbook dazu finden Sie unter Wie man Websites scraped, ohne geblockt zu werden. Wenn Sie diesen Ansatz mit einem Headless-Browser-Build vergleichen möchten, beschreibt Web-Scraping mit Python und Selenium diesen Stack.
Der ehrliche Teil: Nutzungsbedingungen und Legalität
Das Scrapen einer großen kommerziellen Reise-Website befindet sich in einer rechtlichen Grauzone, und die Antwort auf "Ist es erlaubt?" hängt von den Nutzungsbedingungen der Plattform, Ihrer Jurisdiktion und dem Verwendungszweck der Daten ab. Expedias Bedingungen schränken den automatisierten Zugriff ein, sodass Scraping unabhängig von der Sorgfalt Ihrer Werkzeuge gegen diese Bedingungen verstoßen kann. Keiner der hier gezeigten Code ändert das; er lässt lediglich den technischen Teil funktionieren.
Einige Linien, an denen man festhalten sollte. Sammeln Sie nur öffentliche Daten: Listings, Preise, Verfügbarkeit und Bewertungen, die jeder ohne Account sehen kann. Respektieren Sie die robots.txt der Website und ihre angegebenen Rate-Erwartungen, und halten Sie Ihr Anfragevolumen niedrig genug, damit Sie niemandes Server belasten. Wenn Sie die Daten kommerziell wiederverwenden planen, holen Sie eine Genehmigung oder eine offizielle Datenvereinbarung ein, anstatt anzunehmen, Schweigen sei Zustimmung. Und sammeln Sie niemals personenbezogene Daten, einschließlich allem, was mit individuellen Benutzerkonten verbunden ist.
Diese Anleitung ist bewusst auf öffentliche Listing-Daten beschränkt, weil das die Grenze ist, die die Arbeit vertretbar macht. Sie deckt nichts hinter einem Login ab, keine Konto- oder Profildaten, keine mit identifizierbaren Personen verbundenen Bewertungen und keine Buchungsaktionen jeglicher Art. Wenn Ihr Projekt mehr als öffentliche Listings benötigt, ist der richtige Weg eine offizielle API oder eine Datenvereinbarung mit Expedia, kein ausgefeilterer Scraper. Für Hintergrundinformationen, wie sich verwalteter Zugriff vom rohen Scraping unterscheidet, ist Was ist ein API-Proxy eine nützliche Lektüre.
Wichtigste Erkenntnisse
- Expedia wird clientseitig gerendert. Ein einfacher Abruf gibt eine leere Hülle zurück, also müssen Sie die Seite rendern, bevor Sie sie parsen.
-
Sie brauchen Rendering und eine vertrauenswürdige IP gemeinsam. Die Crawling API mit einem JS-Token erledigt beides in einem Aufruf;
ajax_waitundpage_waitsteuern, wie lange sie auf Inhalte wartet. - Cheerio übernimmt die Extraktion. Ordnen Sie Name, Preis, Bewertung und Rezensionen den aktuellen Selektoren zu, und erwarten Sie, dass diese Selektoren mit der Zeit abweichen.
-
Paginierung ist ein Klick. Verwenden Sie
css_click_selector, um "Weitere Ergebnisse anzeigen" auszulösen, bevor das HTML zurückkommt. - Bleiben Sie bei öffentlichen Daten. Respektieren Sie Expedias Nutzungsbedingungen und robots.txt; keine Konten, keine personenbezogenen Daten, keine Buchungsaktionen.
Häufig gestellte Fragen
Warum gibt ein einfacher Abruf keine Hoteldaten von Expedia zurück?
Weil Expedia seine Listings clientseitig mit JavaScript rendert. Das anfängliche HTML ist eine Hülle, die sich erst nach dem Ausführen der Seitenskripte in einem Browser füllt, sodass eine rohe HTTP-Anfrage Status 200 zurückgibt, aber die Hotelfelder leer bleiben. Um echte Daten zu erhalten, müssen Sie die Seite zuerst rendern, was der JS-Token der Crawling API für Sie erledigt.
Brauche ich den normalen Token oder den JS-Token für Expedia?
Den JS-Token. Der normale Token ruft statisches HTML ab, was bei Expedia dieselbe leere Hülle wie ein einfacher Abruf ist. Der JS-Token rendert die Seite in einem echten Browser, bevor er das HTML zurückgibt, sodass die Listings vorhanden sind, wenn Cheerio sie parst.
Meine Cheerio-Selektoren geben leere Zeichenketten zurück. Was hat sich geändert?
Mit ziemlicher Sicherheit Expedias Markup. Seine uitk-*-Klassennamen und data-stid-Attribute ändern sich ohne Vorankündigung, sodass Selektoren, die letzten Monat funktionierten, jetzt brechen können. Inspizieren Sie eine Live-Suchseite in den Entwicklertools Ihres Browsers und aktualisieren Sie die Selektoren. Regelmäßige Selektor-Wartung ist bei jedem Produktions-Scraper normal.
Wie erhalte ich mehr als die erste Seite der Ergebnisse?
Expedia zeigt zusätzliche Hotels hinter einem "Weitere Ergebnisse anzeigen"-Button statt nummerierten Seiten. Übergeben Sie den CSS-Selektor dieses Buttons URL-kodiert an die Option css_click_selector der Crawling API, und die API klickt ihn nach dem Rendern, sodass die zusätzlichen Listings laden, bevor das HTML zurückkommt. Geben Sie page_wait dabei etwas mehr Zeit.
Wie vermeide ich, beim Scrapen von Expedia geblockt zu werden?
Halten Sie Ihre pro-IP-Anfragefrequenz niedrig, variieren Sie Ihre Suchparameter statt dieselbe URL zu loopen, und leiten Sie durch rotierende Residential-IPs, damit keine einzelne Adresse ein Rate-Limit auslöst. Die Crawling API verwaltet Rotation und einen vertrauenswürdigen IP-Pool für Sie; wenn Sie Ihren eigenen Stack aufbauen, ist das der Teil, in den Sie investieren müssen. Beobachten Sie die Statuscodes und treten Sie einen Schritt zurück, wenn Herausforderungen auftreten.
Ist das Scrapen von Expedia legal?
Das hängt von Expedias Nutzungsbedingungen, Ihrer Jurisdiktion und Ihrem Zweck ab, und ihre Bedingungen schränken den automatisierten Zugriff ein. Beschränken Sie sich strikt auf öffentliche Listing-Daten, respektieren Sie robots.txt und Rate-Erwartungen und berühren Sie niemals Konten, personenbezogene Daten oder Buchungsaktionen. Für kommerzielle Wiederverwendung holen Sie eine Genehmigung oder eine offizielle Datenvereinbarung ein, anstatt sich auf einen Scraper zu verlassen.
Crawlen Sie jede Website im großen Maßstab, ohne gegen die Infrastruktur zu kämpfen.
Crawlbase übernimmt Proxys, Fingerprints und CAPTCHAs, damit Ihr Team Datenpipelines ausliefert, statt Crawl-Infrastruktur zu pflegen. 1.000 Anfragen kostenlos, keine Karte erforderlich.
