TripAdvisor mit Python scrapen – ohne gesperrt zu werden

Zuletzt aktualisiert am April 17, 2026

Letzte Woche wollte ich für rund 200 Unterkünfte in drei europäischen Städten Hotelbewertungen und die Anzahl der Rezensionen von TripAdvisor abrufen. Mein erstes Skript – ein simples requests.get() mit Standard-Headern – lieferte bei jeder einzelnen Anfrage ein sauberes 403 Forbidden. Nicht ein einziger brauchbarer Datenbyte.

TripAdvisor ist eine der wertvollsten öffentlichen Datenquellen in der Reisebranche: über , mehr als 8 Millionen Unternehmenseinträge und rund 460 Millionen einzigartige Besucher pro Monat. Die Plattform beeinflusst jährlich Reiseausgaben von über . Aber diese Daten programmatisch zu bekommen? Genau da wird es heikel. TripAdvisor setzt DataDome zur Bot-Erkennung, Cloudflare WAF, TLS-Fingerprinting und JavaScript-Challenges ein – eine mehrschichtige Verteidigung, die die meisten naiven Scraping-Versuche schon im Ansatz stoppt. Dieser Leitfaden ist die Ressource, die ich mir damals gewünscht hätte: ein direkter Vergleich von drei Python-Ansätzen zum Scrapen (plus eine No-Code-Alternative), vollständiger Code für jede Methode, ein strukturierter Abschnitt zur Fehlerbehebung bei Anti-Bot-Sperren und wiederverwendbare Muster, die für Hotels, Restaurants und Sehenswürdigkeiten funktionieren. Egal ob du Python-Einsteiger oder erfahrener Entwickler bist – das spart dir jede Menge unnötige 403er.

Sie wollen keinen Code schreiben? TripAdvisor einfach scrapen

Ich möchte hier ehrlich sein: Viele, die nach „TripAdvisor mit Python scrapen“ suchen, wollen eigentlich gar nicht unbedingt programmieren. Sie wollen einfach Daten – Hotelnamen, Bewertungen, Rezensionenzahlen, Preise – schnell in einer Tabelle haben. Wenn das auf dich zutrifft, gibt es einen deutlich kürzeren Weg.

ist eine KI-gestützte Chrome-Erweiterung, die wir entwickelt haben. Sie kann jede TripAdvisor-Seite lesen und automatisch passende Spalten zum Extrahieren vorschlagen. Der Ablauf ist wirklich nur zwei Klicks lang:

  1. Eine TripAdvisor-Listenseite öffnen (z. B. Suchergebnisse für „Hotels in Paris“).
  2. In der Thunderbit-Seitenleiste auf „KI-Felder vorschlagen“ klicken. Die KI analysiert die Seite und schlägt Spalten wie Hotelname, Bewertung, Anzahl der Rezensionen, Preis und Standort vor.
  3. Auf „Scrape“ klicken. Thunderbit extrahiert die Daten aus allen Einträgen auf der Seite – und übernimmt bei Bedarf die Paginierung automatisch.
  4. Exportieren nach Excel, Google Sheets, Airtable oder Notion. Exporte sind in jedem Tarif kostenlos.

Thunderbit funktioniert ohne Konfiguration für Hotels, Restaurants und Sehenswürdigkeiten – die KI passt sich an den Seiteninhalt an. Bei seitenweise Ergebnissen erkennt es automatisch „Weiter“-Buttons und Endlos-Scrollen. Und weil es direkt in deinem echten Chrome-Browser läuft, verwendet es deine Session-Cookies und den Browser-Fingerprint – das verschafft einen natürlichen Vorteil gegenüber Bot-Erkennung.

Du kannst es mit der ausprobieren – der kostenlose Tarif umfasst 6 Seiten pro Monat, genug für einen Test.

Wenn du programmatische Kontrolle, eigene Parsing-Logik oder das Scrapen von mehr als 10.000 Seiten brauchst, ist Python der richtige Weg. Lies weiter.

Warum TripAdvisor mit Python scrapen?

TripAdvisor-Daten haben einen direkten, messbaren Geschäftswert. Eine zeigte, dass ein Anstieg des Global Review Index eines Hotels um 1 Punkt zu einem Anstieg des durchschnittlichen Tagespreises um 0,89 % und des Revenue per Available Room um 1,42 % führt. Eine separate fand heraus, dass ein externer Anstieg der TripAdvisor-Bewertung um 1 Stern für ein durchschnittliches Hotel zusätzlichen Jahresumsatz von 55.000 bis 75.000 US-Dollar bedeutet. Bewertungen sind also nicht nur eine Eitelkeitskennzahl – sie treiben den Umsatz.

So nutzen verschiedene Teams TripAdvisor-Daten:

AnwendungsfallWer profitiertBenötigte Daten
Wettbewerbsanalyse für HotelsHotelketten, Revenue ManagerBewertungen, Preise, Bewertungsvolumen, Ausstattung
Marktanalyse für RestaurantsRestaurantgruppen, Food-MarkenKüchenarten, Preisbereiche, Stimmungsanalyse
Trendtracking für AttraktionenReiseveranstalter, TourismusverbändeBeliebtheitsrankings, saisonale Muster
Sentiment-AnalyseForschende, DatenanalystenVollständige Rezensionstexte, Sternebewertungen, Daten
LeadgenerierungVertriebsteams, ReiseagenturenFirmennamen, Kontaktdaten, Standorte

Warum gerade Python? Drei Gründe. Erstens das Ökosystem: BeautifulSoup, Selenium, Playwright, Scrapy, httpx, pandas – Python bietet ausgereiftere Scraping- und Datenanalyse-Bibliotheken als jede andere Sprache. Zweitens nutzen Python, was mehr Community-Support, mehr StackOverflow-Antworten und aktuellere Anleitungen bedeutet. Drittens der Pipeline-Vorteil: Du kannst mit BeautifulSoup scrapen, mit pandas bereinigen, Sentiment-Analyse mit Hugging Face Transformers durchführen und Dashboards bauen – alles in einer Sprache. Kein ständiges Wechseln des Kontexts.

Drei Wege, TripAdvisor mit Python zu scrapen (im Vergleich)

Jeder Konkurrenzleitfaden pickt einen Ansatz und bleibt dabei. Das hilft nicht, wenn du die Entscheidung vor dem Coden treffen willst. Hier ist die Vergleichstabelle, die ich mir gewünscht hätte:

AnsatzGeschwindigkeitJS-UnterstützungAnti-Bot-WiderstandKomplexitätAm besten geeignet für
requests + BeautifulSoup⚡ Schnell (~120–200 Seiten/Min. roh)❌ Keine⚠️ NiedrigEinfachStatische Listenseiten, kleine Projekte
Selenium / Headless-Browser🐢 Langsam (~8–20 Seiten/Min.)✅ Vollständig⚠️ MittelMittelDynamische Inhalte, „Mehr lesen“-Klicks, Cookie-Banner
Versteckte JSON-/GraphQL-API⚡⚡ Am schnellsten (~200–600 Seiten/Min. roh)N/A✅ HöherSchwerGroße Mengen an Bewertungen/Hotel-Daten
No-Code (Thunderbit)⚡ Schnell✅ Integriert✅ IntegriertAm einfachstenNicht-Entwickler, schnelle Einzel-Exporte

Ein paar wichtige Hinweise: Diese Rohgeschwindigkeiten sind theoretisch – TripAdvisors Rate Limits (~10–15 Anfragen pro Minute und IP) begrenzen den realen Durchsatz unabhängig vom Ansatz auf ungefähr 10 Seiten/Minute pro IP. Die versteckte-JSON-Methode liefert pro Anfrage am meisten Daten, was weniger Gesamtanfragen und damit weniger Angriffsfläche für Rate Limits bedeutet. Selenium ist in Praxis-Benchmarks etwa 5x langsamer als request-basierte Ansätze, ist aber die einzige Option, wenn Buttons geklickt oder JavaScript gerendert werden muss.

Der Rest dieses Leitfadens führt dich durch alle drei Python-Methoden mit vollständigem Code. Wähle, was zu deiner Situation passt, oder kombiniere die Ansätze (ich nutze oft requests+BS4 für Listenseiten und verstecktes JSON für Detailseiten).

Ihre Python-Umgebung vorbereiten

Bevor wir starten, richten wir die Umgebung ein. Du brauchst Python 3.10+ (ich empfehle 3.12 oder 3.13 – alle wichtigen Pakete unterstützen diese Versionen ohne bekannte Probleme).

Alles auf einmal installieren:

1pip install requests beautifulsoup4 selenium httpx parsel pandas curl-cffi

Hinweise zu den Paketen:

  • requests (2.33.1) — HTTP-Anfragen, benötigt Python 3.10+
  • beautifulsoup4 (4.14.3) — HTML-Parsing
  • selenium (4.43.0) — Browser-Automatisierung, benötigt Python 3.10+
  • httpx (0.28.1) — Asynchroner HTTP-Client
  • parsel (1.11.0) — CSS-/XPath-Selektoren (leichter als BS4)
  • pandas (3.0.2) — Datenexport, benötigt Python 3.11+
  • curl_cffi (0.15.0) — TLS-Fingerprint-Imitation (wichtig zum Umgehen von Cloudflare)

ChromeDriver: Wenn du Selenium nutzt, gute Nachricht: Seit Selenium 4.6 lädt der Selenium Manager automatisch die passende ChromeDriver-Binary herunter und cached sie. Eine manuelle Installation ist nicht nötig. Die Version wird dynamisch abgeglichen, sodass du dich nicht um Chrome-Mismatch-Probleme kümmern musst.

Virtuelle Umgebung (empfohlen):

1python -m venv tripadvisor-scraper
2source tripadvisor-scraper/bin/activate  # macOS/Linux
3tripadvisor-scraper\Scripts\activate     # Windows

Ansatz 1: TripAdvisor mit Requests und BeautifulSoup scrapen

Das ist der einfachste Ansatz. Er funktioniert gut für Listenseiten (Hotelsuchergebnisse, Restaurantlisten), bei denen die benötigten Daten bereits im statischen HTML vorhanden sind. Kein Browser, kein JavaScript-Rendering, geringer Ressourcenverbrauch.

TripAdvisor-URL-Muster verstehen

TripAdvisor-URLs folgen je nach Kategorie einem vorhersehbaren Muster:

  • Hotels: https://www.tripadvisor.com/Hotels-g{locationId}-{Location_Name}-Hotels.html
  • Restaurants: https://www.tripadvisor.com/Restaurants-g{locationId}-{Location_Name}.html
  • Attraktionen: https://www.tripadvisor.com/Attractions-g{locationId}-Activities-{Location_Name}.html

Die Paginierung nutzt den Parameter oa (offset anchors), der in die URL eingefügt wird. Jede Seite zeigt 30 Ergebnisse:

  • Seite 1: Basis-URL (ohne oa-Parameter)
  • Seite 2: Hotels-g187768-oa30-Italy-Hotels.html
  • Seite 3: Hotels-g187768-oa60-Italy-Hotels.html

Für Bewertungsseiten ist der Offset-Parameter or mit Schritten von 10:

  • Seite 1: Reviews-or0-Hotel_Name.html
  • Seite 2: Reviews-or10-Hotel_Name.html

Um Bewertungen in allen Sprachen zu erhalten, hängst du ?filterLang=ALL an die URL an.

Anfragen mit realistischen Headern senden

TripAdvisor prüft Header sehr streng. Eine Anfrage mit Standard-Python-Headern wird sofort blockiert. Du musst einen echten Chrome-Browser nachahmen:

1import requests
2import time
3import random
4session = requests.Session()
5> This paragraph contains content that cannot be parsed and has been skipped.
6session.headers.update(headers)
7url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
8response = session.get(url)
9print(f"Status: {response.status_code}")
10print(f"Content length: {len(response.text)} characters")

Wichtiger Punkt: TripAdvisor validiert, ob User-Agent und Sec-CH-UA-Client-Hints zusammenpassen. Wenn du im User-Agent angibst, Chrome 135 zu sein, dein Sec-CH-UA aber Chrome 120 meldet, fällst du auf. Rotiere immer komplette Header-Sets gemeinsam, nicht einzelne Header.

Listings mit BeautifulSoup parsen

Wenn du eine erfolgreiche Antwort erhältst, extrahierst du die Daten mit BeautifulSoup. TripAdvisor nutzt data-automation- und data-test-attribute-Eigenschaften, die stabiler sind als CSS-Klassennamen, die sich häufig ändern:

1from bs4 import BeautifulSoup
2soup = BeautifulSoup(response.text, "html.parser")
3# Alle Hotelkarten finden
4cards = soup.select('div[data-test-attribute="location-results-card"]')
5hotels = []
6for card in cards:
7    # Hotelname
8    title_el = card.select_one('div[data-automation="hotel-card-title"]')
9    name = title_el.get_text(strip=True) if title_el else None
10    # Link zur Detailseite
11    link_el = card.select_one('div[data-automation="hotel-card-title"] a')
12    link = "https://www.tripadvisor.com" + link_el["href"] if link_el else None
13    # Bewertung
14    rating_el = card.select_one('[data-automation="bubbleRatingValue"]')
15    rating = rating_el.get_text(strip=True) if rating_el else None
16    # Anzahl der Rezensionen
17    review_el = card.select_one('[data-automation="bubbleReviewCount"]')
18    review_count = review_el.get_text(strip=True).replace(",", "").split()[0] if review_el else None
19> This paragraph contains content that cannot be parsed and has been skipped.
20print(f"{len(hotels)} Hotels auf dieser Seite gefunden")
21for h in hotels[:3]:
22    print(h)

Hinweis zu Selektoren: TripAdvisor verwendet obfuskiert wirkende CSS-Klassennamen (wie FGwzt, yyzcQ), die sich mit jedem Update ändern. Die data-automation- und data-test-target-Attribute sind deutlich stabiler. Bevorzuge immer Datenattribute statt Klassennamen.

Paginierung behandeln

Um mehrere Seiten zu scrapen, läufst du den Offset-Parameter mit einer kurzen, zufälligen Pause zwischen den Anfragen durch:

1import pandas as pd
2all_hotels = []
3base_url = "https://www.tripadvisor.com/Hotels-g187147-oa{offset}-Paris_Ile_de_France-Hotels.html"
4for page in range(5):  # Erste 5 Seiten
5    offset = page * 30
6    url = base_url.format(offset=offset) if page > 0 else "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
7    response = session.get(url)
8    if response.status_code != 200:
9        print(f"Seite {page + 1}: Status {response.status_code}, Abbruch.")
10        break
11    soup = BeautifulSoup(response.text, "html.parser")
12    cards = soup.select('div[data-test-attribute="location-results-card"]')
13    for card in cards:
14        title_el = card.select_one('div[data-automation="hotel-card-title"]')
15        name = title_el.get_text(strip=True) if title_el else None
16        rating_el = card.select_one('[data-automation="bubbleRatingValue"]')
17        rating = rating_el.get_text(strip=True) if rating_el else None
18        review_el = card.select_one('[data-automation="bubbleReviewCount"]')
19        review_count = review_el.get_text(strip=True).replace(",", "").split()[0] if review_el else None
20> This paragraph contains content that cannot be parsed and has been skipped.
21    print(f"Seite {page + 1}: {len(cards)} Hotels gefunden")
22    time.sleep(random.uniform(3, 7))  # Zufällige Pause zur Vermeidung von Rate Limits
23df = pd.DataFrame(all_hotels)
24print(f"\nInsgesamt gescrapte Hotels: {len(df)}")

Die time.sleep(random.uniform(3, 7)) ist wichtig. TripAdvisors Rate-Limit-Schwelle liegt bei etwa 10–15 Anfragen pro Minute und IP. Schnelleres Vorgehen löst Captchas oder 429-Fehler aus.

Grenzen dieses Ansatzes

Wo scheitert das Ganze? Der Ansatz mit requests+BS4 stößt an Grenzen, wenn:

  • TripAdvisor Inhalte per JavaScript rendert (bei manchen Suchergebnisseiten ist JS nötig)
  • Rezensionstexte hinter „Mehr lesen“-Buttons gekürzt sind
  • Anti-Bot-Maßnahmen zu JavaScript-Challenges oder CAPTCHAs eskalieren
  • du Daten brauchst, die erst nach clientseitigem Rendering erscheinen (Preise, Verfügbarkeit)

Für diese Fälle brauchst du entweder Selenium (Ansatz 2) oder die versteckte-JSON-Methode (Ansatz 3).

Ansatz 2: TripAdvisor mit Selenium scrapen (Headless-Browser)

Selenium startet einen echten Browser. Das bedeutet: JavaScript rendern, Buttons klicken, Cookie-Banner handhaben und mit dynamischen Inhalten interagieren. Der Preis: Es ist etwa und verbraucht 300–500 MB RAM pro Browserinstanz.

Selenium mit Anti-Detection-Einstellungen konfigurieren

Selenium ist standardmäßig sehr leicht erkennbar. TripAdvisors Fingerprinting erkennt es sofort. Du musst die Automatisierungsflags deaktivieren:

1from selenium import webdriver
2from selenium.webdriver.chrome.options import Options
3from selenium.webdriver.common.by import By
4from selenium.webdriver.support.ui import WebDriverWait
5from selenium.webdriver.support import expected_conditions as EC
6options = Options()
7options.add_argument("--headless=new")  # Neuer Headless-Modus (Chrome 112+)
8options.add_argument("--disable-blink-features=AutomationControlled")
9options.add_argument("--window-size=1920,1080")
10options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36")
11options.add_experimental_option("excludeSwitches", ["enable-automation"])
12options.add_experimental_option("useAutomationExtension", False)
13driver = webdriver.Chrome(options=options)
14> This paragraph contains content that cannot be parsed and has been skipped.
15**Reicht das für TripAdvisor?** Für Scraping in kleinem Umfang (unter 50 Seiten) funktioniert dieses Setup mit Residential Proxies meist. Für größere Mengen brauchst du möglicherweise `undetected-chromedriver` oder `nodriver` – TripAdvisors DataDome-Schutz analysiert über 1.000 Signale pro Anfrage, einschließlich TLS-Fingerprints, die normales Selenium nicht nachahmen kann.
16### Hotelsuchergebnisse mit Selenium scrapen
17```python
18import time
19import random
20url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
21driver.get(url)
22# Warten, bis die Hotelkarten geladen sind
23wait = WebDriverWait(driver, 15)
24wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')))
25# Cookie-Einwilligungsfenster behandeln (falls es erscheint)
26try:
27    cookie_btn = driver.find_element(By.ID, "onetrust-accept-btn-handler")
28    cookie_btn.click()
29    time.sleep(1)
30except:
31    pass  # Kein Cookie-Popup
32# Hotel-Daten extrahieren
33cards = driver.find_elements(By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')
34hotels = []
35for card in cards:
36    try:
37        name = card.find_element(By.CSS_SELECTOR, 'div[data-automation="hotel-card-title"]').text
38    except:
39        name = None
40    try:
41        rating = card.find_element(By.CSS_SELECTOR, '[data-automation="bubbleRatingValue"]').text
42    except:
43        rating = None
44    try:
45        reviews = card.find_element(By.CSS_SELECTOR, '[data-automation="bubbleReviewCount"]').text
46    except:
47        reviews = None
48> This paragraph contains content that cannot be parsed and has been skipped.
49print(f"{len(hotels)} Hotels gescrapt")
50for h in hotels[:3]:
51    print(h)

Bei mir dauerte eine einzelne Seite etwa 8 Sekunden – im Vergleich zu unter 1 Sekunde mit requests+BS4. Dieser 8-fache Unterschied summiert sich schnell, wenn du Hunderte Seiten scrapen willst.

„Mehr lesen“ aufklappen und vollständige Rezensionen scrapen

Rezensionsseiten kürzen längere Texte hinter einem „Mehr lesen“-Button. Selenium kann darauf klicken:

1review_url = "https://www.tripadvisor.com/Hotel_Review-g187147-d188726-Reviews-Le_Marais_Hotel-Paris_Ile_de_France.html"
2driver.get(review_url)
3wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-reviewid]')))
4time.sleep(2)
5# Alle „Mehr lesen“-Buttons klicken
6read_more_buttons = driver.find_elements(By.XPATH, '//button//*[contains(text(), "Read more")]/..')
7for btn in read_more_buttons:
8    try:
9        driver.execute_script("arguments[0].click();", btn)
10        time.sleep(0.3)
11    except:
12        pass
13# Rezensionen extrahieren
14review_elements = driver.find_elements(By.CSS_SELECTOR, 'div[data-reviewid]')
15reviews = []
16for rev in review_elements:
17    try:
18        title = rev.find_element(By.CSS_SELECTOR, 'div[data-test-target="review-title"]').text
19    except:
20        title = None
21    try:
22        body = rev.find_element(By.CSS_SELECTOR, 'q.IRsGHoPm span').text
23    except:
24        try:
25            body = rev.find_element(By.CSS_SELECTOR, 'p.partial_entry').text
26        except:
27            body = None
28    try:
29        rating_class = rev.find_element(By.CSS_SELECTOR, 'div[data-test-target="review-rating"] span').get_attribute("class")
30        # Bewertung ist im Klassennamen kodiert, z. B. "ui_bubble_rating bubble_50" = 5.0
31        rating_num = [c for c in rating_class.split() if "bubble_" in c][0].replace("bubble_", "")
32        rating = int(rating_num) / 10
33    except:
34        rating = None
35> This paragraph contains content that cannot be parsed and has been skipped.
36print(f"{len(reviews)} Rezensionen gescrapt")

Proxy-Rotation mit Selenium

Für dauerhaftes Scraping brauchst du Proxy-Rotation. Da selenium-wire seit Januar 2024 veraltet ist, nutzt du die integrierte Proxy-Unterstützung von Chrome:

1# Mit Proxy ohne Authentifizierung
2proxy = "http://your-proxy-address:port"
3options.add_argument(f"--proxy-server={proxy}")
4# Für Proxys mit Authentifizierung nutze eine Chrome-Erweiterung oder das BiDi-Protokoll von Selenium 4

Für rotierende Proxys erstellst du programmatisch für jeden Anfrageblock eine neue Driver-Instanz mit einem anderen Proxy. Nicht elegant, aber zuverlässig.

Ansatz 3: Die versteckte JSON-Methode (HTML-Parsing komplett überspringen)

Die meisten Anleitungen lassen diesen Ansatz weg – schade, denn er ist der schnellste und sauberste von allen. TripAdvisor bettet strukturierte Daten direkt im HTML ein – innerhalb von <script>-Tags als JavaScript-Variablen wie pageManifest und urqlCache. Wenn du dieses JSON extrahierst, erhältst du sauberere Daten (Bewertungen als Zahlen, Datumswerte im ISO-Format), brauchst weniger Requests und kein JavaScript-Rendering.

Das eingebettete JSON im Seitenquelltext finden

Der entscheidende Trick: Du kannst die Seite mit einem einfachen requests.get() abrufen und anschließend das JSON aus dem rohen HTML ziehen, ohne JavaScript zu rendern.

1import requests
2import re
3import json
4> This paragraph contains content that cannot be parsed and has been skipped.
5url = "https://www.tripadvisor.com/Hotel_Review-g188590-d194317-Reviews-NH_City_Centre_Amsterdam.html"
6response = requests.get(url, headers=headers)
7> This paragraph contains content that cannot be parsed and has been skipped.
8**So findest du den Variablennamen selbst:** Öffne irgendeine TripAdvisor-Hotelseite in Chrome, Rechtsklick → Seitenquelltext anzeigen, dann mit Strg+F nach `pageManifest`, `urqlCache` oder `aggregateRating` suchen. Die Daten sind da und warten nur darauf, geparst zu werden.
9### JSON parsen und strukturierte Daten extrahieren
10TripAdvisor bettet außerdem `application/ld+json`-Schema.org-Daten ein, die noch einfacher auszulesen sind:
11```python
12from parsel import Selector
13sel = Selector(text=response.text)
14# JSON-LD- strukturierte Daten extrahieren
15json_ld_scripts = sel.xpath("//script[@type='application/ld+json']/text()").getall()
16for script in json_ld_scripts:
17    data = json.loads(script)
18    if isinstance(data, dict) and data.get("@type") in ["Hotel", "Restaurant", "TouristAttraction"]:
19        print(f"Name: {data.get('name')}")
20        print(f"Bewertung: {data.get('aggregateRating', {}).get('ratingValue')}")
21        print(f"Anzahl der Rezensionen: {data.get('aggregateRating', {}).get('reviewCount')}")
22        print(f"Preisspanne: {data.get('priceRange')}")
23        print(f"Adresse: {data.get('address', {}).get('streetAddress')}")
24        print(f"Koordinaten: {data.get('geo', {}).get('latitude')}, {data.get('geo', {}).get('longitude')}")
25        break

Die JSON-LD-Daten sind im statischen HTML eingebettet und benötigen kein JavaScript-Rendering. Sie liefern dir Name, Gesamtbewertung, Rezensionenzahl, Adresse, Koordinaten, Preisspanne und Foto-URLs – ganz ohne ein einziges HTML-Tag zu parsen.

Für reichhaltigere Daten (einzelne Bewertungen, Bewertungsaufschlüsselungen, Ausstattungslisten) brauchst du das urqlCache-Objekt:

1# urqlCache für detaillierte Bewertungsdaten extrahieren
2cache_match = re.search(r'"urqlCache"\s*:\s*({.+?})\s*,\s*"redux"', response.text)
3if cache_match:
4    cache_data = json.loads(cache_match.group(1))
5    # Im Cache nach Bewertungsdaten suchen
6    for key, value in cache_data.items():
7        if "reviews" in str(value).lower()[:100]:
8            reviews_data = json.loads(value.get("data", "{}")) if isinstance(value, dict) else None
9            if reviews_data:
10                print(f"Cache-Eintrag mit Reviews gefunden: {key[:50]}...")
11                break

Die genauen JSON-Pfade ändern sich gelegentlich, wenn TripAdvisor das Frontend aktualisiert, aber die grundlegende Struktur – JSON-LD für Zusammenfassungsdaten, urqlCache für Detaildaten – ist seit Jahren stabil.

TripAdvisors GraphQL-API reverse-engineeren (fortgeschritten)

Für Extraktion im großen Maßstab liefern TripAdvisors GraphQL-Endpunkte strukturierte Daten direkt zurück. Das ist die schnellste Methode, erfordert aber auch den meisten Pflegeaufwand.

1import httpx
2import random
3import string
4def generate_request_id():
5    """Den Wert für den X-Requested-By-Header erzeugen"""
6    random_chars = ''.join(random.choices(string.ascii_letters + string.digits, k=180))
7    return f"TNI1625!{random_chars}"
8> This paragraph contains content that cannot be parsed and has been skipped.
9> This paragraph contains content that cannot be parsed and has been skipped.
10with httpx.Client() as client:
11    response = client.post(
12        "https://www.tripadvisor.com/data/graphql/ids",
13        json=search_payload,
14        headers=graphql_headers
15    )
16    if response.status_code == 200:
17        results = response.json()
18        print(json.dumps(results, indent=2)[:1000])
19    else:
20        print(f"GraphQL-Anfrage fehlgeschlagen: {response.status_code}")

Zum Abrufen von Bewertungen per GraphQL:

1review_payload = [{
2    "variables": {
3        "locationId": 194317,  # NH City Centre Amsterdam
4        "offset": 0,
5        "limit": 20,
6        "filters": {},
7        "sortType": None,
8        "sortBy": "date",
9        "language": "en",
10        "doMachineTranslation": False,
11        "photosPerReviewLimit": 3
12    },
13    "extensions": {
14        "preRegisteredQueryId": "ef1a9f94012220d3"
15    }
16}]
17with httpx.Client() as client:
18    response = client.post(
19        "https://www.tripadvisor.com/data/graphql/ids",
20        json=review_payload,
21        headers=graphql_headers
22    )
23    if response.status_code == 200:
24        data = response.json()
25        reviews = data[0]["data"]["locations"][0]["reviewListPage"]["reviews"]
26        total = data[0]["data"]["locations"][0]["reviewListPage"]["totalCount"]
27        print(f"Gesamtzahl der Bewertungen: {total}")
28        for r in reviews[:3]:
29            print(f"  [{r['rating']}/5] {r['title']} - {r['createdDate']}")

Wichtiger Hinweis: Die preRegisteredQueryId-Werte (wie 84b17ed122fbdbd4 für die Suche und ef1a9f94012220d3 für Bewertungen) können nach TripAdvisor-Rollouts brechen. Dann schlagen die Anfragen stillschweigend fehl. Du musst die Query-IDs neu ermitteln, indem du die Netzwerkaufrufe in den Browser-DevTools beobachtest.

Warum diese Methode den Proxy-Bedarf reduziert

Die Rechnung ist einfach. Mit requests+BS4 brauchst du für 100 Hotel-Detailseiten 100 Anfragen. Mit der versteckten-JSON-Methode liefert jede Anfrage alle Daten einer einzelnen Seitenladung – ohne zusätzliche Requests für aufgeklappte Bewertungen oder dynamisch nachgeladene Inhalte. Mit GraphQL kann ein einzelner API-Call 20 Bewertungen auf einmal liefern. Weniger Anfragen = weniger Rate-Limit-Risiko = weniger Bedarf an Proxy-Rotation. Für kleine bis mittlere Projekte (unter 1.000 Seiten) brauchst du mit vernünftigen Pausen eventuell gar keine Proxys.

Hotels, Restaurants und Attraktionen mit einem wiederverwendbaren Skript scrapen

Vier von fünf Konkurrenzleitfäden behandeln nur Hotels. TripAdvisor hat aber drei zentrale Inhaltskategorien, und die URL-Muster sowie Datenfelder unterscheiden sich. So baust du eine Funktion, die alle drei abdeckt.

Verfügbare Datenfelder pro Kategorie

FeldHotelsRestaurantsAttraktionen
Name
Bewertung
Anzahl der Rezensionen
Preis/PreisspanneManchmal
Adresse
Küchenart
Dauer/Tourtyp
Ausstattung
Koordinaten

Eine wiederverwendbare scrape_tripadvisor()-Funktion bauen

1import requests
2from bs4 import BeautifulSoup
3import pandas as pd
4import time
5import random
6import re
7import json
8def scrape_tripadvisor(category, location_id, location_name, num_pages=3):
9    """
10    TripAdvisor-Listings für Hotels, Restaurants oder Attraktionen scrapen.
11> This paragraph contains content that cannot be parsed and has been skipped.
12> This paragraph contains content that cannot be parsed and has been skipped.
13> This paragraph contains content that cannot be parsed and has been skipped.
14    session = requests.Session()
15    session.headers.update(headers)
16    all_items = []
17    for page in range(num_pages):
18        offset = page * 30
19        if page == 0:
20            url = first_page_patterns[category].format(geo=location_id, name=location_name)
21        else:
22            url = url_patterns[category].format(geo=location_id, offset=offset, name=location_name)
23        response = session.get(url)
24        if response.status_code != 200:
25            print(f"  Seite {page + 1}: Status {response.status_code}, Abbruch.")
26            break
27        soup = BeautifulSoup(response.text, "html.parser")
28        cards = soup.select('div[data-test-attribute="location-results-card"]')
29> This paragraph contains content that cannot be parsed and has been skipped.
30        print(f"  Seite {page + 1}: {len(cards)} Einträge gefunden")
31        time.sleep(random.uniform(3, 7))
32    return pd.DataFrame(all_items)
33# Anwendungsbeispiele
34print("=== Hotels in Paris ===")
35hotels_df = scrape_tripadvisor("hotels", "187147", "Paris_Ile_de_France", num_pages=2)
36print(hotels_df.head())
37print("\n=== Restaurants in Rom ===")
38restaurants_df = scrape_tripadvisor("restaurants", "187791", "Rome_Lazio", num_pages=2)
39print(restaurants_df.head())
40print("\n=== Attraktionen in Barcelona ===")
41attractions_df = scrape_tripadvisor("attractions", "187497", "Barcelona_Catalonia", num_pages=2)
42print(attractions_df.head())

Eine Funktion, drei Kategorien, kein doppelter Code. Wenn TripAdvisor einen Selektor ändert, passt du ihn an nur einer Stelle an.

Was tun, wenn TripAdvisor dich blockiert? Anti-Bot-Fehlerbehebung

Das ist der Abschnitt, den ich am dringendsten gebraucht hätte, als ich mit TripAdvisor angefangen habe – und genau der, den kein Konkurrenzleitfaden strukturiert liefert. TripAdvisor nutzt DataDome (analysiert pro Tag) zusammen mit Cloudflare WAF. Hier ist eine Diagnosetabelle für die häufigsten Fehlerbilder:

This paragraph contains content that cannot be parsed and has been skipped.

Retry-Logik mit exponentiellem Backoff

Kein anderer Artikel zeigt diesen Code tatsächlich. Hier ist eine wiederverwendbare Retry-Funktion:

1import time
2import random
3import requests
4def fetch_with_retry(session, url, max_retries=4, base_delay=2, max_delay=60):
5    """
6    Eine URL mit exponentiellem Backoff und Jitter abrufen.
7    Rotiert bei jedem Retry den User-Agent.
8    """
9    user_agents = [
10        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
11        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
12        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
13        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
14    ]
15    for attempt in range(max_retries):
16        # User-Agent beim erneuten Versuch rotieren
17        if attempt &gt; 0:
18            session.headers["User-Agent"] = random.choice(user_agents)
19        try:
20            response = session.get(url, timeout=30)
21            if response.status_code == 200:
22                return response
23            if response.status_code == 429:
24                # Retry-After-Header beachten, falls vorhanden
25                retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
26                print(f"  Rate limit (429). Warte {retry_after}s...")
27                time.sleep(retry_after)
28                continue
29> This paragraph contains content that cannot be parsed and has been skipped.
30            # Andere Fehlercodes – nicht erneut versuchen
31            print(f"  Unerwarteter Status {response.status_code} für {url}")
32            return response
33> This paragraph contains content that cannot be parsed and has been skipped.
34    print(f"  Alle {max_retries} Versuche für {url} ausgeschöpft")
35    return None

Header, Proxys und Sessions rotieren

Für dauerhaftes Scraping solltest du einen Pool aus Header-Sets pflegen und sie gemeinsam rotieren:

1import random
2> This paragraph contains content that cannot be parsed and has been skipped.
3PROXY_LIST = [
4    "http://user:pass@residential-proxy-1:port",
5    "http://user:pass@residential-proxy-2:port",
6    # Weitere Residential Proxies hinzufügen
7]
8def get_rotated_session():
9    """Eine neue Session mit rotierten Headern und Proxy erstellen."""
10    session = requests.Session()
11> This paragraph contains content that cannot be parsed and has been skipped.
12> This paragraph contains content that cannot be parsed and has been skipped.
13    return session

Der Proxy-Typ ist wichtig. Datacenter-Proxys werden von TripAdvisor fast sofort blockiert (HTTP 1020 Access Denied). Residential Proxies sind für dauerhaftes Scraping praktisch Pflicht – sie laufen über Consumer-Internetanschlüsse und sind für die Plattform kaum von echten Nutzern zu unterscheiden. Rechne je nach Anbieter mit 2,50 bis 8,40 US-Dollar pro GB.

Gescrapte TripAdvisor-Daten exportieren und speichern

Sobald du die Daten hast, ist der Export in ein nutzbares Format unkompliziert.

CSV-Export (am häufigsten)

1import pandas as pd
2df = pd.DataFrame(all_hotels)
3df.to_csv("tripadvisor_hotels_paris.csv", index=False, encoding="utf-8-sig")
4print(f"{len(df)} Zeilen nach CSV exportiert")

Die Angabe encoding='utf-8-sig' ist wichtig – sie sorgt dafür, dass Excel nicht-lateinische Zeichen korrekt anzeigt (französische Akzente, chinesische Schriftzeichen usw.), wenn die CSV geöffnet wird.

JSON-Export (für verschachtelte Daten)

Wenn Rezensionen unter Hotels verschachtelt sind, bewahrt JSON die Hierarchie:

1# Hierarchische Struktur
2hotel_data = {
3    "property_id": "d194317",
4    "name": "NH City Centre Amsterdam",
5    "rating": 4.0,
6    "reviews": [
7        {"title": "Tolle Lage", "rating": 5, "date": "2025-03-15", "text": "..."},
8        {"title": "Durchschnittlicher Aufenthalt", "rating": 3, "date": "2025-03-10", "text": "..."},
9    ]
10}
11# Für flache Analysen json_normalize verwenden
12flat_reviews = pd.json_normalize(
13    hotel_data,
14    record_path="reviews",
15    meta=["property_id", "name"]
16)
17flat_reviews.to_csv("reviews_flat.csv", index=False)

Zwei-Dateien-Ansatz für relationale Daten

Für große Datensätze verwende ich zwei CSV-Dateien:

  • hotels.csv — eine Zeile pro Unterkunft (flach)
  • reviews.csv — eine Zeile pro Rezension, mit property_id als Fremdschlüssel

Das macht es einfach, in pandas zu verknüpfen, in eine Datenbank zu laden oder in BI-Tools zu importieren.

Wenn du dich nicht mit diesem Export-Logik-Kram beschäftigen möchtest, kannst du mit Thunderbit gescrapte Daten direkt nach – kostenlos und ohne Code. Praktisch, wenn du Ergebnisse mit nicht-technischen Teamkollegen teilen musst.

Tipps für verantwortungsvolles und effizientes TripAdvisor-Scraping

Verantwortungsvolles Scraping in sechs Punkten:

  • robots.txt prüfen: TripAdvisors robots.txt blockiert KI-Trainings-Bots (GPTBot, ClaudeBot usw.) vollständig. Standard-Crawler unterliegen selektiven Pfadbeschränkungen. Prüfe sie unter tripadvisor.com/robots.txt.
  • Pausen einbauen: 3–7 Sekunden zwischen Anfragen sind ein sicherer Bereich. Schneller als 10–15 Anfragen pro Minute und IP zu arbeiten löst Rate Limits aus.
  • Nur öffentliche Daten scrapen. Logge dich nicht ein, um eingeschränkte Inhalte zu lesen.
  • Daten sicher speichern und bei personenbezogenen Informationen (z. B. Rezensentennamen) GDPR/CCPA beachten.
  • Die offizielle TripAdvisor-API in Betracht ziehen, wenn du Daten im kommerziellen Maßstab brauchst. Das bietet Zugriff auf Unternehmensdaten sowie bis zu 5 Bewertungen und 5 Fotos pro Ort – begrenzt, aber legal und stabil.
  • Den rechtlichen Kontext kennen: Das hat ToS-basierte Scraping-Verbote in der EU verschärft. TripAdvisors Nutzungsbedingungen untersagen Scraping ausdrücklich. Scrape verantwortungsvoll und auf eigenes Risiko.

Fazit

Damit hast du das vollständige Bild.

  • Requests + BeautifulSoup ist der einfachste Weg. Er funktioniert für statische Listenseiten, braucht wenig Setup und ist schnell. Starte hier, wenn du weniger als 100 Seiten scrapen und keine JavaScript-gerenderten Inhalte brauchst.
  • Selenium deckt alles ab, was requests nicht kann: dynamische Inhalte, „Mehr lesen“-Buttons, Cookie-Banner. Es ist fünfmal langsamer und ressourcenintensiv, aber die einzige Option, wenn du mit der Seite interagieren musst.
  • Verstecktes JSON / GraphQL ist der sauberste und schnellste Ansatz. Du bekommst strukturierte Daten ohne HTML-Parsing, reduzierst die Zahl der Anfragen und damit den Proxy-Bedarf, und erhältst Daten in direkt analysierbaren Formaten. Dafür braucht es anfangs mehr Reverse Engineering und gelegentlich Pflege, wenn TripAdvisor seine Datenstruktur ändert.

Die wiederverwendbare Funktion scrape_tripadvisor() deckt Hotels, Restaurants und Attraktionen ab. Du solltest dafür kein zweites Tutorial brauchen.

Und wenn du mitten im Tutorial merkst, dass Coden doch nicht dein Ding ist – oder du einfach bis heute Abend 50 Hotels in einer Tabelle brauchst – kann die das in zwei Klicks erledigen: mit KI-gestützter Felderkennung, automatischer Paginierung und kostenlosem Export nach Excel oder Google Sheets. Kein Python nötig.

Wenn du tiefer einsteigen willst, haben wir weitere Anleitungen auf dem und unserem .

FAQs

1. Ist es legal, TripAdvisor zu scrapen?

TripAdvisors Nutzungsbedingungen verbieten Scraping ausdrücklich. Gerichte haben jedoch in der Regel entschieden, dass das Scrapen öffentlich zugänglicher Daten (also nicht hinter einem Login) in den USA nicht gegen den Computer Fraud and Abuse Act verstößt. In der EU hat das Ryanair-Urteil von 2025 ToS-basierte Einschränkungen jedoch verschärft. Scrape nur öffentliche Daten, respektiere robots.txt, veröffentliche keine urheberrechtlich geschützten Inhalte erneut und hole rechtliche Beratung ein, wenn du die Daten kommerziell nutzen willst.

2. Kann ich TripAdvisor ohne Python scrapen?

Ja. No-Code-Tools wie können TripAdvisor direkt im Browser scrapen – mit KI-gestützter Felderkennung und automatischer Paginierung. Du kannst auch Browser-Erweiterungen, Google-Sheets-Add-ons oder kommerzielle Scraping-APIs verwenden. Python bietet die meiste Kontrolle und Flexibilität, ist aber nicht die einzige Option.

3. Wie verhindere ich, beim Scrapen von TripAdvisor blockiert zu werden?

Die wichtigsten Maßnahmen: realistische und konsistente Header verwenden (vor allem User-Agent und Sec-CH-UA), Residential Proxies rotieren (Datacenter-IPs werden sofort blockiert), zufällige Pausen von 3–7 Sekunden zwischen Anfragen einbauen, die versteckte-JSON-Methode nutzen, um die Gesamtzahl der Anfragen zu minimieren, Retry-Logik mit exponentiellem Backoff implementieren und Sessions durch einen Startseitenaufruf „aufwärmen“, bevor tiefer liegende Seiten gecrawlt werden.

4. Welche Daten kann ich von TripAdvisor scrapen?

Hotels, Restaurants und Attraktionen – einschließlich Namen, Bewertungen, Rezensionenzahlen, Preisspannen, Adressen, Koordinaten, Ausstattungen (Hotels), Küchenarten (Restaurants), Tourdauern (Attraktionen) sowie vollständige Rezensionstexte mit einzelnen Bewertungen und Daten. Die Ansätze mit verstecktem JSON und GraphQL liefern pro Anfrage die reichhaltigsten Daten.

5. Wie viele Seiten kann ich pro Tag von TripAdvisor scrapen?

Mit einer einzelnen IP und sinnvollen Pausen: ungefähr 600–1.000 Seiten pro Tag. Mit 20 rotierenden Residential Proxies: etwa 200.000–300.000 Seiten pro Tag bei request-basierten Ansätzen. Selenium ist langsamer – rechne mit 8.000–12.000 Seiten pro Tag und Proxy. Der Ansatz über verstecktes JSON/GraphQL liefert pro Anfrage die meisten Daten, daher brauchst du oft deutlich weniger Seiten, um dieselbe Informationsmenge zu erhalten.

Mehr erfahren

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

Teste Thunderbit

Leads und andere Daten in nur 2 Klicks scrapen. Unterstützt durch KI.

Thunderbit holen Es ist kostenlos
Daten mit KI extrahieren
Daten einfach zu Google Sheets, Airtable oder Notion übertragen
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week