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:
- Avaa TripAdvisorin listaussivu (esim. Pariisin hotellihakutulokset).
- Klikkaa Thunderbitin sivupalkista “AI Suggest Fields”. Tekoäly analysoi sivun ja ehdottaa sarakkeita kuten hotellin nimi, arvosana, arvostelumäärä, hinta ja sijainti.
- Klikkaa “Scrape”. Thunderbit poimii tiedot kaikista sivun listauksista — ja hoitaa sivutuksen automaattisesti, jos tarvitset lisää tuloksia.
- 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ötapaus | Kenelle hyötyä | Tarvittava data |
|---|---|---|
| Hotellien kilpailija-analyysi | Hotelliketjut, revenue managerit | Arvosanat, hinnat, arvostelumäärät, palvelut |
| Ravintolamarkkinan tutkimus | Ravintolaryhmät, ruokabrändit | Keittiötyypit, hintahaarukat, arvostelujen sävy |
| Nähtävyyksien trendiseuranta | Matkanjärjestäjät, matkailutoimistot | Suosiosijoitukset, kausivaihtelut |
| Tunneanalyysi | Tutkijat, data-analyytikot | Koko arvosteluteksti, tähtiluokitukset, päivämäärät |
| Liidien generointi | Myyntitiimit, matkatoimistot | Yritysten 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ähestymistapa | Nopeus | JS-tuki | Bottisuojauksen kesto | Monimutkaisuus | Paras käyttötapa |
|---|---|---|---|---|---|
requests + BeautifulSoup | ⚡ Nopea (~120–200 sivua/min raakana) | ❌ Ei lainkaan | ⚠️ Heikko | Helppo | Staattiset listaussivut, pienet projektit |
| Selenium / Headless-selain | 🐢 Hidas (~8–20 sivua/min) | ✅ Täysi | ⚠️ Keskitaso | Keskitaso | Dynaaminen sisältö, “Lue lisää” -klikkaukset, evästebannerit |
| Piilotettu JSON / GraphQL API | ⚡⚡ Nopea ainakin (~200–600 sivua/min raakana) | N/A | ✅ Parempi | Vaikea | Laaja hotellien/arvostelujen poiminta |
| No-code (Thunderbit) | ⚡ Nopea | ✅ Sisäänrakennettu | ✅ Sisäänrakennettu | Helpoin | Ei-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ä listaussivuille 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äminenselenium(4.43.0) — selainautomaatio, vaatii Python 3.10+httpx(0.28.1) — asynkroninen HTTP-asiakasparsel(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ä | Hotellit | Ravintolat | Nähtävyydet |
|---|---|---|---|
| Nimi | ✅ | ✅ | ✅ |
| Arvosana | ✅ | ✅ | ✅ |
| Arvostelumäärä | ✅ | ✅ | ✅ |
| Hinta / hintahaarukka | ✅ | ✅ | Joskus |
| 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:
| Oire | Todennäköinen syy | Korjaus |
|---|---|---|
| HTTP 403 -vastaus | Puuttuvat tai epäilyttävät otsakkeet; Cloudflare JS-haaste | Aseta realistiset User-Agent, Accept-Language, Referer ja Sec-CH-UA -otsakkeet. Varmista otsakkeiden yhteensopivuus. |
| CAPTCHA-sivu datan sijasta | Rate limiting tai selaimen sormenjälki | Kierrätä residential-proxyjä, lisää satunnaisia viiveitä (2–7 sekuntia pyyntöjen väliin) |
| Tyhjä HTML tai tyhjä sivun runko | requests ei renderöi JavaScriptiä | Siirry Seleniumiin tai poimi data piilotetusta JSONista sivun lähdekoodista |
| Osittaiset arvostelut / “Lue lisää” ei avaudu | Sisältö latautuu klikkauksella | Käytä Seleniumin .click()-metodia tai poimi data upotetusta JSON-blokista |
| Arvostelut vain yhdellä kielellä | Kieliparametri puuttuu | Lisää arvostelun URL:iin ?filterLang=ALL |
| Data lakkaa latautumasta N sivun jälkeen | Istuntokohtainen rate limit | Kierrätä istuntoja, tyhjennä evästeet erien välillä |
| HTTP 1020 Access Denied | Cloudflare on estänyt IP/ASN:n | Vaihda datacenter-proxysta residential-proxyyn |
| Loputon haastekierre (CAPTCHA jatkuu) | Rikki mennyt evästeiden säilytys | Lä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_idvierasavaimena
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 osoitteessatripadvisor.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


