Scrape TripAdvisor za pomocą Pythona (bez blokady)

Ostatnia aktualizacja: April 17, 2026
Scrape TripAdvisor za pomocą Pythona (bez blokady)

W zeszłym tygodniu próbowałem pobrać oceny hoteli i liczbę recenzji dla około 200 obiektów w trzech europejskich miastach z TripAdvisor. Mój pierwszy skrypt — zwykły requests.get() z domyślnymi nagłówkami — zwrócił piękny błąd 403 Forbidden przy każdym pojedynczym żądaniu. Ani jednego bajtu użytecznych danych.

TripAdvisor to jedno z najbogatszych publicznych źródeł danych w branży turystycznej: ponad 1 miliard recenzji, ponad 8 milionów wpisów firm oraz około 460 milionów unikalnych użytkowników miesięcznie. Wpływa na ponad $60 miliardów rocznych wydatków na podróże. Ale pobieranie tych danych programowo? Tu zaczynają się schody. TripAdvisor korzysta z wykrywania botów DataDome, zapór Cloudflare WAF, fingerprintingu TLS i wyzwań JavaScript — to wielowarstwowa ochrona, która blokuje większość naiwnych prób scrapowania jeszcze zanim się rozpoczną. Ten poradnik to jedna kompletna instrukcja, której sam chciałbym wtedy mieć: bezpośrednie porównanie trzech podejść w Pythonie (plus opcja no-code), gotowy kod dla każdego z nich, uporządkowaną sekcję rozwiązywania problemów z anty-botami oraz wielokrotnego użytku wzorce, które działają dla hoteli, restauracji i atrakcji. Niezależnie od tego, czy dopiero zaczynasz z Pythonem, czy jesteś doświadczonym programistą, ten materiał pozwoli Ci oszczędzić mnóstwo niepotrzebnych błędów 403.

Nie chcesz pisać kodu? Zrób scraping TripAdvisor prostszą drogą

Chcę powiedzieć to wprost: wiele osób szukających frazy „scrape TripAdvisor with Python” tak naprawdę nie marzy o pisaniu kodu. Po prostu potrzebują danych — nazw hoteli, ocen, liczby recenzji, cen — i to szybko, najlepiej do arkusza kalkulacyjnego. Jeśli to brzmi znajomo, istnieje znacznie krótsza ścieżka.

Thunderbit to oparta na AI wtyczka do Chrome, którą zbudowaliśmy. Potrafi odczytać dowolną stronę TripAdvisor i automatycznie zasugerować odpowiednie kolumny do wyciągnięcia danych. Proces naprawdę sprowadza się do dwóch kliknięć:

  1. Otwórz stronę z wynikami na TripAdvisor (np. wyszukiwanie „Hotele w Paryżu”).
  2. Kliknij „AI Suggest Fields” w panelu bocznym Thunderbit. AI analizuje stronę i proponuje kolumny, takie jak Nazwa hotelu, Ocena, Liczba recenzji, Cena i Lokalizacja.
  3. Kliknij „Scrape”. Thunderbit wyciąga dane ze wszystkich ofert na stronie — a jeśli potrzebujesz więcej wyników, automatycznie obsługuje paginację.
  4. Wyeksportuj dane do Excela, Google Sheets, Airtable lub Notion. Eksport jest darmowy w każdym planie.

Thunderbit działa dla hoteli, restauracji i atrakcji bez żadnych zmian konfiguracji — AI dopasowuje się do tego, co akurat znajduje się na stronie. W przypadku wyników stronicowanych automatycznie wykrywa przycisk „Next” i nieskończone przewijanie. A ponieważ działa w Twojej prawdziwej przeglądarce Chrome, korzysta z ciasteczek sesji i fingerprintu przeglądarki, co daje mu naturalną przewagę nad mechanizmami wykrywania botów.

Możesz wypróbować to przez rozszerzenie Thunderbit do Chrome — darmowy plan daje 6 stron miesięcznie, czyli wystarczająco, by przetestować ten workflow.

Jeśli potrzebujesz kontroli programistycznej, własnej logiki parsowania albo planujesz zebrać ponad 10 000 stron, Python będzie właściwym wyborem. Czytaj dalej.

Dlaczego warto scrapować TripAdvisor w Pythonie?

Dane z TripAdvisor mają bezpośredni, mierzalny wpływ na biznes. Badanie Cornell University wykazało, że wzrost Global Review Index hotelu o 1 punkt na 100 punktów przekłada się na wzrost średniej stawki dziennej o 0,89% i wzrost Revenue Per Available Room o 1,42%. Inne badanie ScienceDirect pokazało, że zewnętrzny wzrost oceny TripAdvisor o 1 gwiazdkę oznacza dodatkowe 55 000–75 000 dolarów rocznego przychodu dla przeciętnego hotelu. Recenzje to nie tylko wskaźnik „na pokaz” — to realny motor przychodów.

Tak różne zespoły wykorzystują dane z TripAdvisor:

Przypadek użyciaKto zyskujeJakie dane są potrzebne
Analiza konkurencji hoteliSieci hotelowe, revenue managerowieOceny, ceny, liczba recenzji, udogodnienia
Badanie rynku restauracjiGrupy restauracyjne, marki food & beverageTypy kuchni, przedziały cenowe, sentyment recenzji
Śledzenie trendów atrakcjiOperatorzy turystyczni, organizacje turystycznePopularność, sezonowość
Analiza sentymentuBadacze, analitycy danychPełne treści recenzji, oceny gwiazdkowe, daty
Generowanie leadówZespoły sprzedaży, biura podróżyNazwy firm, dane kontaktowe, lokalizacje

Dlaczego akurat Python? Z trzech powodów. Po pierwsze, ekosystem: BeautifulSoup, Selenium, Playwright, Scrapy, httpx, pandas — Python ma dojrzalsze biblioteki do scrapowania i analizy danych niż jakikolwiek inny język. Po drugie, 71,7% programistów zajmujących się web scrapingiem używa Pythona, co oznacza większe wsparcie społeczności, więcej odpowiedzi na Stack Overflow i bardziej aktualne poradniki. Po trzecie, przewaga w całym pipeline: możesz scrapować z BeautifulSoup, czyścić dane w pandas, uruchamiać analizę sentymentu w Hugging Face Transformers i budować dashboardy — wszystko w jednym języku. Bez przełączania kontekstu.

Trzy sposoby na scrapowanie TripAdvisor w Pythonie (porównanie)

Każdy konkurencyjny poradnik wybiera jedno podejście i trzyma się go kurczowo. To nie pomaga, gdy chcesz podjąć decyzję zanim napiszesz kod. Oto tabela porównawcza, którą sam chciałbym wtedy dostać:

PodejścieSzybkośćObsługa JSOdporność na anty-botyZłożonośćNajlepsze do
requests + BeautifulSoup⚡ Szybkie (~120–200 stron/min surowo)❌ Brak⚠️ NiskaŁatweStatyczne strony z listami, małe projekty
Selenium / przeglądarka headless🐢 Wolne (~8–20 stron/min)✅ Pełna⚠️ ŚredniaŚredniaTreści dynamiczne, kliknięcia „Read more”, banery cookies
Ukryte API JSON / GraphQL⚡⚡ Najszybsze (~200–600 stron/min surowo)N/A✅ WyższaTrudneDuża skala ekstrakcji opinii/hoteli
No-code (Thunderbit)⚡ Szybkie✅ Wbudowana✅ WbudowanaNajłatwiejszeOsoby bez kodowania, szybkie jednorazowe eksporty

Kilka ważnych zastrzeżeń. Podane surowe prędkości są teoretyczne — limity TripAdvisor (~10–15 żądań na minutę na IP) ograniczają rzeczywistą przepustowość do około 10 stron/min na IP, niezależnie od podejścia. Metoda ukrytego JSON daje najwięcej danych na jedno żądanie, więc oznacza mniej zapytań łącznie i mniejsze narażenie na rate limiting. Selenium jest w praktyce około 5 razy wolniejsze niż podejścia oparte na requestach, ale to jedyna opcja, gdy trzeba klikać przyciski albo renderować JavaScript.

Dalsza część poradnika pokazuje wszystkie trzy metody w Pythonie wraz z kompletnym kodem. Wybierz tę, która pasuje do Twojej sytuacji, albo połącz je (ja często używam requests+BS4 dla stron z listami i ukrytego JSON dla stron szczegółowych).

Przygotowanie środowiska Pythona

Zanim zaczniemy, przygotujmy środowisko. Potrzebujesz Pythona 3.10+ (polecam 3.12 lub 3.13 — wszystkie główne pakiety obsługują te wersje bez znanych problemów).

Zainstaluj wszystko jednocześnie:

pip install requests beautifulsoup4 selenium httpx parsel pandas curl-cffi

Uwagi o pakietach:

  • requests (2.33.1) — żądania HTTP, wymaga Pythona 3.10+
  • beautifulsoup4 (4.14.3) — parsowanie HTML
  • selenium (4.43.0) — automatyzacja przeglądarki, wymaga Pythona 3.10+
  • httpx (0.28.1) — asynchroniczny klient HTTP
  • parsel (1.11.0) — selektory CSS/XPath (lżejsze niż BS4)
  • pandas (3.0.2) — eksport danych, wymaga Pythona 3.11+
  • curl_cffi (0.15.0) — imitacja fingerprintu TLS (kluczowe przy obchodzeniu Cloudflare)

ChromeDriver: Jeśli korzystasz z Selenium, dobra wiadomość — od Selenium 4.6 Selenium Manager automatycznie pobiera i buforuje właściwy plik ChromeDriver. Nie musisz instalować go ręcznie. Dopasowanie wersji odbywa się dynamicznie, więc nie trzeba martwić się niezgodnością wersji Chrome.

Wirtualne środowisko (zalecane):

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

Podejście 1: scrapowanie TripAdvisor za pomocą Requests i BeautifulSoup

To najprostsza metoda. Sprawdza się dobrze przy stronach z listami (wyniki wyszukiwania hoteli, listy restauracji), gdzie potrzebne dane znajdują się w statycznym HTML. Bez przeglądarki, bez renderowania JavaScript, minimalne zużycie zasobów.

Zrozumienie wzorców URL TripAdvisor

Adresy TripAdvisor mają przewidywalne wzorce zależne od kategorii:

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

Paginacja używa parametru oa (offset anchors), wstawianego do URL. Każda strona pokazuje 30 wyników:

  • Strona 1: bazowy URL (bez parametru oa)
  • Strona 2: Hotels-g187768-oa30-Italy-Hotels.html
  • Strona 3: Hotels-g187768-oa60-Italy-Hotels.html

Dla stron z recenzjami parametr offsetu to or, z krokiem co 10:

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

Aby pobrać recenzje we wszystkich językach, dodaj do adresu ?filterLang=ALL.

Wysyłanie żądań z realistycznymi nagłówkami

TripAdvisor bardzo skrupulatnie sprawdza nagłówki. Żądanie z domyślnymi nagłówkami Pythona jest blokowane od razu. Musisz upodobnić się do prawdziwej przeglądarki Chrome:

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"Długość treści: {len(response.text)} znaków")

Kluczowy szczegół: TripAdvisor sprawdza, czy User-Agent i nagłówki Client Hints Sec-CH-UA są ze sobą spójne. Jeśli deklarujesz Chrome 135 w User-Agent, a w Sec-CH-UA masz Chrome 120, zostaniesz oznaczony jako podejrzany. Zawsze rotuj całe zestawy nagłówków razem, a nie pojedyncze nagłówki.

Parsowanie list z BeautifulSoup

Gdy masz już poprawną odpowiedź, wyciągnij dane za pomocą BeautifulSoup. TripAdvisor używa atrybutów data-automation i data-test-attribute, które są znacznie stabilniejsze niż nazwy klas CSS (zmieniają się bardzo często):

from bs4 import BeautifulSoup

soup = BeautifulSoup(response.text, "html.parser")

# Znajdź wszystkie karty hoteli
cards = soup.select('div[data-test-attribute="location-results-card"]')

hotels = []
for card in cards:
    # Nazwa hotelu
    title_el = card.select_one('div[data-automation="hotel-card-title"]')
    name = title_el.get_text(strip=True) if title_el else None

    # Link do strony szczegółów
    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

    # Ocena
    rating_el = card.select_one('[data-automation="bubbleRatingValue"]')
    rating = rating_el.get_text(strip=True) if rating_el else None

    # Liczba recenzji
    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"Znaleziono {len(hotels)} hoteli na tej stronie")
for h in hotels[:3]:
    print(h)

Uwaga o selektorach: TripAdvisor używa zaciemnionych nazw klas CSS (np. FGwzt, yyzcQ), które zmieniają się przy każdej aktualizacji serwisu. Atrybuty data-automation i data-test-target są znacznie stabilniejsze. Zawsze wybieraj atrybuty danych zamiast nazw klas.

Obsługa paginacji

Aby scrapować wiele stron, przechodź przez parametr offsetu z uprzejmym opóźnieniem między żądaniami:

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):  # pierwsze 5 stron
    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"Strona {page + 1}: status {response.status_code}, przerywam.")
        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"Strona {page + 1}: znaleziono {len(cards)} hoteli")
    time.sleep(random.uniform(3, 7))  # losowe opóźnienie, by uniknąć rate limitu

df = pd.DataFrame(all_hotels)
print(f"\nŁącznie zeskrobano: {len(df)} hoteli")

time.sleep(random.uniform(3, 7)) jest ważne. Próg rate limitu TripAdvisor to mniej więcej 10–15 żądań na minutę na IP. Szybsze tempo uruchamia CAPTCHA albo błędy 429.

Ograniczenia tego podejścia

Kiedy ta metoda przestaje działać? Podejście requests+BS4 zawodzi, gdy:

  • TripAdvisor serwuje treści renderowane po stronie JavaScript (niektóre strony wyników wymagają JS)
  • Teksty recenzji są skrócone za przyciskiem „Read more”
  • Mechanizmy anty-botowe eskalują do wyzwań JavaScript lub CAPTCHA
  • Potrzebujesz danych dostępnych dopiero po renderowaniu po stronie klienta (ceny, dostępność)

W takich sytuacjach potrzebujesz albo Selenium (Podejście 2), albo metody ukrytego JSON (Podejście 3).

Podejście 2: scrapowanie TripAdvisor z Selenium (headless browser)

Selenium uruchamia prawdziwą przeglądarkę, więc potrafi renderować JavaScript, klikać przyciski, obsługiwać banery zgody na cookies i wchodzić w interakcję z dynamiczną treścią. Cena: jest około 5 razy wolniejsze i zużywa 300–500 MB RAM na jedną instancję przeglądarki.

Konfiguracja Selenium z ustawieniami anty-detekcji

W domyślnej konfiguracji Selenium jest bardzo łatwo wykrywalne. Fingerprinting TripAdvisor od razu je rozpoznaje. Trzeba wyłączyć flagi automatyzacji:

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")  # nowy tryb headless (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)

# Usuń właściwość webdriver z navigator
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
})

Czy to wystarczy dla TripAdvisor? Przy małej skali scrapowania (poniżej 50 stron) takie ustawienie zwykle działa, jeśli używasz proxy residential. Przy większych wolumenach może być potrzebny undetected-chromedriver albo nodriver — zabezpieczenia DataDome TripAdvisor analizują ponad 1000 sygnałów na żądanie, w tym fingerprinty TLS, których zwykłe Selenium nie potrafi ukryć.

Scrapowanie wyników wyszukiwania hoteli w Selenium

import time
import random

url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
driver.get(url)

# Poczekaj, aż załadują się karty hoteli
wait = WebDriverWait(driver, 15)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')))

# Obsłuż popup zgody na cookies (jeśli się pojawi)
try:
    cookie_btn = driver.find_element(By.ID, "onetrust-accept-btn-handler")
    cookie_btn.click()
    time.sleep(1)
except:
    pass  # brak popupu cookies

# Pobierz dane hoteli
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"Zeskrobano {len(hotels)} hoteli")
for h in hotels[:3]:
    print(h)

U mnie ta strona zajmowała około 8 sekund, podczas gdy requests+BS4 poniżej 1 sekundy. Taka 8-krotna różnica bardzo szybko się kumuluje, gdy scrapujesz setki stron.

Rozwijanie „Read more” i pobieranie pełnych recenzji

Strony recenzji skracają długie opinie za przyciskiem „Read more”. Selenium może go kliknąć:

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)

# Kliknij wszystkie przyciski „Read more”
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

# Pobierz recenzje
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")
        # Ocena zakodowana w klasie, np. "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"Zeskrobano {len(reviews)} recenzji")

Dodawanie rotacji proxy do Selenium

Przy długotrwałym scrapowaniu potrzebujesz rotacji proxy. Ponieważ selenium-wire jest wycofany od stycznia 2024, użyj wbudowanej obsługi proxy w Chrome:

# Dla proxy bez uwierzytelniania
proxy = "http://your-proxy-address:port"
options.add_argument(f"--proxy-server={proxy}")

# Dla proxy z uwierzytelnianiem użyj rozszerzenia Chrome lub protokołu BiDi Selenium 4

Do programowej rotacji proxy twórz nową instancję drivera z innym proxy dla każdej partii żądań. Nie jest to eleganckie, ale działa niezawodnie.

Podejście 3: ukryty JSON (bez parsowania HTML)

Większość poradników całkowicie pomija to podejście, a szkoda — jest najszybsze i najbardziej eleganckie z całej trójki. TripAdvisor osadza ustrukturyzowane dane jako JSON bezpośrednio w kodzie stron — wewnątrz tagów <script> jako zmienne JavaScript, takie jak pageManifest i urqlCache. Pobranie tego JSON daje czystsze dane (oceny jako liczby, daty w formacie ISO), przy mniejszej liczbie żądań i bez potrzeby renderowania JavaScript.

Znajdowanie osadzonego JSON w kodzie strony

Kluczowe odkrycie: możesz użyć zwykłego requests.get(), aby pobrać stronę, a następnie wyciągnąć JSON z surowego HTML bez renderowania JavaScript.

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)

# Wyciągnij blob JSON pageManifest
match = re.search(r"pageManifest:({.+?})};", response.text)
if match:
    page_data = json.loads(match.group(1))
    print("Znaleziono dane pageManifest")
    print(f"Klucze: {list(page_data.keys())[:10]}")

Jak samodzielnie znaleźć nazwę zmiennej: Otwórz dowolną stronę hotelu TripAdvisor w Chrome, kliknij prawym przyciskiem → Wyświetl źródło strony, a potem Ctrl+F i wyszukaj pageManifest, urqlCache albo aggregateRating. Dane tam są — czekają tylko na parsowanie.

Parsowanie JSON i ekstrakcja danych strukturalnych

TripAdvisor osadza też dane schema.org application/ld+json, które łatwo wyciągnąć:

from parsel import Selector

sel = Selector(text=response.text)

# Pobierz dane strukturalne JSON-LD
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"Nazwa: {data.get('name')}")
        print(f"Ocena: {data.get('aggregateRating', {}).get('ratingValue')}")
        print(f"Liczba recenzji: {data.get('aggregateRating', {}).get('reviewCount')}")
        print(f"Przedział cenowy: {data.get('priceRange')}")
        print(f"Adres: {data.get('address', {}).get('streetAddress')}")
        print(f"Współrzędne: {data.get('geo', {}).get('latitude')}, {data.get('geo', {}).get('longitude')}")
        break

Dane JSON-LD są osadzone w statycznym HTML i nie wymagają renderowania JavaScript. Dają nazwę obiektu, ocenę zbiorczą, liczbę recenzji, adres, współrzędne, przedział cenowy i adresy URL zdjęć — bez parsowania ani jednego taga HTML.

Do bogatszych danych (pojedyncze recenzje, rozkład ocen, listy udogodnień) potrzebujesz obiektu urqlCache:

# Wyciągnij urqlCache dla szczegółowych danych o recenzjach
cache_match = re.search(r'"urqlCache"\s*:\s*({.+?})\s*,\s*"redux"', response.text)
if cache_match:
    cache_data = json.loads(cache_match.group(1))
    # Przeszukaj cache w poszukiwaniu danych recenzji
    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"Znaleziono wpis cache recenzji: {key[:50]}...")
                break

Dokładne ścieżki JSON zmieniają się czasem, gdy TripAdvisor aktualizuje frontend, ale ogólna struktura — JSON-LD dla danych podsumowujących, urqlCache dla danych szczegółowych — pozostaje stabilna od lat.

Reverse engineering API GraphQL TripAdvisor (zaawansowane)

Do ekstrakcji na dużą skalę endpointy GraphQL TripAdvisor zwracają dane strukturalne bezpośrednio. To najszybsza metoda, ale wymaga największej ilości utrzymania.

import httpx
import random
import string

def generate_request_id():
    """Wygeneruj wartość nagłówka X-Requested-By"""
    random_chars = ''.join(random.choices(string.ascii_letters + string.digits, k=180))
    return f"TNI1625!{random_chars}"

# Wyszukiwanie hoteli w Paryżu
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"Żądanie GraphQL nie powiodło się: {response.status_code}")

Do pobierania recenzji przez 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"Łączna liczba recenzji: {total}")
        for r in reviews[:3]:
            print(f"  [{r['rating']}/5] {r['title']} - {r['createdDate']}")

Ważne zastrzeżenie: Wartości preRegisteredQueryId (takie jak 84b17ed122fbdbd4 dla wyszukiwania i ef1a9f94012220d3 dla recenzji) mogą przestać działać, gdy TripAdvisor wdroży nową wersję. Wtedy żądania będą po cichu zawodzić. Trzeba ponownie odkryć identyfikatory zapytań, monitorując ruch sieciowy w DevTools przeglądarki.

Dlaczego ta metoda zmniejsza potrzebę użycia proxy

Matematyka jest prosta. W podejściu requests+BS4 scrapowanie 100 stron szczegółów hoteli wymaga 100 żądań. W metodzie ukrytego JSON każde żądanie zwraca wszystkie potrzebne dane z jednej strony — bez dodatkowych requestów do rozwijania recenzji czy ładowania dynamicznej zawartości. W GraphQL jedno wywołanie API może zwrócić od razu 20 recenzji. Mniej żądań = mniejsze narażenie na rate limiting = mniejsza potrzeba rotacji proxy. Przy małych i średnich projektach (poniżej 1000 stron) możesz w ogóle nie potrzebować proxy, jeśli dodasz rozsądne opóźnienia.

Scrapowanie hoteli, restauracji i atrakcji jednym wielokrotnego użytku skryptem

Cztery na pięć konkurencyjnych poradników omawiają tylko hotele. Tymczasem TripAdvisor ma trzy główne kategorie treści, a wzorce URL i pola danych różnią się między nimi. Oto jak zbudować jedną funkcję, która obsłuży wszystkie trzy.

Pola danych dostępne dla każdej kategorii

PoleHoteleRestauracjeAtrakcje
Nazwa
Ocena
Liczba recenzji
Cena / przedział cenowyCzasami
Adres
Typ kuchni
Czas trwania / typ wycieczki
Udogodnienia
Współrzędne

Budowa wielokrotnego użytku funkcji scrape_tripadvisor()

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):
    """
    Scrapuje wyniki TripAdvisor dla hoteli, restauracji lub atrakcji.

    Args:
        category: "hotels", "restaurants" lub "attractions"
        location_id: geo ID TripAdvisor (np. "187147" dla Paryża)
        location_name: nazwa przyjazna URL (np. "Paris_Ile_de_France")
        num_pages: liczba stron do zeskrobania
    """
    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"  Strona {page + 1}: status {response.status_code}, przerywam.")
            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"  Strona {page + 1}: znaleziono {len(cards)} elementów")
        time.sleep(random.uniform(3, 7))

    return pd.DataFrame(all_items)

# Przykłady użycia
print("=== Hotele w Paryżu ===")
hotels_df = scrape_tripadvisor("hotels", "187147", "Paris_Ile_de_France", num_pages=2)
print(hotels_df.head())

print("\n=== Restauracje w Rzymie ===")
restaurants_df = scrape_tripadvisor("restaurants", "187791", "Rome_Lazio", num_pages=2)
print(restaurants_df.head())

print("\n=== Atrakcje w Barcelonie ===")
attractions_df = scrape_tripadvisor("attractions", "187497", "Barcelona_Catalonia", num_pages=2)
print(attractions_df.head())

Jedna funkcja, trzy kategorie, zero powielania kodu. Jeśli TripAdvisor zmieni selektor, poprawiasz go w jednym miejscu.

Co zrobić, gdy TripAdvisor Cię blokuje (rozwiązywanie problemów z anty-botami)

To jest sekcja, której najbardziej potrzebowałem, gdy zaczynałem scrapować TripAdvisor, i której nie daje żaden konkurencyjny poradnik w uporządkowanej formie. TripAdvisor korzysta jednocześnie z DataDome (analizującego ponad 5 bilionów punktów danych dziennie) i Cloudflare WAF. Oto tabela diagnostyczna najczęstszych problemów:

ObjawPrawdopodobna przyczynaNaprawa
Odpowiedź HTTP 403Brakujące lub podejrzane nagłówki; wyzwanie JS od CloudflareUstaw realistyczne nagłówki User-Agent, Accept-Language, Referer i Sec-CH-UA. Zadbaj o spójność nagłówków.
Strona CAPTCHA zamiast danychRate limiting albo fingerprinting przeglądarkiRotuj residential proxy, dodaj losowe opóźnienia (2–7 sekund między żądaniami)
Pusty HTML albo pusty body stronyJavaScript nie został wyrenderowany przez requestsPrzejdź na Selenium albo wyciągaj dane z ukrytego JSON w kodzie strony
Częściowe recenzje / „Read more” się nie rozwijaTreść ładowana po kliknięciuUżyj Selenium z .click() albo pobierz dane z osadzonego bloku JSON
Recenzje tylko w jednym językuBrak parametru językaDodaj ?filterLang=ALL do adresu recenzji
Dane przestają się ładować po N stronachLimit oparty na sesjiRotuj sesje, czyść ciasteczka między partiami
HTTP 1020 Access DeniedIP/ASN zablokowany przez CloudflareZmień proxy z datacenter na residential
Pętla wyzwań (nieskończona CAPTCHA)Uszkodzona trwałość cookiesRozgrzej sesję, najpierw odwiedzając stronę główną; zachowuj cookie jar

Logika ponawiania z wykładniczym backoffem

Żaden konkurencyjny artykuł tak naprawdę nie pokazuje tego kodu. Oto wielokrotnego użytku funkcja retry:

import time
import random
import requests

def fetch_with_retry(session, url, max_retries=4, base_delay=2, max_delay=60):
    """
    Pobiera URL z wykładniczym backoffem i jitterem.
    Rotuje User-Agent przy każdej próbie.
    """
    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):
        # Rotuj User-Agent przy ponownej próbie
        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:
                # Szanuj nagłówek Retry-After, jeśli istnieje
                retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
                print(f"  Rate limit (429). Czekam {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"  Otrzymano {response.status_code}. Próba {attempt + 1}/{max_retries} za {wait:.1f}s...")
                time.sleep(wait)
                continue

            # Inne kody błędów — bez ponawiania
            print(f"  Nieoczekiwany status {response.status_code} dla {url}")
            return response

        except requests.exceptions.Timeout:
            wait = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)
            print(f"  Timeout. Próba {attempt + 1}/{max_retries} za {wait:.1f}s...")
            time.sleep(wait)

    print(f"  Wyczerpano wszystkie {max_retries} prób dla {url}")
    return None

Rotowanie nagłówków, proxy i sesji

Przy długotrwałym scrapowaniu warto utrzymywać pulę zestawów nagłówków i rotować je razem:

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",
    # Dodaj więcej residential proxy
]

def get_rotated_session():
    """Tworzy nową sesję z rotującymi nagłówkami i proxy."""
    session = requests.Session()

    # Wybierz losowy zestaw nagłówków
    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)

    # Wybierz losowe proxy
    if PROXY_LIST:
        proxy = random.choice(PROXY_LIST)
        session.proxies = {"http": proxy, "https": proxy}

    return session

Typ proxy ma znaczenie. Proxy datacenter są blokowane przez TripAdvisor niemal natychmiast (HTTP 1020 Access Denied). Do długotrwałego scrapowania konieczne są residential proxy — przechodzą przez konsumenckich dostawców internetu i są nie do odróżnienia od prawdziwych użytkowników. Cena zwykle wynosi od $2,50 do $8,40/GB, w zależności od dostawcy.

Eksport i przechowywanie danych z TripAdvisor

Gdy dane są już zebrane, ich zapis do użytecznego formatu jest prosty.

Eksport CSV (najpopularniejszy)

import pandas as pd

df = pd.DataFrame(all_hotels)
df.to_csv("tripadvisor_hotels_paris.csv", index=False, encoding="utf-8-sig")
print(f"Wyeksportowano {len(df)} wierszy do CSV")

encoding='utf-8-sig' jest ważne — dzięki temu Excel poprawnie wyświetla znaki spoza alfabetu łacińskiego (akcenty francuskie, znaki chińskie itd.) przy otwieraniu pliku CSV.

Eksport JSON (dla danych zagnieżdżonych)

Gdy recenzje są zagnieżdżone pod hotelami, JSON zachowuje strukturę hierarchiczną:

# Struktura hierarchiczna
hotel_data = {
    "property_id": "d194317",
    "name": "NH City Centre Amsterdam",
    "rating": 4.0,
    "reviews": [
        {"title": "Świetna lokalizacja", "rating": 5, "date": "2025-03-15", "text": "..."},
        {"title": "Przeciętny pobyt", "rating": 3, "date": "2025-03-10", "text": "..."},
    ]
}

# Do płaskiej analizy użyj json_normalize
flat_reviews = pd.json_normalize(
    hotel_data,
    record_path="reviews",
    meta=["property_id", "name"]
)
flat_reviews.to_csv("reviews_flat.csv", index=False)

Podejście z dwoma plikami dla danych relacyjnych

Przy dużych zbiorach danych używam dwóch plików CSV:

  • hotels.csv — jeden wiersz na obiekt (płasko)
  • reviews.csv — jeden wiersz na recenzję, z property_id jako kluczem obcym

Ułatwia to łączenie w pandas, ładowanie do bazy danych i import do narzędzi BI.

Jeśli nie chcesz zajmować się logiką eksportu, Thunderbit pozwala wyeksportować dane bezpośrednio do Excela, Google Sheets, Airtable lub Notion — wszystko za darmo i bez kodu. Bardzo przydatne, gdy musisz pokazać wyniki nietechnicznym członkom zespołu.

Wskazówki do odpowiedzialnego i wydajnego scrapowania TripAdvisor

Odpowiedzialne scrapowanie w sześciu punktach:

  • Sprawdź robots.txt: plik robots.txt TripAdvisor całkowicie blokuje boty do trenowania AI (GPTBot, ClaudeBot itd.). Standardowe crawlery mają selektywne ograniczenia ścieżek. Sprawdź go pod adresem tripadvisor.com/robots.txt.
  • Dodaj opóźnienia: 3–7 sekund między żądaniami to bezpieczny zakres. Szybsze tempo niż 10–15 żądań na minutę na IP uruchamia rate limiting.
  • Scrapuj tylko dane publiczne. Nie loguj się, aby uzyskiwać dostęp do treści z ograniczeniami.
  • Przechowuj dane bezpiecznie i przestrzegaj GDPR/CCPA, jeśli przetwarzasz dane osobowe (np. imiona recenzentów).
  • Rozważ oficjalne API TripAdvisor, jeśli potrzebujesz danych na skalę komercyjną. Developer Portal oferuje dostęp do informacji o firmach oraz do 5 recenzji i 5 zdjęć na lokalizację — ograniczone, ale legalne i stabilne.
  • Miej świadomość kontekstu prawnego: wyrok Sądu UE w sprawie Ryanair (grudzień 2025) wzmocnił zakazy scrapowania oparte na ToS w całej UE. Regulamin TripAdvisor wyraźnie zabrania scrapowania. Działaj odpowiedzialnie i na własne ryzyko.

Podsumowanie

To pełny obraz sytuacji.

  • Requests + BeautifulSoup to najprostsza droga. Działa dla statycznych stron z listami, wymaga minimalnej konfiguracji i jest szybka. Zacznij tutaj, jeśli scrapujesz mniej niż 100 stron i nie potrzebujesz treści renderowanych przez JavaScript.
  • Selenium poradzi sobie ze wszystkim, czego nie obsłuży requests: treści dynamiczne, przyciski „Read more”, banery cookies. Jest 5 razy wolniejsze i bardziej zasobożerne, ale to jedyna opcja, gdy musisz wchodzić w interakcję ze stroną.
  • Ukryty JSON / GraphQL to najbardziej eleganckie i najszybsze podejście. Daje ustrukturyzowane dane bez parsowania HTML, zmniejsza liczbę żądań (a więc i potrzebę proxy) oraz zwraca dane gotowe do analizy. Wymaga większego reverse engineeringu na starcie i okazjonalnej konserwacji, gdy TripAdvisor zmienia strukturę danych.

Wielokrotnego użytku funkcja scrape_tripadvisor() obsługuje hotele, restauracje i atrakcje. Nie powinieneś potrzebować drugiego tutorialu.

A jeśli w połowie poradnika dojdziesz do wniosku, że kodowanie jednak nie jest dla Ciebie — albo po prostu potrzebujesz 50 hoteli w arkuszu do końca dnia — rozszerzenie Thunderbit do Chrome zrobi to w dwóch kliknięciach, z wykrywaniem pól wspieranym przez AI, automatyczną paginacją i darmowym eksportem do Excela lub Google Sheets. Bez Pythona.

Jeśli chcesz wejść głębiej, mamy więcej poradników na blogu Thunderbit i na naszym kanale YouTube.

FAQ

1. Czy scrapowanie TripAdvisor jest legalne?

Regulamin TripAdvisor wyraźnie zabrania scrapowania. Jednak sądy generalnie uznawały, że pobieranie publicznie dostępnych danych (nieza loginem) nie narusza w USA Computer Fraud and Abuse Act. Mimo to wyrok sądu UE w sprawie Ryanair z 2025 roku zaostrzył ograniczenia oparte na ToS w Europie. Scrapuj wyłącznie dane publiczne, respektuj robots.txt, nie publikuj ponownie treści chronionych prawem autorskim i skonsultuj się z prawnikiem, jeśli używasz danych komercyjnie.

2. Czy mogę scrapować TripAdvisor bez Pythona?

Tak. Narzędzia no-code, takie jak Thunderbit, mogą scrapować TripAdvisor bezpośrednio z przeglądarki, z wykrywaniem pól przez AI i automatyczną paginacją. Możesz też używać rozszerzeń do przeglądarki, dodatków do Google Sheets albo komercyjnych API do scrapowania. Python daje największą kontrolę i elastyczność, ale nie jest jedyną opcją.

3. Jak uniknąć blokady podczas scrapowania TripAdvisor?

Kluczowe techniki: używaj realistycznych i spójnych nagłówków (zwłaszcza User-Agent i Sec-CH-UA), rotuj residential proxy (IP z datacenter są blokowane natychmiast), dodawaj losowe opóźnienia 3–7 sekund między żądaniami, korzystaj z metody ukrytego JSON, aby ograniczyć liczbę żądań, wdrażaj retry z wykładniczym backoffem i „rozgrzewaj” sesję przez wejście na stronę główną przed scrapowaniem głębszych podstron.

4. Jakie dane mogę zeskrobać z TripAdvisor?

Hotele, restauracje i atrakcje — w tym nazwy, oceny, liczbę recenzji, przedziały cenowe, adresy, współrzędne, udogodnienia (hotele), typy kuchni (restauracje), czas trwania wycieczki (atrakcje) oraz pełne treści recenzji z indywidualnymi ocenami i datami. Podejścia oparte na ukrytym JSON i GraphQL zwracają najbogatsze dane na jedno żądanie.

5. Ile stron dziennie mogę zeskrobać z TripAdvisor?

Przy jednym IP i rozsądnych opóźnieniach: około 600–1000 stron dziennie. Przy 20 rotujących residential proxy: mniej więcej 200 000–300 000 stron dziennie przy podejściu opartym na requestach. Selenium jest wolniejsze — spodziewaj się 8000–12 000 stron dziennie na jedno proxy. Podejście ukrytego JSON/GraphQL daje najwięcej danych na żądanie, więc do uzyskania tej samej ilości informacji możesz potrzebować znacznie mniej stron.

Dowiedz się więcej

Ke
Ke
CTO w Thunderbit | Starszy data scientist i ekspert ML Dzięki prawie dziesięciu latom doświadczenia w uczeniu maszynowym i data science, Ke Shen jest absolwentem Columbia University i byłym starszym data scientistą w Walmart Labs. Dysponując dogłębną, uznaną przez branżowych ekspertów wiedzą w zakresie Python, R, Java i statystyki, dzieli się sprawdzonymi w boju spostrzeżeniami na temat wdrażania złożonych algorytmów AI — od teorii po architekturę gotową do produkcji.

Wypróbuj Thunderbit

Zbieraj leady i inne dane w zaledwie 2 kliknięcia. Wspierane przez AI.

Pobierz Thunderbit To darmowe
Wyciągaj dane z użyciem AI
Łatwo przenoś dane do Google Sheets, Airtable lub Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week