TripAdvisorin kaavinta Pythonilla (jäämättä kiinni)

Viimeksi päivitetty April 17, 2026
TripAdvisorin kaavinta Pythonilla (jäämättä kiinni)

Viime viikolla yritin hakea TripAdvisorista hotellien arvosanoja ja arvostelujen määriä noin 200 majoituskohteelle kolmessa Euroopan kaupungissa. Ensimmäinen skriptini — perus requests.get() oletusotsakkeilla — palautti kauniin 403 Forbidden -vastauksen joka ainoalla pyynnöllä. Ei ainuttakaan hyödyllistä tavunmurusta.

TripAdvisor on yksi matkailualan rikkaimmista julkisista tietolähteistä: yli 1 miljardi arvostelua, yli 8 miljoonaa yritystietoa ja noin 460 miljoonaa yksilöllistä kuukausikävijää. Sen vaikutus vuotuiseen matkailukulutukseen on yli $60 miljardia. Mutta tuon datan hakeminen ohjelmallisesti? Siinä homma mutkistuu. TripAdvisor käyttää DataDomen bottitunnistusta, Cloudflaren WAF-suojausta, TLS-fingerprinting-tekniikoita ja JavaScript-haasteita — monikerroksista puolustusjärjestelmää, joka pysäyttää suurimman osan naiivista kaavinnasta jo ennen kuin se ehtii kunnolla alkaa. Tämä opas on juuri se yksi resurssi, jonka olisin itse toivonut löytäväni: vertailu kolmesta Python-kaavintatavasta (plus yksi no-code-vaihtoehto), täydelliset koodit kaikille, jäsennelty vianmääritysosio botensuojauksen ongelmiin sekä uudelleenkäytettäviä malleja hotelleihin, ravintoloihin ja nähtävyyksiin. Olitpa Python-aloittelija tai kokenut kehittäjä, tämä säästää sinut monelta turhalta 403-virheeltä.

Etkö halua kirjoittaa koodia? Kaavi TripAdvisor helpolla tavalla

Haluan sanoa tämän suoraan: moni, joka hakee “scrape TripAdvisor with Python”, ei oikeasti ole naimisissa koodaamisen kanssa. He haluavat vain datan — hotellien nimet, arvosanat, arvostelumäärät, hinnat — nopeasti taulukkoon. Jos kuulostat tutulta, tie on paljon lyhyempi.

Thunderbit on kehittämämme tekoälypohjainen Chrome-laajennus, joka osaa lukea minkä tahansa TripAdvisor-sivun ja ehdottaa automaattisesti sopivat sarakkeet poimittavaksi. Käyttö on aidosti kahden klikkauksen homma:

  1. Avaa TripAdvisorin listaussivu (esim. Pariisin hotellihakutulokset).
  2. Klikkaa Thunderbitin sivupalkista “AI Suggest Fields”. Tekoäly analysoi sivun ja ehdottaa sarakkeita kuten hotellin nimi, arvosana, arvostelumäärä, hinta ja sijainti.
  3. Klikkaa “Scrape”. Thunderbit poimii tiedot kaikista sivun listauksista — ja hoitaa sivutuksen automaattisesti, jos tarvitset lisää tuloksia.
  4. Vie tiedot Exceliin, Google Sheetsiin, Airtableen tai Notioniin. Vienti on ilmainen kaikissa paketeissa.

Thunderbit toimii hotelleissa, ravintoloissa ja nähtävyyksissä ilman mitään asetusten muutoksia — tekoäly mukautuu sivulla olevaan sisältöön. Useampisivuisissa tuloksissa se tunnistaa automaattisesti “Seuraava”-painikkeet ja loputtoman vierityksen. Ja koska se toimii oikeassa Chrome-selaimessasi, se käyttää istuntokeksejäsi ja selaimen sormenjälkeäsi, mikä antaa sille luonnollisen etumatkan bottisuojauksia vastaan.

Voit kokeilla sitä Thunderbit Chrome -laajennuksella — ilmaisversiolla saat 6 sivua kuukaudessa, mikä riittää työnkulun testaamiseen.

Jos tarvitset ohjelmallista hallintaa, omaa jäsentelylogiikkaa tai aiot kaapia yli 10 000 sivua, Python on oikea valinta. Jatka lukemista.

Miksi kaapia TripAdvisor Pythonilla?

TripAdvisorin datalla on suora ja mitattava liiketoimintavaikutus. Cornell Universityn tutkimus osoitti, että yhden pisteen nousu hotellin 100 pisteen Global Review Indexissä kasvattaa keskimääräistä päivähintaa 0,89 % ja Available Room -tuottoa 1,42 %. Toinen ScienceDirect-tutkimus puolestaan osoitti, että ulkoinen yhden tähden nousu TripAdvisor-arvosanassa tuo keskimääräiselle hotellille 55 000–75 000 dollaria lisävuosituloa. Arvostelut eivät ole vain maineen mittareita — ne ovat suoraa liikevaihtoa.

Näin eri tiimit hyödyntävät TripAdvisor-dataa:

KäyttötapausKenelle hyötyäTarvittava data
Hotellien kilpailija-analyysiHotelliketjut, revenue manageritArvosanat, hinnat, arvostelumäärät, palvelut
Ravintolamarkkinan tutkimusRavintolaryhmät, ruokabränditKeittiötyypit, hintahaarukat, arvostelujen sävy
Nähtävyyksien trendiseurantaMatkanjärjestäjät, matkailutoimistotSuosiosijoitukset, kausivaihtelut
TunneanalyysiTutkijat, data-analyytikotKoko arvosteluteksti, tähtiluokitukset, päivämäärät
Liidien generointiMyyntitiimit, matkatoimistotYritysten nimet, yhteystiedot, sijainnit

Miksi juuri Python? Kolme syytä. Ensinnäkin ekosysteemi: BeautifulSoup, Selenium, Playwright, Scrapy, httpx, pandas — Pythonilla on kypsämmät kaavinta- ja data-analyysikirjastot kuin millään muulla kielellä. Toiseksi 71,7 % web-kaavinnan kehittäjistä käyttää Pythonia, mikä tarkoittaa enemmän yhteisön tukea, enemmän Stack Overflow -vastauksia ja enemmän ajantasaisia oppaita. Kolmanneksi putkietu: voit kaapia BeautifulSoupilla, siivota datan pandasilla, ajaa tunneanalyysin Hugging Face Transformersilla ja rakentaa dashboardeja — kaikki samalla kielellä. Ei kontekstinvaihtoa.

Kolme tapaa kaapia TripAdvisor Pythonilla (verrattuna)

Useimmat vertailuartikkelit valitsevat yhden lähestymistavan ja jäävät siihen. Se ei auta, kun yrität päättää ennen koodaamista. Tässä on vertailutaulukko, jonka toivoin jonkun antavan minulle:

LähestymistapaNopeusJS-tukiBottisuojauksen kestoMonimutkaisuusParas käyttötapa
requests + BeautifulSoup⚡ Nopea (~120–200 sivua/min raakana)❌ Ei lainkaan⚠️ HeikkoHelppoStaattiset listaussivut, pienet projektit
Selenium / Headless-selain🐢 Hidas (~8–20 sivua/min)✅ Täysi⚠️ KeskitasoKeskitasoDynaaminen sisältö, “Lue lisää” -klikkaukset, evästebannerit
Piilotettu JSON / GraphQL API⚡⚡ Nopea ainakin (~200–600 sivua/min raakana)N/A✅ ParempiVaikeaLaaja hotellien/arvostelujen poiminta
No-code (Thunderbit)⚡ Nopea✅ Sisäänrakennettu✅ SisäänrakennettuHelpoinEi-kehittäjille, nopeisiin kertavienteihin

Muutama tärkeä huomio. Nuo raakanopeudet ovat teoreettisia — TripAdvisorin rajoitukset (~10–15 pyyntöä minuutissa per IP) rajoittavat käytännön läpimenoa noin 10 sivuun minuutissa per IP:stä riippumatta lähestymistavasta. Piilotettu JSON-menetelmä antaa eniten dataa yhtä pyyntöä kohden, mikä tarkoittaa vähemmän pyyntöjä yhteensä ja pienempää altistumista rajoituksille. Selenium on käytännön testeissä noin 5 kertaa hitaampi kuin request-pohjaiset menetelmät, mutta se on ainoa vaihtoehto, kun täytyy klikata painikkeita tai renderöidä JavaScriptiä.

Tämän oppaan loppuosa käy läpi kaikki kolme Python-menetelmää täydellä koodilla. Valitse tilanteeseesi sopiva tai yhdistele niitä (itse käytän usein requests+BS4:tä listaus­sivuille ja piilotettua JSONia detaljisivuille).

Python-ympäristön valmistelu

Ennen kuin mennään pidemmälle, laitetaan ympäristö kuntoon. Tarvitset Python 3.10+:n (suosittelen 3.12:ta tai 3.13:a — kaikki suuret paketit tukevat niitä ilman tunnettuja ongelmia).

Asenna kaikki kerralla:

pip install requests beautifulsoup4 selenium httpx parsel pandas curl-cffi

Pakettihuomiot:

  • requests (2.33.1) — HTTP-pyynnöt, vaatii Python 3.10+
  • beautifulsoup4 (4.14.3) — HTML:n jäsentäminen
  • selenium (4.43.0) — selainautomaatio, vaatii Python 3.10+
  • httpx (0.28.1) — asynkroninen HTTP-asiakas
  • parsel (1.11.0) — CSS/XPath-valitsimet (kevyempi kuin BS4)
  • pandas (3.0.2) — datan vienti, vaatii Python 3.11+
  • curl_cffi (0.15.0) — TLS-sormenjäljen jäljittely (kriittinen Cloudflaren kiertämisessä)

ChromeDriver: Jos käytät Seleniumia, hyvä uutinen — Selenium 4.6+:sta alkaen Selenium Manager lataa ja välimuistittaa oikean ChromeDriver-binaarin automaattisesti. Manuaalista asennusta ei tarvita. Se hoitaa version yhteensopivuuden dynaamisesti, joten Chrome-version yhteensopimattomuuksista ei tarvitse huolehtia.

Virtuaaliympäristö (suositeltu):

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

Lähestymistapa 1: Kaavi TripAdvisor requestsillä ja BeautifulSoupilla

Tämä on yksinkertaisin tapa. Se toimii hyvin listaussivujen kaavintaan (hotellihakutulokset, ravintolalistat), kun tarvitsemasi data on staattisessa HTML:ssä. Ei selainta, ei JavaScript-renderöintiä, vähän resurssien käyttöä.

TripAdvisorin URL-rakenteen ymmärtäminen

TripAdvisorin URL:t noudattavat kategorioittain melko ennustettavia kaavoja:

  • Hotellit: https://www.tripadvisor.com/Hotels-g{locationId}-{Location_Name}-Hotels.html
  • Ravintolat: https://www.tripadvisor.com/Restaurants-g{locationId}-{Location_Name}.html
  • Nähtävyydet: https://www.tripadvisor.com/Attractions-g{locationId}-Activities-{Location_Name}.html

Sivutus käyttää oa-parametria (offset anchors), joka lisätään URL:iin. Jokaisella sivulla näkyy 30 tulosta:

  • Sivu 1: perus-URL (ei oa-parametria)
  • Sivu 2: Hotels-g187768-oa30-Italy-Hotels.html
  • Sivu 3: Hotels-g187768-oa60-Italy-Hotels.html

Arvostelusivuilla offset-parametri on or, ja kasvu on 10:

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

Jos haluat arvostelut kaikilla kielillä, lisää URL:n loppuun ?filterLang=ALL.

Pyynnöt realistisilla otsakkeilla

TripAdvisor tarkistaa otsakkeita aggressiivisesti. Oletusarvoisilla Python-otsakkeilla tehty pyyntö blokataan heti. Sinun pitää jäljitellä oikeaa Chrome-selainta:

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"Sisällön pituus: {len(response.text)} merkkiä")

Tärkeä yksityiskohta: TripAdvisor varmistaa, että User-Agent- ja Sec-CH-UA-Client Hints -otsakkeet ovat keskenään johdonmukaisia. Jos väität olevasi Chrome 135 User-Agentissa, mutta Sec-CH-UA ilmoittaa Chrome 120:n, järjestelmä liputtaa sinut. Kierrä aina kokonaisia otsakepaketteja, älä yksittäisiä otsakkeita.

Listausten jäsentäminen BeautifulSoupilla

Kun vastaus onnistuu, poimi data BeautifulSoupilla. TripAdvisor käyttää data-automation- ja data-test-attribute-attribuutteja, jotka ovat vakaampia kuin CSS-luokkanimet (jotka vaihtuvat usein):

from bs4 import BeautifulSoup

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

# Etsi kaikki hotellikortit
cards = soup.select('div[data-test-attribute="location-results-card"]')

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

    # Linkki detaljisivulle
    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

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

    # Arvostelumäärä
    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"Löytyi {len(hotels)} hotellia tältä sivulta")
for h in hotels[:3]:
    print(h)

Huomio valitsimista: TripAdvisor käyttää sumentuneita CSS-luokkanimiä (kuten FGwzt, yyzcQ), jotka muuttuvat jokaisessa sivupäivityksessä. data-automation- ja data-test-target-attribuutit ovat paljon vakaampia. Suosi aina data-attribuutteja luokkien sijaan.

Sivutuksen käsittely

Kaapataksesi useita sivuja, käy offset-parametri läpi ja lisää pyyntöjen väliin kohtelias viive:

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):  # Ensimmäiset 5 sivua
    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"Sivu {page + 1}: status {response.status_code}, lopetetaan.")
        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"Sivu {page + 1}: löydettiin {len(cards)} hotellia")
    time.sleep(random.uniform(3, 7))  # Satunnainen viive rate limitin välttämiseksi

df = pd.DataFrame(all_hotels)
print(f"\nKaavittuja hotelleja yhteensä: {len(df)}")

time.sleep(random.uniform(3, 7)) on tärkeä. TripAdvisorin rate limit on suunnilleen 10–15 pyyntöä minuutissa per IP. Jos etenet tätä nopeammin, seurauksena voi olla CAPTCHA tai 429-virhe.

Tämän lähestymistavan rajoitukset

Milloin tämä hajoaa? Requests+BS4 -lähestymistapa epäonnistuu, kun:

  • TripAdvisor tarjoaa JavaScriptillä renderöityä sisältöä (osa hakutulossivuista vaatii JS:n)
  • Arvosteluteksti on typistetty “Lue lisää” -painikkeen taakse
  • Bottisuojaus eskaloituu JavaScript-haasteisiin tai CAPTCHAan
  • Tarvitset dataa, joka näkyy vasta client-side-renderöinnin jälkeen (hinnat, saatavuus)

Näissä tilanteissa tarvitset joko Seleniumin (Lähestymistapa 2) tai piilotetun JSON-menetelmän (Lähestymistapa 3).

Lähestymistapa 2: Kaavi TripAdvisor Seleniumilla (headless-selain)

Selenium käynnistää oikean selaimen, joten se pystyy renderöimään JavaScriptiä, klikkaamaan painikkeita, käsittelemään evästehyväksynnät ja vuorovaikuttamaan dynaamisen sisällön kanssa. Hinta: se on noin 5 kertaa hitaampi ja käyttää 300–500 MB RAMia per selaininstanssi.

Seleniumin asetukset anti-detection-tilassa

Sellaisenaan Selenium on helposti tunnistettavissa. TripAdvisorin sormenjälkijärjestelmä huomaa sen heti. Sinun täytyy poistaa automaatio-flagit:

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")  # Uusi headless-tila (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)

# Poista webdriver-ominaisuus navigatorista
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
})

Riittääkö tämä TripAdvisorille? Pienen mittakaavan kaavinnassa (alle 50 sivua) tämä setup residential proxyjen kanssa yleensä toimii. Suuremmissa määrissä saatat tarvita undetected-chromedriveriä tai nodriveriä — TripAdvisorin DataDome-suoja analysoi yli 1 000 signaalia pyyntöä kohden, mukaan lukien TLS-sormenjäljet, joita tavallinen Selenium ei osaa jäljitellä.

Hotellihakutulosten kaavinta Seleniumilla

import time
import random

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

# Odota, että hotellikortit latautuvat
wait = WebDriverWait(driver, 15)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')))

# Käsittele evästebanneri, jos se ilmestyy
try:
    cookie_btn = driver.find_element(By.ID, "onetrust-accept-btn-handler")
    cookie_btn.click()
    time.sleep(1)
except:
    pass  # Ei evästeikkunaa

# Poimi hotellitiedot
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"Kaavittu {len(hotels)} hotellia")
for h in hotels[:3]:
    print(h)

Tämä vei omalla koneellani noin 8 sekuntia yhdelle sivulle — verrattuna alle sekuntiin requests+BS4:lla. Ero kasvaa nopeasti, kun kaavit satoja sivuja.

“Lue lisää” -tekstin avaaminen ja täysien arvostelujen poiminta

Arvostelusivut typistävät pitkät tekstit “Lue lisää” -painikkeen taakse. Selenium pystyy klikkaamaan sen auki:

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)

# Klikkaa kaikki "Lue lisää" -painikkeet
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

# Poimi arvostelut
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")
        # Rating on koodattu luokkaan kuten "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"Kaavittu {len(reviews)} arvostelua")

Proxyjen kierrätys Seleniumissa

Jatkuvaan kaavintaan tarvitset proxyjen kierrätystä. Koska selenium-wire on ollut vanhentunut tammikuusta 2024 lähtien, käytä Chromen sisäänrakennettua proxy-tukea:

# Ilman tunnistautumista toimiva proxy
proxy = "http://your-proxy-address:port"
options.add_argument(f"--proxy-server={proxy}")

# Proxyt, jotka vaativat tunnistautumista, hoidetaan Chrome-laajennuksella tai Selenium 4:n BiDi-protokollalla

Kun kierrätät proxyt ohjelmallisesti, luo uusi driver-instanssi eri proxyn kanssa jokaista pyyntöerää varten. Se ei ole eleganttia, mutta se on luotettavaa.

Lähestymistapa 3: Piilotettu JSON-menetelmä (ohita HTML:n parsinta kokonaan)

Useimmat oppaat ohittavat tämän lähestymistavan täysin, mikä on harmi — se on näistä kolmesta nopein ja siistein. TripAdvisor upottaa jäsenneltyä dataa suoraan HTML-sivuihinsa — <script>-tageihin JavaScript-muuttujina kuten pageManifest ja urqlCache. Tämän JSONin poimiminen antaa siistimpää dataa (arvosanat numeroina, päivämäärät ISO-muodossa) vähemmillä pyynnöillä eikä vaadi JavaScript-renderöintiä.

Upotetun JSONin löytäminen sivun lähdekoodista

Keskeinen oivallus: voit käyttää tavallista requests.get()-kutsua sivun hakemiseen ja poimia JSONin raakasta HTML:stä ilman JavaScriptin suorittamista.

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)

# Poimi pageManifest-JSON-blobi
match = re.search(r"pageManifest:({.+?})};", response.text)
if match:
    page_data = json.loads(match.group(1))
    print("Löytyi pageManifest-data")
    print(f"Avaimet: {list(page_data.keys())[:10]}")

Miten löydät muuttujan nimen itse: Avaa mikä tahansa TripAdvisorin hotellisivu Chromessa, klikkaa hiiren oikealla → View Page Source, ja etsi Ctrl+F:llä pageManifest, urqlCache tai aggregateRating. Data on siellä odottamassa poimintaa.

JSONin jäsentäminen ja rakenteisen datan poiminta

TripAdvisor upottaa myös application/ld+json-schema.org-dataa, joka on vielä helpompi poimia:

from parsel import Selector

sel = Selector(text=response.text)

# Poimi JSON-LD-rakenteinen data
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"Nimi: {data.get('name')}")
        print(f"Arvosana: {data.get('aggregateRating', {}).get('ratingValue')}")
        print(f"Arvostelumäärä: {data.get('aggregateRating', {}).get('reviewCount')}")
        print(f"Hintahaarukka: {data.get('priceRange')}")
        print(f"Osoite: {data.get('address', {}).get('streetAddress')}")
        print(f"Koordinaatit: {data.get('geo', {}).get('latitude')}, {data.get('geo', {}).get('longitude')}")
        break

JSON-LD-data on upotettu staattiseen HTML:ään eikä vaadi JavaScript-renderöintiä. Se antaa majoituskohteen nimen, kokonaisarvosanan, arvostelumäärän, osoitteen, koordinaatit, hintahaarukan ja kuvalinkit — kaiken ilman yhtään HTML-tagia parsittavaksi.

Rikkaampaa dataa varten (yksittäiset arvostelut, arvosanojen jakaumat, palvelulistat) tarvitset urqlCache-objektin:

# Poimi urqlCache yksityiskohtaisempaa arvosteludataa varten
cache_match = re.search(r'"urqlCache"\s*:\s*({.+?})\s*,\s*"redux"', response.text)
if cache_match:
    cache_data = json.loads(cache_match.group(1))
    # Etsi cachesta arvosteludataa
    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"Löytyi arvostelucachen merkintä: {key[:50]}...")
                break

Tarkat JSON-polut muuttuvat ajoittain, kun TripAdvisor päivittää front-endinsä, mutta yleinen rakenne — JSON-LD yhteenvetotietoa varten, urqlCache yksityiskohtaista dataa varten — on pysynyt vuosia.

TripAdvisorin GraphQL-API:n reverse-engineering (edistynyt)

Suurten massojen poimintaan TripAdvisorin GraphQL-päätepisteet palauttavat rakenteista dataa suoraan. Tämä on nopein menetelmä, mutta myös ylläpidoltaan työläin.

import httpx
import random
import string

def generate_request_id():
    """Luo X-Requested-By-otsakkeen arvo"""
    random_chars = ''.join(random.choices(string.ascii_letters + string.digits, k=180))
    return f"TNI1625!{random_chars}"

# Etsi hotelleja Pariisista
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-pyyntö epäonnistui: {response.status_code}")

Arvostelujen hakeminen GraphQL:n kautta:

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"Arvosteluja yhteensä: {total}")
        for r in reviews[:3]:
            print(f"  [{r['rating']}/5] {r['title']} - {r['createdDate']}")

Tärkeä huomio: preRegisteredQueryId-arvot (kuten 84b17ed122fbdbd4 haulle ja ef1a9f94012220d3 arvosteluille) voivat rikkoutua, kun TripAdvisor julkaisee uuden version. Silloin pyyntösi epäonnistuvat hiljaisesti. Sinun täytyy löytää query ID:t uudelleen seuraamalla verkkopyyntöjä selaimen DevToolsissa.

Miksi tämä menetelmä vähentää proxyn tarvetta

Matematiikka on yksinkertaista. Requests+BS4:lla 100 hotellin detaljisivun kaavinta vaatii 100 pyyntöä. Piilotetulla JSON-menetelmällä yksi pyyntö palauttaa kaiken tarvittavan yhdestä sivulatauksesta — ei lisäpyyntöjä arvostelujen avaamiseen tai dynaamisen sisällön lataamiseen. GraphQL:llä yksi API-kutsu voi palauttaa 20 arvostelua kerralla. Vähemmän pyyntöjä = vähemmän altistumista rate limitille = vähemmän tarvetta proxyjen kierrätykseen. Pienissä ja keskisuurissa projekteissa (alle 1 000 sivua) et välttämättä tarvitse proxya ollenkaan, jos lisäät järkevät viiveet.

Kaavi hotellit, ravintolat ja nähtävyydet yhdellä uudelleenkäytettävällä skriptillä

Neljä viidestä kilpailevasta oppaasta käsittelee vain hotelleja. Mutta TripAdvisorissa on kolme keskeistä sisältökategoriaa, ja URL-rakenteet sekä data kentät eroavat niiden välillä. Näin rakennat yhden funktion, joka hoitaa kaikki kolme.

Kategoriakohtaiset saatavilla olevat datakentät

KenttäHotellitRavintolatNähtävyydet
Nimi
Arvosana
Arvostelumäärä
Hinta / hintahaarukkaJoskus
Osoite
Keittiötyyppi
Kesto / kierroksen tyyppi
Palvelut
Koordinaatit

Uudelleenkäytettävän scrape_tripadvisor()-funktion rakentaminen

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):
    """
    Kaavi TripAdvisorin listaukset hotelleista, ravintoloista tai nähtävyyksistä.

    Args:
        category: "hotels", "restaurants" tai "attractions"
        location_id: TripAdvisorin geo-ID (esim. "187147" Pariisille)
        location_name: URL-ystävällinen nimi (esim. "Paris_Ile_de_France")
        num_pages: Kaavittavien sivujen määrä
    """
    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"  Sivu {page + 1}: status {response.status_code}, lopetetaan.")
            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"  Sivu {page + 1}: löytyi {len(cards)} kohdetta")
        time.sleep(random.uniform(3, 7))

    return pd.DataFrame(all_items)

# Käyttöesimerkit
print("=== Hotellit Pariisissa ===")
hotels_df = scrape_tripadvisor("hotels", "187147", "Paris_Ile_de_France", num_pages=2)
print(hotels_df.head())

print("\n=== Ravintolat Roomassa ===")
restaurants_df = scrape_tripadvisor("restaurants", "187791", "Rome_Lazio", num_pages=2)
print(restaurants_df.head())

print("\n=== Nähtävyydet Barcelonassa ===")
attractions_df = scrape_tripadvisor("attractions", "187497", "Barcelona_Catalonia", num_pages=2)
print(attractions_df.head())

Yksi funktio, kolme kategoriaa, ei koodin toistoa. Jos TripAdvisor muuttaa valitsinta, korjaat sen yhdessä paikassa.

Mitä tehdä, kun TripAdvisor estää sinut (botensuojauksen vianmääritys)

Tämä on se osio, jota itse tarvitsin eniten aloittaessani TripAdvisorin kaavinnan — ja juuri se osa, jota kilpailijat eivät yleensä esitä jäsennellysti. TripAdvisor käyttää yhdessä DataDomea (joka analysoi yli 5 biljoonaa datapistettä päivässä) ja Cloudflaren WAF:ia. Tässä diagnostiikkataulukko yleisimmille virhetiloille:

OireTodennäköinen syyKorjaus
HTTP 403 -vastausPuuttuvat tai epäilyttävät otsakkeet; Cloudflare JS-haasteAseta realistiset User-Agent, Accept-Language, Referer ja Sec-CH-UA -otsakkeet. Varmista otsakkeiden yhteensopivuus.
CAPTCHA-sivu datan sijastaRate limiting tai selaimen sormenjälkiKierrätä residential-proxyjä, lisää satunnaisia viiveitä (2–7 sekuntia pyyntöjen väliin)
Tyhjä HTML tai tyhjä sivun runkorequests ei renderöi JavaScriptiäSiirry Seleniumiin tai poimi data piilotetusta JSONista sivun lähdekoodista
Osittaiset arvostelut / “Lue lisää” ei avauduSisältö latautuu klikkauksellaKäytä Seleniumin .click()-metodia tai poimi data upotetusta JSON-blokista
Arvostelut vain yhdellä kielelläKieliparametri puuttuuLisää arvostelun URL:iin ?filterLang=ALL
Data lakkaa latautumasta N sivun jälkeenIstuntokohtainen rate limitKierrätä istuntoja, tyhjennä evästeet erien välillä
HTTP 1020 Access DeniedCloudflare on estänyt IP/ASN:nVaihda datacenter-proxysta residential-proxyyn
Loputon haastekierre (CAPTCHA jatkuu)Rikki mennyt evästeiden säilytysLämmitä istunto käymällä ensin etusivulla; säilytä cookie jar

Uudelleenyritykset eksponentiaalisella backoffilla

Yksikään kilpailuartikkeli ei oikeasti näytä tätä koodia. Tässä uudelleenkäytettävä retry-funktio:

import time
import random
import requests

def fetch_with_retry(session, url, max_retries=4, base_delay=2, max_delay=60):
    """
    Hakee URL:n eksponentiaalisella backoffilla ja jitterillä.
    Kierrättää User-Agentin jokaisella uudella yrityksellä.
    """
    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):
        # Kierrätä User-Agent uudella yrityksellä
        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:
                # Kunnioita Retry-After-otsaketta, jos se on olemassa
                retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
                print(f"  Rate limit (429). Odotetaan {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"  Saatiin {response.status_code}. Uudelleenyritys {attempt + 1}/{max_retries} {wait:.1f}s kuluttua...")
                time.sleep(wait)
                continue

            # Muut virhekoodit — ei uudelleenyritystä
            print(f"  Odottamaton status {response.status_code} osoitteelle {url}")
            return response

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

    print(f"  Kaikki {max_retries} yritystä käytetty osoitteelle {url}")
    return None

Otsakkeiden, proxyjen ja istuntojen kierrättäminen

Pitkäkestoisessa kaavinnassa pidä käytössä otsakejoukkoja ja kierrätä niitä yhdessä:

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",
    # Lisää lisää residential-proxyjä
]

def get_rotated_session():
    """Luo uusi istunto kierrätetyillä otsakkeilla ja proxyn valinnalla."""
    session = requests.Session()

    # Valitse satunnainen otsakejoukko
    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)

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

    return session

Proxy-tyypillä on väliä. Datacenter-proxyt blokataan TripAdvisorissa lähes heti (HTTP 1020 Access Denied). Residential-proxyt ovat pakollisia jatkuvassa kaavinnassa — ne reitittyvät kuluttajaliittymien kautta ja näyttävät tavallisilta käyttäjiltä. Varaudu maksamaan noin 2,50–8,40 dollaria/GB palveluntarjoajasta riippuen.

Kaavittujen TripAdvisor-tietojen vienti ja tallennus

Kun data on hallussa, sen vieminen käyttökelpoiseen muotoon on suoraviivaista.

CSV-vienti (yleisin)

import pandas as pd

df = pd.DataFrame(all_hotels)
df.to_csv("tripadvisor_hotels_paris.csv", index=False, encoding="utf-8-sig")
print(f"Vietiin {len(df)} riviä CSV:ksi")

encoding='utf-8-sig' on tärkeä — se varmistaa, että Excel näyttää oikein ei-latinalaiset merkit (ranskankieliset aksentit, kiinalaiset merkit jne.), kun CSV avataan.

JSON-vienti (sisäkkäiselle datalle)

Kun arvostelut ovat sisäkkäin hotellien alla, JSON säilyttää hierarkian:

# Hierarkkinen rakenne
hotel_data = {
    "property_id": "d194317",
    "name": "NH City Centre Amsterdam",
    "rating": 4.0,
    "reviews": [
        {"title": "Great location", "rating": 5, "date": "2025-03-15", "text": "..."},
        {"title": "Average stay", "rating": 3, "date": "2025-03-10", "text": "..."},
    ]
}

# Litteään analyysiin käytä json_normalizea
flat_reviews = pd.json_normalize(
    hotel_data,
    record_path="reviews",
    meta=["property_id", "name"]
)
flat_reviews.to_csv("reviews_flat.csv", index=False)

Kahden tiedoston malli relaatiodatalle

Suurissa aineistoissa käytän kahta CSV-tiedostoa:

  • hotels.csv — yksi rivi per kohde (litteä)
  • reviews.csv — yksi rivi per arvostelu, property_id vierasavaimena

Tämä tekee yhdistämisestä pandasissa, tietokantaan lataamisesta tai BI-työkaluihin tuonnista helppoa.

Jos et halua säätää mitään vientilogiikkaa, Thunderbit antaa sinun viedä kaavittu data suoraan Exceliin, Google Sheetsiin, Airtableen tai Notioniin — kaikki ilmaiseksi ja ilman koodia. Kätevää, kun pitää jakaa tulokset myös ei-teknisille tiimikavereille.

Vinkkejä vastuulliseen ja tehokkaaseen TripAdvisor-kaavintaan

Vastuullinen kaavinta kuudessa kohdassa:

  • Tarkista robots.txt: TripAdvisorin robots.txt estää kokonaan AI-koulutusbotit (GPTBot, ClaudeBot jne.). Tavallisille crawlereille on valikoivia polkurajoituksia. Tarkista se osoitteessa tripadvisor.com/robots.txt.
  • Lisää viiveitä: 3–7 sekuntia pyyntöjen välissä on turvallinen haarukka. Jos etenet yli 10–15 pyyntöä minuutissa per IP, rate limit iskee.
  • Kaavi vain julkista dataa. Älä kirjaudu sisään päästäksesi käsiksi rajoitettuun sisältöön.
  • Säilytä data turvallisesti ja noudata GDPR/CCPA-sääntelyä, jos käsittelet henkilötietoja (esim. arvostelijoiden nimet).
  • Harkitse TripAdvisorin virallista API:a, jos tarvitset kaupallisen mittakaavan dataa. Developer Portal tarjoaa yritystietoja sekä enintään 5 arvostelua ja 5 kuvaa kohdetta kohden — rajallinen, mutta laillinen ja vakaa.
  • Huomioi oikeudellinen konteksti: EU-tuomioistuimen Ryanair-ratkaisu (joulukuu 2025) vahvisti EU:ssa käyttöehtopohjaisen kaavintakieltoa. TripAdvisorin käyttöehdot kieltävät kaavinnan nimenomaisesti. Kaavi vastuullisesti ja omalla riskilläsi.

Yhteenveto

Siinä kokonaiskuva.

  • Requests + BeautifulSoup on yksinkertaisin reitti. Se toimii staattisilla listaussivuilla, vaatii vähän valmistelua ja on nopea. Aloita tästä, jos kaavit alle 100 sivua etkä tarvitse JavaScriptillä renderöityä sisältöä.
  • Selenium hoitaa kaiken sen, mihin requests ei pysty: dynaamisen sisällön, “Lue lisää” -painikkeet, evästebannerit. Se on 5 kertaa hitaampi ja raskaampi, mutta ainoa vaihtoehto, kun sinun täytyy vuorovaikuttaa sivun kanssa.
  • Piilotettu JSON / GraphQL on siistein ja nopein lähestymistapa. Se antaa rakenteista dataa ilman HTML:n parsintaa, vähentää pyyntöjen määrää (ja siten proxyn tarvetta) ja palauttaa datan analyysivalmiissa muodossa. Se vaatii enemmän reverse-engineeringiä aluksi sekä satunnaista ylläpitoa, kun TripAdvisor muuttaa datarakennettaan.

Uudelleenkäytettävä scrape_tripadvisor()-funktio kattaa hotellit, ravintolat ja nähtävyydet. Et tarvitse toista opasta.

Ja jos päätät kesken oppaan, ettei koodaaminen ole sinua varten — tai tarvitset vain 50 hotellia taulukkoon saman päivän aikana — Thunderbitin Chrome-laajennus tekee sen kahdella klikkauksella tekoälypohjaisella kenttätunnistuksella, automaattisella sivutuksella ja ilmaisella viennillä Exceliin tai Google Sheetsiin. Pythonia ei tarvita.

Jos haluat mennä syvemmälle, meillä on lisää kaavintaoppaita Thunderbit-blogissa ja YouTube-kanavallamme.

UKK

1. Onko TripAdvisorin kaavinta laillista?

TripAdvisorin käyttöehdot kieltävät kaavinnan nimenomaisesti. Tuomioistuimet ovat kuitenkin yleisesti katsoneet, että julkisesti saatavilla olevan datan kaavinta (ei kirjautumisen takana) ei riko Yhdysvaltain Computer Fraud and Abuse Actia. Tästä huolimatta vuoden 2025 EU-tuomioistuimen Ryanair-ratkaisu vahvisti käyttöehtoihin perustuvia rajoituksia Euroopassa. Kaavi vain julkista dataa, kunnioita robots.txt:ää, älä julkaise tekijänoikeuksilla suojattua sisältöä uudelleen ja kysy juristilta neuvoa, jos käytät dataa kaupallisesti.

2. Voinko kaapia TripAdvisoria ilman Pythonia?

Kyllä. No-code-työkalut kuten Thunderbit voivat kaapia TripAdvisorin suoraan selaimestasi tekoälypohjaisella kenttätunnistuksella ja automaattisella sivutuksella. Voit käyttää myös selainlaajennuksia, Google Sheets -lisäosia tai kaupallisia scraping-rajapintoja. Python antaa eniten hallintaa ja joustavuutta, mutta se ei ole ainoa vaihtoehto.

3. Miten vältän estot TripAdvisoria kaapatessa?

Keskeiset keinot: käytä realistisia ja johdonmukaisia otsakkeita (erityisesti User-Agent ja Sec-CH-UA), kierrätä residential-proxyjä (datacenter-IP:t blokataan heti), lisää satunnaisia 3–7 sekunnin viiveitä pyyntöjen väliin, käytä piilotettua JSON-menetelmää pyyntöjen kokonaismäärän minimoimiseksi, toteuta retry-logiikka eksponentiaalisella backoffilla ja lämmitä istunto käymällä etusivulla ennen syvien sivujen kaavintaa.

4. Mitä dataa voin kaapia TripAdvisorista?

Hotelleja, ravintoloita ja nähtävyyksiä — mukaan lukien nimet, arvosanat, arvostelumäärät, hintahaarukat, osoitteet, koordinaatit, palvelut (hotellit), keittiötyypit (ravintolat), kierrosten kestot (nähtävyydet) sekä koko arvostelutekstin yksittäisine arvioineen ja päivämäärineen. Piilotettu JSON- ja GraphQL-lähestymistapa palauttaa rikkaimman datan per pyyntö.

5. Kuinka monta sivua voin kaapia TripAdvisorista päivässä?

Yhdellä IP-osoitteella ja järkevillä viiveillä noin 600–1 000 sivua päivässä. 20 kierrätettävällä residential-proxyllä noin 200 000–300 000 sivua päivässä request-pohjaisilla menetelmillä. Selenium on hitaampi — arvioi 8 000–12 000 sivua päivässä per proxy. Piilotettu JSON/GraphQL-menetelmä antaa eniten dataa yhtä pyyntöä kohden, joten saman informaatiomäärän saamiseksi tarvitset usein paljon vähemmän sivuja.

Lisää luettavaa

Ke
Ke
Thunderbitin CTO | Senior Data Scientist & ML-asiantuntija Lähes vuosikymmenen kokemuksella koneoppimisesta ja data science -työstä Ke Shen on Columbia Universityn alumni ja entinen Senior Data Scientist Walmart Labsilla. Hänellä on syvällistä, alan kollegoiden tunnustamaa asiantuntemusta Pythonista, R:stä, Javasta ja tilastotieteestä, ja hän jakaa käytännössä koeteltuja oivalluksia siitä, miten monimutkaiset tekoälyalgoritmit viedään teoriasta tuotantokäyttöön sopivaksi arkkitehtuuriksi.
Sisällysluettelo

Poimi verkkosivu pelkällä pyynnöllä

Kerro mitä tarvitset selkeällä englannilla. Tai vielä parempaa, älä kerro mitään.

Kokeile Thunderbitiä ilmaista
Poimi dataa AI:n avulla
Siirrä data helposti Google Sheetiin, Airtableen tai Notioniin
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week