Für ein Projekt brauchte ich neulich die Bewertungen und Rezensionszahlen von rund 200 Unterkünften aus drei europäischen Städten – Quelle: TripAdvisor. Mein erster Versuch, ein schlichtes requests.get() mit Standard-Headern, kassierte bei jeder einzelnen Anfrage ein sauberes 403 Forbidden. Kein einziges brauchbares Byte.
TripAdvisor ist eine der wertvollsten öffentlichen Datenquellen der Reisebranche: über 1 Milliarde Bewertungen, mehr als 8 Millionen Unternehmenseinträge und rund 460 Millionen einzigartige Besucher pro Monat. Die Plattform beeinflusst jährlich Reiseausgaben von über $60 Milliarden. Nur: An diese Daten programmatisch heranzukommen, ist eine andere Hausnummer. TripAdvisor fährt DataDome zur Bot-Erkennung auf, dazu Cloudflare WAF, TLS-Fingerprinting und JavaScript-Challenges – eine mehrschichtige Verteidigung, die die meisten naiven Scraping-Versuche schon im Ansatz abwürgt. Dieser Leitfaden ist die Ressource, die mir damals gefehlt hat: ein direkter Vergleich von drei Python-Ansätzen (plus einer 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. Ob Python-Einsteiger oder erfahrener Entwickler – das erspart dir jede Menge unnötige 403er.
Du willst keinen Code schreiben? TripAdvisor einfach scrapen
Sagen wir es ehrlich: Viele, die nach „TripAdvisor mit Python scrapen“ suchen, wollen eigentlich gar nicht programmieren. Sie wollen einfach Daten – Hotelnamen, Bewertungen, Rezensionszahlen, Preise – schnell in einer Tabelle haben. Trifft das auf dich zu, gibt es einen deutlich kürzeren Weg.
Thunderbit ist eine KI-gestützte Chrome-Erweiterung aus unserer Entwicklung. Sie liest jede TripAdvisor-Seite und schlägt automatisch passende Spalten zum Extrahieren vor. Der Ablauf umfasst wirklich nur zwei Klicks:
- Eine TripAdvisor-Listenseite öffnen (z. B. Suchergebnisse für „Hotels in Paris“).
- 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.
- Auf „Scrape“ klicken. Thunderbit extrahiert die Daten aus allen Einträgen auf der Seite – und übernimmt bei Bedarf die Paginierung automatisch.
- Exportieren nach Excel, Google Sheets, Airtable oder Notion. Exporte sind in jedem Tarif kostenlos.
Thunderbit läuft ohne Konfiguration für Hotels, Restaurants und Sehenswürdigkeiten – die KI passt sich dem Seiteninhalt an. Bei mehrseitigen Ergebnissen erkennt es automatisch „Weiter“-Buttons und Endlos-Scrollen. Und weil es direkt in deinem echten Chrome-Browser arbeitet, nutzt es deine Session-Cookies und den Browser-Fingerprint – das verschafft einen natürlichen Vorsprung gegenüber der Bot-Erkennung.
Du kannst es mit der Thunderbit Chrome-Erweiterung 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 Studie der Cornell University zeigte, dass ein Anstieg des Global Review Index eines Hotels um 1 Punkt den durchschnittlichen Tagespreis um 0,89 % und den Revenue per Available Room um 1,42 % anhebt. Eine separate ScienceDirect-Studie fand heraus, dass ein externer Anstieg der TripAdvisor-Bewertung um 1 Stern einem durchschnittlichen Hotel zusätzlichen Jahresumsatz von 55.000 bis 75.000 US-Dollar bringt. Bewertungen sind also keine bloße Eitelkeitskennzahl – sie treiben den Umsatz.
So setzen verschiedene Teams TripAdvisor-Daten ein:
| Anwendungsfall | Wer profitiert | Benötigte Daten |
|---|---|---|
| Wettbewerbsanalyse für Hotels | Hotelketten, Revenue Manager | Bewertungen, Preise, Bewertungsvolumen, Ausstattung |
| Marktanalyse für Restaurants | Restaurantgruppen, Food-Marken | Küchenarten, Preisbereiche, Stimmungsanalyse |
| Trendtracking für Attraktionen | Reiseveranstalter, Tourismusverbände | Beliebtheitsrankings, saisonale Muster |
| Sentiment-Analyse | Forschende, Datenanalysten | Vollständige Rezensionstexte, Sternebewertungen, Daten |
| Leadgenerierung | Vertriebsteams, Reiseagenturen | Firmennamen, Kontaktdaten, Standorte |
Warum gerade Python? Drei Gründe. Erstens das Ökosystem: BeautifulSoup, Selenium, Playwright, Scrapy, httpx, pandas – bei Scraping- und Datenanalyse-Bibliotheken bietet Python die ausgereifteste Auswahl überhaupt. Zweitens arbeiten 71,7 % der Web-Scraping-Entwickler mit Python, was mehr Community-Support, mehr StackOverflow-Antworten und aktuellere Anleitungen bedeutet. Drittens der Pipeline-Vorteil: scrapen mit BeautifulSoup, bereinigen mit pandas, Sentiment-Analyse mit Hugging Face Transformers, Dashboards bauen – alles in einer Sprache, ohne ständigen Kontextwechsel.
Drei Wege, TripAdvisor mit Python zu scrapen (im Vergleich)
Jeder Konkurrenzleitfaden pickt sich einen Ansatz heraus und bleibt dabei. Das hilft dir nicht, wenn du die Entscheidung vor dem Coden treffen willst. Hier ist die Vergleichstabelle, die mir damals gefehlt hat:
| Ansatz | Geschwindigkeit | JS-Unterstützung | Anti-Bot-Widerstand | Komplexität | Am besten geeignet für |
|---|---|---|---|---|---|
requests + BeautifulSoup | ⚡ Schnell (~120–200 Seiten/Min. roh) | ❌ Keine | ⚠️ Niedrig | Einfach | Statische Listenseiten, kleine Projekte |
| Selenium / Headless-Browser | 🐢 Langsam (~8–20 Seiten/Min.) | ✅ Vollständig | ⚠️ Mittel | Mittel | Dynamische Inhalte, „Mehr lesen“-Klicks, Cookie-Banner |
| Versteckte JSON-/GraphQL-API | ⚡⚡ Am schnellsten (~200–600 Seiten/Min. roh) | N/A | ✅ Höher | Schwer | Große Mengen an Bewertungen/Hotel-Daten |
| No-Code (Thunderbit) | ⚡ Schnell | ✅ Integriert | ✅ Integriert | Am einfachsten | Nicht-Entwickler, schnelle Einzel-Exporte |
Ein paar Dinge dazu: Diese Rohgeschwindigkeiten sind theoretisch – TripAdvisors Rate Limits (~10–15 Anfragen pro Minute und IP) drücken den realen Durchsatz unabhängig vom Ansatz auf etwa 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, bleibt 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 nehme oft requests+BS4 für Listenseiten und verstecktes JSON für Detailseiten).
Deine Python-Umgebung vorbereiten
Bevor es losgeht, 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:
pip 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-Parsingselenium(4.43.0) — Browser-Automatisierung, benötigt Python 3.10+httpx(0.28.1) — Asynchroner HTTP-Clientparsel(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: Nutzt du Selenium, gute Nachricht: Seit Selenium 4.6 lädt der Selenium Manager die passende ChromeDriver-Binary automatisch herunter und cached sie. Eine manuelle Installation entfällt. Die Version wird dynamisch abgeglichen, sodass dich Chrome-Mismatch-Probleme nicht weiter beschäftigen.
Virtuelle Umgebung (empfohlen):
python -m venv tripadvisor-scraper
source tripadvisor-scraper/bin/activate # macOS/Linux
tripadvisor-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 stecken. 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 läuft über 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 heißt 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 nachbilden:
import requests
import time
import random
session = requests.Session()
headers = {
"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",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Referer": "https://www.tripadvisor.com/",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-CH-UA": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"Sec-CH-UA-Mobile": "?0",
"Sec-CH-UA-Platform": '"Windows"',
}
session.headers.update(headers)
url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
response = session.get(url)
print(f"Status: {response.status_code}")
print(f"Content length: {len(response.text)} characters")
Wichtig: TripAdvisor prüft, ob User-Agent und die Sec-CH-UA-Client-Hints zusammenpassen. Behauptest du im User-Agent, Chrome 135 zu sein, meldet dein Sec-CH-UA aber Chrome 120, fliegst du auf. Rotiere immer komplette Header-Sets gemeinsam, nie einzelne Header.
Listings mit BeautifulSoup parsen
Kommt eine erfolgreiche Antwort zurück, extrahierst du die Daten mit BeautifulSoup. TripAdvisor setzt auf data-automation- und data-test-attribute-Eigenschaften, die stabiler sind als die häufig wechselnden CSS-Klassennamen:
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, "html.parser")
# Alle Hotelkarten finden
cards = soup.select('div[data-test-attribute="location-results-card"]')
hotels = []
for card in cards:
# Hotelname
title_el = card.select_one('div[data-automation="hotel-card-title"]')
name = title_el.get_text(strip=True) if title_el else None
# Link zur Detailseite
link_el = card.select_one('div[data-automation="hotel-card-title"] a')
link = "https://www.tripadvisor.com" + link_el["href"] if link_el else None
# Bewertung
rating_el = card.select_one('[data-automation="bubbleRatingValue"]')
rating = rating_el.get_text(strip=True) if rating_el else None
# Anzahl der Rezensionen
review_el = card.select_one('[data-automation="bubbleReviewCount"]')
review_count = review_el.get_text(strip=True).replace(",", "").split()[0] if review_el else None
hotels.append({
"name": name,
"rating": rating,
"review_count": review_count,
"url": link,
})
print(f"{len(hotels)} Hotels auf dieser Seite gefunden")
for h in hotels[:3]:
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:
import pandas as pd
all_hotels = []
base_url = "https://www.tripadvisor.com/Hotels-g187147-oa{offset}-Paris_Ile_de_France-Hotels.html"
for page in range(5): # Erste 5 Seiten
offset = page * 30
url = base_url.format(offset=offset) if page > 0 else "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
response = session.get(url)
if response.status_code != 200:
print(f"Seite {page + 1}: Status {response.status_code}, Abbruch.")
break
soup = BeautifulSoup(response.text, "html.parser")
cards = soup.select('div[data-test-attribute="location-results-card"]')
for card in cards:
title_el = card.select_one('div[data-automation="hotel-card-title"]')
name = title_el.get_text(strip=True) if title_el else None
rating_el = card.select_one('[data-automation="bubbleRatingValue"]')
rating = rating_el.get_text(strip=True) if rating_el else None
review_el = card.select_one('[data-automation="bubbleReviewCount"]')
review_count = review_el.get_text(strip=True).replace(",", "").split()[0] if review_el else None
all_hotels.append({"name": name, "rating": rating, "review_count": review_count})
print(f"Seite {page + 1}: {len(cards)} Hotels gefunden")
time.sleep(random.uniform(3, 7)) # Zufällige Pause zur Vermeidung von Rate Limits
df = pd.DataFrame(all_hotels)
print(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. Schneller vorzugehen löst Captchas oder 429-Fehler aus.
Grenzen dieses Ansatzes
Wo läuft das Ganze auf? Der Ansatz mit requests+BS4 stößt an Grenzen, sobald:
- 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 heißt: JavaScript rendern, Buttons klicken, Cookie-Banner handhaben und mit dynamischen Inhalten interagieren. Der Preis dafür: Es ist etwa 5x langsamer und schluckt 300–500 MB RAM pro Browserinstanz.
Selenium mit Anti-Detection-Einstellungen konfigurieren
Selenium ist von Haus aus sehr leicht zu erkennen. TripAdvisors Fingerprinting durchschaut es sofort. Du musst die Automatisierungsflags deaktivieren:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.add_argument("--headless=new") # Neuer Headless-Modus (Chrome 112+)
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("--window-size=1920,1080")
options.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")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
driver = webdriver.Chrome(options=options)
# webdriver-Property aus navigator entfernen
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
})
Reicht das für TripAdvisor? Für Scraping in kleinem Umfang (unter 50 Seiten) klappt dieses Setup mit Residential Proxies meist. Bei größeren Mengen brauchst du womöglich undetected-chromedriver oder nodriver – TripAdvisors DataDome-Schutz wertet über 1.000 Signale pro Anfrage aus, darunter TLS-Fingerprints, die normales Selenium nicht nachahmen kann.
Hotelsuchergebnisse mit Selenium scrapen
import time
import random
url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
driver.get(url)
# Warten, bis die Hotelkarten geladen sind
wait = WebDriverWait(driver, 15)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')))
# Cookie-Einwilligungsfenster behandeln (falls es erscheint)
try:
cookie_btn = driver.find_element(By.ID, "onetrust-accept-btn-handler")
cookie_btn.click()
time.sleep(1)
except:
pass # Kein Cookie-Popup
# Hotel-Daten extrahieren
cards = driver.find_elements(By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')
hotels = []
for card in cards:
try:
name = card.find_element(By.CSS_SELECTOR, 'div[data-automation="hotel-card-title"]').text
except:
name = None
try:
rating = card.find_element(By.CSS_SELECTOR, '[data-automation="bubbleRatingValue"]').text
except:
rating = None
try:
reviews = card.find_element(By.CSS_SELECTOR, '[data-automation="bubbleReviewCount"]').text
except:
reviews = None
hotels.append({"name": name, "rating": rating, "review_count": reviews})
print(f"{len(hotels)} Hotels gescrapt")
for h in hotels[:3]:
print(h)
Bei mir dauerte eine einzelne Seite etwa 8 Sekunden – gegenüber unter 1 Sekunde mit requests+BS4. Dieser 8-fache Unterschied summiert sich rasant, sobald 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:
review_url = "https://www.tripadvisor.com/Hotel_Review-g187147-d188726-Reviews-Le_Marais_Hotel-Paris_Ile_de_France.html"
driver.get(review_url)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-reviewid]')))
time.sleep(2)
# Alle „Mehr lesen“-Buttons klicken
read_more_buttons = driver.find_elements(By.XPATH, '//button//*[contains(text(), "Read more")]/..')
for btn in read_more_buttons:
try:
driver.execute_script("arguments[0].click();", btn)
time.sleep(0.3)
except:
pass
# Rezensionen extrahieren
review_elements = driver.find_elements(By.CSS_SELECTOR, 'div[data-reviewid]')
reviews = []
for rev in review_elements:
try:
title = rev.find_element(By.CSS_SELECTOR, 'div[data-test-target="review-title"]').text
except:
title = None
try:
body = rev.find_element(By.CSS_SELECTOR, 'q.IRsGHoPm span').text
except:
try:
body = rev.find_element(By.CSS_SELECTOR, 'p.partial_entry').text
except:
body = None
try:
rating_class = rev.find_element(By.CSS_SELECTOR, 'div[data-test-target="review-rating"] span').get_attribute("class")
# Bewertung ist im Klassennamen kodiert, z. B. "ui_bubble_rating bubble_50" = 5.0
rating_num = [c for c in rating_class.split() if "bubble_" in c][0].replace("bubble_", "")
rating = int(rating_num) / 10
except:
rating = None
reviews.append({"title": title, "body": body, "rating": rating})
print(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 Chromes integrierte Proxy-Unterstützung:
# Mit Proxy ohne Authentifizierung
proxy = "http://your-proxy-address:port"
options.add_argument(f"--proxy-server={proxy}")
# Für Proxys mit Authentifizierung nutze eine Chrome-Erweiterung oder das BiDi-Protokoll von Selenium 4
Für rotierende Proxys erzeugst du programmatisch pro 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 aus – schade, denn er ist der schnellste und sauberste von allen. TripAdvisor bettet strukturierte Daten direkt ins HTML ein – innerhalb von <script>-Tags als JavaScript-Variablen wie pageManifest und urqlCache. Extrahierst du dieses JSON, bekommst 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 Kniff: Du rufst die Seite mit einem einfachen requests.get() ab und ziehst anschließend das JSON aus dem rohen HTML, ohne JavaScript zu rendern.
import requests
import re
import json
headers = {
"User-Agent": "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",
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Referer": "https://www.tripadvisor.com/",
"Sec-CH-UA": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"Sec-CH-UA-Mobile": "?0",
"Sec-CH-UA-Platform": '"macOS"',
}
url = "https://www.tripadvisor.com/Hotel_Review-g188590-d194317-Reviews-NH_City_Centre_Amsterdam.html"
response = requests.get(url, headers=headers)
# JSON-Blob pageManifest extrahieren
match = re.search(r"pageManifest:({.+?})};", response.text)
if match:
page_data = json.loads(match.group(1))
print("pageManifest-Daten gefunden")
print(f"Schlüssel: {list(page_data.keys())[:10]}")
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.
JSON parsen und strukturierte Daten extrahieren
TripAdvisor bettet außerdem application/ld+json-Schema.org-Daten ein, die noch einfacher auszulesen sind:
from parsel import Selector
sel = Selector(text=response.text)
# JSON-LD- strukturierte Daten extrahieren
json_ld_scripts = sel.xpath("//script[@type='application/ld+json']/text()").getall()
for script in json_ld_scripts:
data = json.loads(script)
if isinstance(data, dict) and data.get("@type") in ["Hotel", "Restaurant", "TouristAttraction"]:
print(f"Name: {data.get('name')}")
print(f"Bewertung: {data.get('aggregateRating', {}).get('ratingValue')}")
print(f"Anzahl der Rezensionen: {data.get('aggregateRating', {}).get('reviewCount')}")
print(f"Preisspanne: {data.get('priceRange')}")
print(f"Adresse: {data.get('address', {}).get('streetAddress')}")
print(f"Koordinaten: {data.get('geo', {}).get('latitude')}, {data.get('geo', {}).get('longitude')}")
break
Die JSON-LD-Daten stecken im statischen HTML und brauchen kein JavaScript-Rendering. Sie liefern dir Name, Gesamtbewertung, Rezensionszahl, 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:
# urqlCache für detaillierte Bewertungsdaten extrahieren
cache_match = re.search(r'"urqlCache"\s*:\s*({.+?})\s*,\s*"redux"', response.text)
if cache_match:
cache_data = json.loads(cache_match.group(1))
# Im Cache nach Bewertungsdaten suchen
for key, value in cache_data.items():
if "reviews" in str(value).lower()[:100]:
reviews_data = json.loads(value.get("data", "{}")) if isinstance(value, dict) else None
if reviews_data:
print(f"Cache-Eintrag mit Reviews gefunden: {key[:50]}...")
break
Die genauen JSON-Pfade verschieben sich gelegentlich, wenn TripAdvisor das Frontend aktualisiert, aber die Grundstruktur – 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 geben TripAdvisors GraphQL-Endpunkte strukturierte Daten direkt zurück. Das ist die schnellste Methode, fordert aber auch den meisten Pflegeaufwand.
import httpx
import random
import string
def generate_request_id():
"""Den Wert für den X-Requested-By-Header erzeugen"""
random_chars = ''.join(random.choices(string.ascii_letters + string.digits, k=180))
return f"TNI1625!{random_chars}"
# Hotels in Paris suchen
search_payload = [{
"variables": {
"request": {
"query": "hotels in Paris",
"limit": 10,
"scope": "WORLDWIDE",
"locale": "en-US",
"scopeGeoId": 1,
"searchCenter": None,
"types": ["LOCATION", "QUERY_SUGGESTION", "RESCUE_RESULT"],
"locationTypes": ["GEO", "AIRPORT", "ACCOMMODATION", "ATTRACTION", "EATERY", "NEIGHBORHOOD"]
}
},
"extensions": {
"preRegisteredQueryId": "84b17ed122fbdbd4"
}
}]
graphql_headers = {
"Content-Type": "application/json",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.9",
"Origin": "https://www.tripadvisor.com",
"Referer": "https://www.tripadvisor.com/Hotels",
"X-Requested-By": generate_request_id(),
"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",
}
with httpx.Client() as client:
response = client.post(
"https://www.tripadvisor.com/data/graphql/ids",
json=search_payload,
headers=graphql_headers
)
if response.status_code == 200:
results = response.json()
print(json.dumps(results, indent=2)[:1000])
else:
print(f"GraphQL-Anfrage fehlgeschlagen: {response.status_code}")
Zum Abrufen von Bewertungen per GraphQL:
review_payload = [{
"variables": {
"locationId": 194317, # NH City Centre Amsterdam
"offset": 0,
"limit": 20,
"filters": {},
"sortType": None,
"sortBy": "date",
"language": "en",
"doMachineTranslation": False,
"photosPerReviewLimit": 3
},
"extensions": {
"preRegisteredQueryId": "ef1a9f94012220d3"
}
}]
with httpx.Client() as client:
response = client.post(
"https://www.tripadvisor.com/data/graphql/ids",
json=review_payload,
headers=graphql_headers
)
if response.status_code == 200:
data = response.json()
reviews = data[0]["data"]["locations"][0]["reviewListPage"]["reviews"]
total = data[0]["data"]["locations"][0]["reviewListPage"]["totalCount"]
print(f"Gesamtzahl der Bewertungen: {total}")
for r in reviews[:3]:
print(f" [{r['rating']}/5] {r['title']} - {r['createdDate']}")
Wichtig: 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 senkt
Die Rechnung ist simpel. Mit requests+BS4 brauchst du für 100 Hotel-Detailseiten 100 Anfragen. Mit der versteckte-JSON-Methode liefert jede Anfrage alle Daten einer einzelnen Seitenladung – ohne Extra-Requests für aufgeklappte Bewertungen oder dynamisch nachgeladene Inhalte. Mit GraphQL holt ein einzelner API-Call 20 Bewertungen auf einmal. Weniger Anfragen = weniger Rate-Limit-Risiko = weniger Bedarf an Proxy-Rotation. Für kleine bis mittlere Projekte (unter 1.000 Seiten) kommst du mit vernünftigen Pausen unter Umständen ganz ohne Proxys aus.
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
| Feld | Hotels | Restaurants | Attraktionen |
|---|---|---|---|
| Name | ✅ | ✅ | ✅ |
| Bewertung | ✅ | ✅ | ✅ |
| Anzahl der Rezensionen | ✅ | ✅ | ✅ |
| Preis/Preisspanne | ✅ | ✅ | Manchmal |
| Adresse | ✅ | ✅ | ✅ |
| Küchenart | ❌ | ✅ | ❌ |
| Dauer/Tourtyp | ❌ | ❌ | ✅ |
| Ausstattung | ✅ | ❌ | ❌ |
| Koordinaten | ✅ | ✅ | ✅ |
Eine wiederverwendbare scrape_tripadvisor()-Funktion bauen
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import random
import re
import json
def scrape_tripadvisor(category, location_id, location_name, num_pages=3):
"""
TripAdvisor-Listings für Hotels, Restaurants oder Attraktionen scrapen.
Args:
category: "hotels", "restaurants" oder "attractions"
location_id: TripAdvisor-Geo-ID (z. B. "187147" für Paris)
location_name: URL-freundlicher Name (z. B. "Paris_Ile_de_France")
num_pages: Anzahl der zu scrapenden Seiten
"""
url_patterns = {
"hotels": "https://www.tripadvisor.com/Hotels-g{geo}-oa{offset}-{name}-Hotels.html",
"restaurants": "https://www.tripadvisor.com/Restaurants-g{geo}-oa{offset}-{name}.html",
"attractions": "https://www.tripadvisor.com/Attractions-g{geo}-oa{offset}-Activities-{name}.html",
}
first_page_patterns = {
"hotels": "https://www.tripadvisor.com/Hotels-g{geo}-{name}-Hotels.html",
"restaurants": "https://www.tripadvisor.com/Restaurants-g{geo}-{name}.html",
"attractions": "https://www.tripadvisor.com/Attractions-g{geo}-Activities-{name}.html",
}
headers = {
"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",
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Referer": "https://www.tripadvisor.com/",
"Sec-CH-UA": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"Sec-CH-UA-Mobile": "?0",
"Sec-CH-UA-Platform": '"Windows"',
}
session = requests.Session()
session.headers.update(headers)
all_items = []
for page in range(num_pages):
offset = page * 30
if page == 0:
url = first_page_patterns[category].format(geo=location_id, name=location_name)
else:
url = url_patterns[category].format(geo=location_id, offset=offset, name=location_name)
response = session.get(url)
if response.status_code != 200:
print(f" Seite {page + 1}: Status {response.status_code}, Abbruch.")
break
soup = BeautifulSoup(response.text, "html.parser")
cards = soup.select('div[data-test-attribute="location-results-card"]')
for card in cards:
item = {"category": category}
title_el = card.select_one('div[data-automation="hotel-card-title"]') or card.select_one('a[data-automation]')
item["name"] = title_el.get_text(strip=True) if title_el else None
rating_el = card.select_one('[data-automation="bubbleRatingValue"]')
item["rating"] = rating_el.get_text(strip=True) if rating_el else None
review_el = card.select_one('[data-automation="bubbleReviewCount"]')
item["review_count"] = review_el.get_text(strip=True) if review_el else None
all_items.append(item)
print(f" Seite {page + 1}: {len(cards)} Einträge gefunden")
time.sleep(random.uniform(3, 7))
return pd.DataFrame(all_items)
# Anwendungsbeispiele
print("=== Hotels in Paris ===")
hotels_df = scrape_tripadvisor("hotels", "187147", "Paris_Ile_de_France", num_pages=2)
print(hotels_df.head())
print("\n=== Restaurants in Rom ===")
restaurants_df = scrape_tripadvisor("restaurants", "187791", "Rome_Lazio", num_pages=2)
print(restaurants_df.head())
print("\n=== Attraktionen in Barcelona ===")
attractions_df = scrape_tripadvisor("attractions", "187497", "Barcelona_Catalonia", num_pages=2)
print(attractions_df.head())
Eine Funktion, drei Kategorien, kein doppelter Code. Ändert TripAdvisor einen Selektor, passt du ihn an genau einer Stelle an.
Was tun, wenn TripAdvisor dich blockiert? Anti-Bot-Fehlerbehebung
Das ist der Abschnitt, den ich zu meinen TripAdvisor-Anfängen am dringendsten gebraucht hätte – und genau der, den kein Konkurrenzleitfaden strukturiert liefert. TripAdvisor setzt DataDome ein (wertet über 5 Billionen Datenpunkte pro Tag aus) plus Cloudflare WAF. Hier ist eine Diagnosetabelle für die häufigsten Fehlerbilder:
| Symptom | Wahrscheinliche Ursache | Lösung |
|---|---|---|
| HTTP-403-Antwort | Fehlende oder verdächtige Header; Cloudflare-JS-Challenge | Realistische User-Agent-, Accept-Language-, Referer- und Sec-CH-UA-Header setzen. Header-Konsistenz sicherstellen. |
| CAPTCHA-Seite statt Daten | Rate Limiting oder Browser-Fingerprinting | Residential Proxies rotieren, zufällige Pausen einbauen (2–7 Sekunden zwischen Anfragen) |
| Leeres HTML oder leerer Seitenkörper | JavaScript wird von requests nicht gerendert | Auf Selenium umsteigen oder JSON aus dem Seitenquelltext extrahieren |
| Teilweise Rezensionen / „Mehr lesen“ klappt nicht auf | Inhalt wird per Klick geladen | Mit Selenium .click() verwenden oder aus dem eingebetteten JSON-Blob extrahieren |
| Rezensionen nur in einer Sprache | Sprachparameter fehlt | ?filterLang=ALL an die Bewertungs-URL anhängen |
| Daten laden nach N Seiten nicht mehr | Sitzungsbasiertes Rate Limit | Sitzungen rotieren, Cookies zwischen Batches löschen |
| HTTP 1020 Access Denied | IP/ASN von Cloudflare gesperrt | Von Datacenter- auf Residential Proxies wechseln |
| Challenge-Schleife (endlose CAPTCHA) | Defekte Cookie-Persistenz | Sitzungen durch Startseitenaufruf „aufwärmen“; Cookie-Jar beibehalten |
Retry-Logik mit exponentiellem Backoff
Kein anderer Artikel zeigt diesen Code tatsächlich. Hier ist eine wiederverwendbare Retry-Funktion:
import time
import random
import requests
def fetch_with_retry(session, url, max_retries=4, base_delay=2, max_delay=60):
"""
Eine URL mit exponentiellem Backoff und Jitter abrufen.
Rotiert bei jedem Retry den User-Agent.
"""
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"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",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
]
for attempt in range(max_retries):
# User-Agent beim erneuten Versuch rotieren
if attempt > 0:
session.headers["User-Agent"] = random.choice(user_agents)
try:
response = session.get(url, timeout=30)
if response.status_code == 200:
return response
if response.status_code == 429:
# Retry-After-Header beachten, falls vorhanden
retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
print(f" Rate limit (429). Warte {retry_after}s...")
time.sleep(retry_after)
continue
if response.status_code in (403, 503):
wait = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)
print(f" {response.status_code} erhalten. Neuer Versuch {attempt + 1}/{max_retries} in {wait:.1f}s...")
time.sleep(wait)
continue
# Andere Fehlercodes – nicht erneut versuchen
print(f" Unerwarteter Status {response.status_code} für {url}")
return response
except requests.exceptions.Timeout:
wait = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)
print(f" Timeout. Neuer Versuch {attempt + 1}/{max_retries} in {wait:.1f}s...")
time.sleep(wait)
print(f" Alle {max_retries} Versuche für {url} ausgeschöpft")
return None
Header, Proxys und Sessions rotieren
Für dauerhaftes Scraping pflegst du am besten einen Pool aus Header-Sets und rotierst sie gemeinsam:
import random
HEADER_SETS = [
{
"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",
"Sec-CH-UA": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"Sec-CH-UA-Platform": '"Windows"',
},
{
"User-Agent": "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",
"Sec-CH-UA": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"Sec-CH-UA-Platform": '"macOS"',
},
{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
"Sec-CH-UA": '"Google Chrome";v="134", "Not-A.Brand";v="8", "Chromium";v="134"',
"Sec-CH-UA-Platform": '"Windows"',
},
]
PROXY_LIST = [
"http://user:pass@residential-proxy-1:port",
"http://user:pass@residential-proxy-2:port",
# Weitere Residential Proxies hinzufügen
]
def get_rotated_session():
"""Eine neue Session mit rotierten Headern und Proxy erstellen."""
session = requests.Session()
# Zufälliges Header-Set wählen
header_set = random.choice(HEADER_SETS)
base_headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Referer": "https://www.tripadvisor.com/",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-CH-UA-Mobile": "?0",
}
base_headers.update(header_set)
session.headers.update(base_headers)
# Zufälligen Proxy wählen
if PROXY_LIST:
proxy = random.choice(PROXY_LIST)
session.proxies = {"http": proxy, "https": proxy}
return session
Der Proxy-Typ entscheidet. Datacenter-Proxys blockiert TripAdvisor fast augenblicklich (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 die Daten vorliegen, ist der Export in ein nutzbares Format unkompliziert.
CSV-Export (am häufigsten)
import pandas as pd
df = pd.DataFrame(all_hotels)
df.to_csv("tripadvisor_hotels_paris.csv", index=False, encoding="utf-8-sig")
print(f"{len(df)} Zeilen nach CSV exportiert")
Die Angabe encoding='utf-8-sig' ist wichtig – sie sorgt dafür, dass Excel beim Öffnen der CSV nicht-lateinische Zeichen korrekt anzeigt (französische Akzente, chinesische Schriftzeichen usw.).
JSON-Export (für verschachtelte Daten)
Sind Rezensionen unter Hotels verschachtelt, bewahrt JSON die Hierarchie:
# Hierarchische Struktur
hotel_data = {
"property_id": "d194317",
"name": "NH City Centre Amsterdam",
"rating": 4.0,
"reviews": [
{"title": "Tolle Lage", "rating": 5, "date": "2025-03-15", "text": "..."},
{"title": "Durchschnittlicher Aufenthalt", "rating": 3, "date": "2025-03-10", "text": "..."},
]
}
# Für flache Analysen json_normalize verwenden
flat_reviews = pd.json_normalize(
hotel_data,
record_path="reviews",
meta=["property_id", "name"]
)
flat_reviews.to_csv("reviews_flat.csv", index=False)
Zwei-Dateien-Ansatz für relationale Daten
Für große Datensätze nehme ich zwei CSV-Dateien:
hotels.csv— eine Zeile pro Unterkunft (flach)reviews.csv— eine Zeile pro Rezension, mitproperty_idals Fremdschlüssel
So lässt sich in pandas leicht verknüpfen, in eine Datenbank laden oder in BI-Tools importieren.
Wer sich diesen Export-Logik-Kram sparen will, exportiert mit Thunderbit gescrapte Daten direkt nach Excel, Google Sheets, Airtable oder Notion – 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.txtprüfen: TripAdvisors robots.txt blockiert KI-Trainings-Bots (GPTBot, ClaudeBot usw.) vollständig. Standard-Crawler unterliegen selektiven Pfadbeschränkungen. Prüfe sie untertripadvisor.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) DSGVO/CCPA beachten.
- Die offizielle TripAdvisor-API in Betracht ziehen, wenn du Daten im kommerziellen Maßstab brauchst. Das Developer Portal bietet Zugriff auf Unternehmensdaten sowie bis zu 5 Bewertungen und 5 Fotos pro Ort – begrenzt, aber legal und stabil.
- Den rechtlichen Kontext kennen: Das EU-Urteil im Ryanair-Fall (Dezember 2025) 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 scrapst und keine JavaScript-gerenderten Inhalte brauchst.
- Selenium deckt alles ab, was
requestsnicht 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. Ein zweites Tutorial brauchst du dafür nicht.
Und wenn dir mitten im Tutorial dämmert, dass Coden doch nicht dein Ding ist – oder du einfach bis heute Abend 50 Hotels in einer Tabelle brauchst – erledigt das die Thunderbit Chrome-Erweiterung in zwei Klicks: 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 Thunderbit-Blog und unserem YouTube-Kanal.
FAQs
1. Ist es legal, TripAdvisor zu scrapen?
TripAdvisors Nutzungsbedingungen untersagen Scraping ausdrücklich. Gerichte haben jedoch meist 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 allerdings verschärft. Scrape nur öffentliche Daten, respektiere robots.txt, veröffentliche keine urheberrechtlich geschützten Inhalte erneut und hole rechtlichen Rat ein, wenn du die Daten kommerziell nutzen willst.
2. Kann ich TripAdvisor ohne Python scrapen?
Ja. No-Code-Tools wie Thunderbit können TripAdvisor direkt im Browser scrapen – mit KI-gestützter Felderkennung und automatischer Paginierung. Möglich sind auch Browser-Erweiterungen, Google-Sheets-Add-ons oder kommerzielle Scraping-APIs. 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 du tiefer liegende Seiten crawlst.
4. Welche Daten kann ich von TripAdvisor scrapen?
Hotels, Restaurants und Attraktionen – einschließlich Namen, Bewertungen, Rezensionszahlen, 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 für dieselbe Informationsmenge.
Mehr erfahren


