Scrape TripAdvisor dengan Python (Tanpa Kena Blokir)

Terakhir diperbarui pada April 17, 2026

Minggu lalu, saya sempat coba menarik rating hotel dan jumlah ulasan untuk sekitar 200 properti di tiga kota Eropa dari TripAdvisor. Script pertama saya — cuma requests.get() biasa dengan header standar — langsung mental kena 403 Forbidden di setiap request. Tidak ada satu byte pun data yang bisa dipakai.

TripAdvisor adalah salah satu sumber data publik paling kaya di industri travel: lebih dari , 8+ juta listing bisnis, dan sekitar 460 juta pengunjung unik per bulan. Platform ini juga memengaruhi lebih dari belanja perjalanan tiap tahun. Tapi kalau mau ambil datanya secara programatik? Nah, di situlah letak ribetnya. TripAdvisor memakai deteksi bot DataDome, Cloudflare WAF, TLS fingerprinting, dan JavaScript challenge — lapisan pertahanan berlapis yang bikin sebagian besar upaya scraping amatiran gagal bahkan sebelum mulai. Panduan ini adalah satu-satunya referensi yang saya harap dulu saya punya: perbandingan head-to-head tiga pendekatan scraping Python (plus opsi no-code), kode lengkap untuk masing-masing, bagian troubleshooting anti-bot yang rapi, dan pola yang bisa dipakai ulang untuk hotel, restoran, dan atraksi. Entah Anda masih pemula di Python atau sudah developer berpengalaman, panduan ini semoga bisa menghemat banyak 403 yang buang-buang waktu.

Tidak Mau Menulis Kode? Cara Mudah Scrape TripAdvisor

Saya mau jujur dari awal. Banyak orang yang cari "scrape TripAdvisor with Python" sebenarnya tidak terlalu pengin ngoprek kode. Mereka cuma butuh datanya — nama hotel, rating, jumlah ulasan, harga — masuk ke spreadsheet, secepat mungkin. Kalau itu yang Anda cari, ada jalan yang jauh lebih singkat.

adalah ekstensi Chrome berbasis AI yang kami buat, yang bisa membaca halaman TripAdvisor apa pun dan otomatis menyarankan kolom yang tepat untuk diekstrak. Alurnya benar-benar cuma dua klik:

  1. Buka halaman listing TripAdvisor (misalnya hasil pencarian "Hotels in Paris").
  2. Klik "AI Suggest Fields" di sidebar Thunderbit. AI akan memindai halaman dan mengusulkan kolom seperti Hotel Name, Rating, Review Count, Price, dan Location.
  3. Klik "Scrape." Thunderbit mengekstrak data dari setiap listing di halaman — dan menangani pagination otomatis kalau Anda butuh hasil lebih banyak.
  4. Ekspor ke Excel, Google Sheets, Airtable, atau Notion. Ekspor gratis di semua paket.

Thunderbit bekerja untuk hotel, restoran, dan atraksi tanpa perlu ubah konfigurasi — AI menyesuaikan diri dengan konten halaman. Untuk hasil bertingkat halaman, alat ini otomatis mendeteksi tombol "Next" dan infinite scroll. Karena berjalan di browser Chrome asli Anda, Thunderbit memakai cookie sesi dan fingerprint browser Anda, jadi punya keunggulan alami menghadapi deteksi bot.

Anda bisa mencobanya lewat — paket gratis memberi Anda 6 halaman/bulan, cukup buat tes alurnya.

Kalau Anda butuh kontrol programatik, logika parsing khusus, atau memang berencana scraping 10.000+ halaman, Python tetap jadi pilihan terbaik. Lanjut baca.

Kenapa Scrape TripAdvisor dengan Python?

Data TripAdvisor punya dampak bisnis yang langsung dan terukur. Sebuah menemukan bahwa kenaikan 1 poin pada Global Review Index hotel (skala 100 poin) menghasilkan kenaikan 0,89% pada average daily rate dan kenaikan 1,42% pada Revenue Per Available Room. Studi lain menunjukkan bahwa kenaikan rating TripAdvisor sebesar 1 bintang bisa menghasilkan tambahan pendapatan tahunan $55.000–$75.000 untuk hotel rata-rata. Ulasan bukan sekadar angka pajangan — mereka benar-benar menggerakkan pendapatan.

Berikut cara berbagai tim memanfaatkan data TripAdvisor:

Use CaseSiapa yang DiuntungkanData yang Dibutuhkan
Analisis pesaing hotelJaringan hotel, revenue managerRating, harga, volume ulasan, fasilitas
Riset pasar restoranGrup restoran, brand makananJenis masakan, kisaran harga, sentimen ulasan
Pelacakan tren atraksiOperator tur, dinas pariwisataPeringkat popularitas, pola musiman
Analisis sentimenPeneliti, analis dataTeks ulasan lengkap, rating bintang, tanggal
Lead generationTim sales, agen perjalananNama bisnis, info kontak, lokasi

Kenapa khusus Python? Ada tiga alasan. Pertama, ekosistemnya: BeautifulSoup, Selenium, Playwright, Scrapy, httpx, pandas — Python punya library scraping dan analisis data yang jauh lebih matang dibanding bahasa lain. Kedua, memakai Python, jadi dukungan komunitas lebih besar, jawaban StackOverflow lebih banyak, dan panduan yang lebih update. Ketiga, keunggulan pipeline: Anda bisa scraping dengan BeautifulSoup, membersihkan data dengan pandas, menjalankan analisis sentimen dengan Hugging Face Transformers, lalu bikin dashboard — semuanya dalam satu bahasa. Tidak perlu loncat konteks.

Tiga Cara Scrape TripAdvisor dengan Python (Dibandingkan)

Biasanya panduan pesaing memilih satu pendekatan lalu langsung ngebut ke sana. Itu kurang membantu kalau Anda masih sedang menimbang-nimbang sebelum nulis kode. Ini tabel perbandingan yang dulu saya harap ada buat saya:

PendekatanKecepatanDukungan JSKetahanan Anti-BotKompleksitasPaling Cocok Untuk
requests + BeautifulSoup⚡ Cepat (~120–200 halaman/menit mentah)❌ Tidak ada⚠️ RendahMudahHalaman listing statis, proyek skala kecil
Selenium / Headless Browser🐢 Lambat (~8–20 halaman/menit)✅ Penuh⚠️ SedangMenengahKonten dinamis, klik "Read more", banner cookie
Hidden JSON / GraphQL API⚡⚡ Paling cepat (~200–600 halaman/menit mentah)N/A✅ Lebih tinggiSulitEkstraksi review/hotel skala besar
No-code (Thunderbit)⚡ Cepat✅ Bawaan✅ BawaanPaling mudahNon-developer, ekspor cepat sekali jalan

Ada beberapa catatan penting. Kecepatan mentah di atas sifatnya teoritis — rate limit TripAdvisor (~10–15 request per menit per IP) membatasi throughput nyata jadi sekitar 10 halaman/menit per IP, apa pun pendekatannya. Metode hidden JSON memberi data paling banyak per request, artinya total request lebih sedikit dan paparan ke rate limiting juga lebih kecil. Selenium sekitar 5x lebih lambat daripada pendekatan berbasis request dalam benchmark dunia nyata, tapi ini satu-satunya opsi kalau Anda perlu klik tombol atau merender JavaScript.

Sisa panduan ini akan membahas ketiga metode Python tersebut dengan kode lengkap. Pilih yang paling cocok untuk situasi Anda, atau gabungkan (saya sendiri sering pakai requests+BS4 untuk halaman listing dan hidden JSON untuk halaman detail).

Menyiapkan Lingkungan Python Anda

Sebelum mulai, mari rapikan environment-nya dulu. Anda butuh Python 3.10+ (saya rekomendasikan 3.12 atau 3.13 — semua paket utama sudah mendukungnya tanpa isu yang diketahui).

Install semuanya sekaligus:

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

Catatan paket:

  • requests (2.33.1) — request HTTP, butuh Python 3.10+
  • beautifulsoup4 (4.14.3) — parsing HTML
  • selenium (4.43.0) — otomasi browser, butuh Python 3.10+
  • httpx (0.28.1) — klien HTTP async
  • parsel (1.11.0) — selector CSS/XPath (lebih ringan dari BS4)
  • pandas (3.0.2) — ekspor data, butuh Python 3.11+
  • curl-cffi (0.15.0) — impersonasi TLS fingerprint (krusial buat nembus Cloudflare)

ChromeDriver: Kalau Anda pakai Selenium, kabar baik — sejak Selenium 4.6+, Selenium Manager otomatis mengunduh dan menyimpan binary ChromeDriver yang cocok. Tidak perlu instal manual. Versi browser akan dicocokkan secara dinamis, jadi Anda tidak perlu pusing soal mismatch versi Chrome.

Virtual environment (disarankan):

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

Pendekatan 1: Scrape TripAdvisor dengan Requests dan BeautifulSoup

Ini pendekatan paling sederhana. Cocok untuk scraping halaman listing (hasil pencarian hotel, daftar restoran) ketika data yang Anda butuhkan sudah ada di HTML statis. Tanpa browser, tanpa rendering JavaScript, dan penggunaan resource minimal.

Memahami Pola URL TripAdvisor

URL TripAdvisor mengikuti pola yang cukup konsisten berdasarkan kategori:

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

Pagination memakai parameter oa (offset anchors) yang disisipkan ke URL. Setiap halaman menampilkan 30 hasil:

  • Halaman 1: base URL (tanpa parameter oa)
  • Halaman 2: Hotels-g187768-oa30-Italy-Hotels.html
  • Halaman 3: Hotels-g187768-oa60-Italy-Hotels.html

Untuk halaman ulasan, parameter offset-nya adalah or dengan kenaikan 10:

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

Untuk mengambil ulasan dalam semua bahasa, tambahkan ?filterLang=ALL ke URL.

Mengirim Request dengan Header yang Realistis

TripAdvisor memeriksa header dengan ketat. Request dengan header default Python biasanya langsung diblokir. Anda perlu meniru browser Chrome asli:

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

Detail penting: TripAdvisor memvalidasi bahwa header User-Agent dan Sec-CH-UA Client Hints Anda konsisten. Kalau Anda mengaku sebagai Chrome 135 di User-Agent, tapi Sec-CH-UA bilang Chrome 120, Anda bakal dianggap mencurigakan. Selalu rotasi satu set header secara utuh, bukan satu-satu.

Parsing Listing dengan BeautifulSoup

Setelah dapat response yang berhasil, ekstrak datanya dengan BeautifulSoup. TripAdvisor memakai atribut data-automation dan data-test-attribute yang jauh lebih stabil dibanding nama class CSS (yang sering berubah):

1from bs4 import BeautifulSoup
2soup = BeautifulSoup(response.text, "html.parser")
3# Cari semua kartu listing hotel
4cards = soup.select('div[data-test-attribute="location-results-card"]')
5hotels = []
6for card in cards:
7    # Nama hotel
8    title_el = card.select_one('div[data-automation="hotel-card-title"]')
9    name = title_el.get_text(strip=True) if title_el else None
10    # Link ke halaman detail
11    link_el = card.select_one('div[data-automation="hotel-card-title"] a')
12    link = "https://www.tripadvisor.com" + link_el["href"] if link_el else None
13    # Rating
14    rating_el = card.select_one('[data-automation="bubbleRatingValue"]')
15    rating = rating_el.get_text(strip=True) if rating_el else None
16    # Jumlah ulasan
17    review_el = card.select_one('[data-automation="bubbleReviewCount"]')
18    review_count = review_el.get_text(strip=True).replace(",", "").split()[0] if review_el else None
19> This paragraph contains content that cannot be parsed and has been skipped.
20print(f"Ditemukan {len(hotels)} hotel di halaman ini")
21for h in hotels[:3]:
22    print(h)

Soal selector: TripAdvisor pakai nama class CSS yang disamarkan (misalnya FGwzt, yyzcQ) dan sering berubah setiap update situs. Atribut data-automation dan data-test-target jauh lebih stabil. Selalu prioritaskan data attribute daripada class name.

Menangani Pagination

Untuk scraping beberapa halaman, lakukan loop pada parameter offset dengan jeda yang wajar di antara request:

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

time.sleep(random.uniform(3, 7)) itu penting. Ambang rate limit TripAdvisor kira-kira 10–15 request per menit per IP. Kalau terlalu ngebut, Anda akan kena CAPTCHA atau error 429.

Keterbatasan Pendekatan Ini

Kapan pendekatan ini mentok? Requests+BS4 bakal gagal kalau:

  • TripAdvisor menyajikan konten yang dirender JavaScript (beberapa halaman hasil pencarian butuh JS)
  • Teks ulasan dipotong di balik tombol "Read more"
  • Perlindungan anti-bot naik level jadi JavaScript challenge atau CAPTCHA
  • Anda butuh data yang cuma muncul setelah rendering sisi klien (harga, ketersediaan)

Untuk skenario seperti ini, Anda butuh Selenium (Pendekatan 2) atau metode hidden JSON (Pendekatan 3).

Pendekatan 2: Scrape TripAdvisor dengan Selenium (Headless Browser)

Selenium menjalankan browser beneran, jadi bisa merender JavaScript, klik tombol, menangani banner persetujuan cookie, dan berinteraksi dengan konten dinamis. Konsekuensinya: kecepatannya sekitar dan memakai 300–500MB RAM per instance browser.

Mengonfigurasi Selenium dengan Pengaturan Anti-Deteksi

Secara default, Selenium gampang banget dikenali. Fingerprinting TripAdvisor akan langsung menangkapnya. Anda perlu mematikan flag otomatisasi:

1from selenium import webdriver
2from selenium.webdriver.chrome.options import Options
3from selenium.webdriver.common.by import By
4from selenium.webdriver.support.ui import WebDriverWait
5from selenium.webdriver.support import expected_conditions as EC
6options = Options()
7options.add_argument("--headless=new")  # Pakai mode headless baru (Chrome 112+)
8options.add_argument("--disable-blink-features=AutomationControlled")
9options.add_argument("--window-size=1920,1080")
10options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36")
11options.add_experimental_option("excludeSwitches", ["enable-automation"])
12options.add_experimental_option("useAutomationExtension", False)
13driver = webdriver.Chrome(options=options)
14> This paragraph contains content that cannot be parsed and has been skipped.
15**Apakah ini cukup untuk TripAdvisor?** Untuk scraping skala kecil (di bawah 50 halaman), setelan ini biasanya berhasil kalau dipasangkan dengan residential proxy. Untuk volume yang lebih besar, Anda mungkin perlu `undetected-chromedriver` atau `nodriver` — proteksi DataDome TripAdvisor menganalisis lebih dari 1.000 sinyal per request, termasuk TLS fingerprint yang tidak bisa dipalsukan oleh Selenium biasa.
16### Scrape Hasil Pencarian Hotel dengan Selenium
17```python
18import time
19import random
20url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
21driver.get(url)
22# Tunggu kartu hotel dimuat
23wait = WebDriverWait(driver, 15)
24wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')))
25# Tangani popup persetujuan cookie (kalau muncul)
26try:
27    cookie_btn = driver.find_element(By.ID, "onetrust-accept-btn-handler")
28    cookie_btn.click()
29    time.sleep(1)
30except:
31    pass  # Tidak ada popup cookie
32# Ekstrak data hotel
33cards = driver.find_elements(By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')
34hotels = []
35for card in cards:
36    try:
37        name = card.find_element(By.CSS_SELECTOR, 'div[data-automation="hotel-card-title"]').text
38    except:
39        name = None
40    try:
41        rating = card.find_element(By.CSS_SELECTOR, '[data-automation="bubbleRatingValue"]').text
42    except:
43        rating = None
44    try:
45        reviews = card.find_element(By.CSS_SELECTOR, '[data-automation="bubbleReviewCount"]').text
46    except:
47        reviews = None
48> This paragraph contains content that cannot be parsed and has been skipped.
49print(f"Berhasil scrape {len(hotels)} hotel")
50for h in hotels[:3]:
51    print(h)

Ini memakan sekitar 8 detik untuk satu halaman di mesin saya — dibandingkan kurang dari 1 detik dengan requests+BS4. Selisih 8x ini bakal terasa banget saat Anda scrape ratusan halaman.

Membuka "Read More" dan Mengambil Ulasan Lengkap

Halaman ulasan memotong review panjang di balik tombol "Read more". Selenium bisa mengkliknya:

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

Menambahkan Rotasi Proxy di Selenium

Untuk scraping berkelanjutan, Anda butuh rotasi proxy. Karena selenium-wire sudah deprecated sejak Januari 2024, pakai dukungan proxy bawaan Chrome:

1# Dengan proxy tanpa autentikasi
2proxy = "http://your-proxy-address:port"
3options.add_argument(f"--proxy-server={proxy}")
4# Untuk proxy dengan autentikasi, gunakan ekstensi Chrome atau protokol BiDi Selenium 4

Untuk rotasi proxy secara programatik, buat instance driver baru dengan proxy berbeda untuk setiap batch request. Memang kurang elegan, tapi cukup andal.

Pendekatan 3: Metode Hidden JSON (Tanpa Parsing HTML Sama Sekali)

Banyak panduan melewatkan pendekatan ini, padahal justru ini yang paling cepat dan paling bersih dari ketiganya. TripAdvisor menyematkan data terstruktur sebagai JSON langsung di halaman HTML — di dalam tag <script> sebagai variabel JavaScript seperti pageManifest dan urqlCache. Mengekstrak JSON ini memberi Anda data yang lebih rapi (rating sebagai angka, tanggal dalam format ISO) dengan request lebih sedikit dan tanpa perlu rendering JavaScript.

Menemukan JSON Tersembunyi di Source Halaman

Triknya sederhana: Anda cukup memakai requests.get() untuk mengambil halaman, lalu mengekstrak JSON dari HTML mentah tanpa pernah merender JavaScript.

1import requests
2import re
3import json
4> This paragraph contains content that cannot be parsed and has been skipped.
5url = "https://www.tripadvisor.com/Hotel_Review-g188590-d194317-Reviews-NH_City_Centre_Amsterdam.html"
6response = requests.get(url, headers=headers)
7> This paragraph contains content that cannot be parsed and has been skipped.
8**Cara menemukan nama variabelnya sendiri:** Buka halaman hotel TripAdvisor apa pun di Chrome, klik kanan → View Page Source, lalu Ctrl+F untuk `pageManifest` atau `urqlCache` atau `aggregateRating`. Datanya ada di sana, tinggal diparse.
9### Parsing JSON dan Mengambil Data Terstruktur
10TripAdvisor juga menyematkan data schema.org `application/ld+json` yang bahkan lebih gampang diekstrak:
11```python
12from parsel import Selector
13sel = Selector(text=response.text)
14# Ambil structured data JSON-LD
15json_ld_scripts = sel.xpath("//script[@type='application/ld+json']/text()").getall()
16for script in json_ld_scripts:
17    data = json.loads(script)
18    if isinstance(data, dict) and data.get("@type") in ["Hotel", "Restaurant", "TouristAttraction"]:
19        print(f"Nama: {data.get('name')}")
20        print(f"Rating: {data.get('aggregateRating', {}).get('ratingValue')}")
21        print(f"Jumlah Ulasan: {data.get('aggregateRating', {}).get('reviewCount')}")
22        print(f"Rentang Harga: {data.get('priceRange')}")
23        print(f"Alamat: {data.get('address', {}).get('streetAddress')}")
24        print(f"Koordinat: {data.get('geo', {}).get('latitude')}, {data.get('geo', {}).get('longitude')}")
25        break

Data JSON-LD ini tertanam di HTML statis dan TIDAK butuh rendering JavaScript. Anda bisa dapat nama properti, aggregate rating, jumlah ulasan, alamat, koordinat, rentang harga, dan URL foto — tanpa parsing satu tag HTML pun.

Untuk data yang lebih kaya lagi (ulasan individual, breakdown rating, daftar fasilitas), Anda perlu objek urqlCache:

1# Ekstrak urqlCache untuk data ulasan detail
2cache_match = re.search(r'"urqlCache"\s*:\s*({.+?})\s*,\s*"redux"', response.text)
3if cache_match:
4    cache_data = json.loads(cache_match.group(1))
5    # Telusuri cache untuk menemukan data ulasan
6    for key, value in cache_data.items():
7        if "reviews" in str(value).lower()[:100]:
8            reviews_data = json.loads(value.get("data", "{}")) if isinstance(value, dict) else None
9            if reviews_data:
10                print(f"Ditemukan entry cache ulasan: {key[:50]}...")
11                break

Path JSON yang tepat kadang berubah saat TripAdvisor memperbarui frontend, tapi struktur umumnya — JSON-LD untuk data ringkas, urqlCache untuk data detail — sudah stabil selama bertahun-tahun.

Reverse-Engineering API GraphQL TripAdvisor (Advanced)

Untuk ekstraksi skala besar, endpoint GraphQL TripAdvisor mengembalikan data terstruktur secara langsung. Ini metode tercepat, tapi juga paling butuh perawatan.

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

Untuk mengambil ulasan lewat GraphQL:

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

Catatan penting: Nilai preRegisteredQueryId (seperti 84b17ed122fbdbd4 untuk pencarian dan ef1a9f94012220d3 untuk ulasan) bisa rusak ketika TripAdvisor redeploy. Kalau itu terjadi, request Anda bisa gagal diam-diam. Anda perlu menemukan ulang query ID ini dengan memantau request jaringan di DevTools browser.

Kenapa Metode Ini Mengurangi Kebutuhan Proxy

Logikanya simpel. Dengan requests+BS4, scraping 100 halaman detail hotel berarti 100 request. Dengan metode hidden JSON, setiap request sudah ngasih semua data yang Anda butuhkan dari satu page load — tanpa request tambahan untuk membuka ulasan atau memuat konten dinamis. Dengan GraphQL, satu API call bisa mengembalikan 20 ulasan sekaligus. Lebih sedikit request = lebih kecil paparan ke rate limiting = lebih sedikit kebutuhan rotasi proxy. Untuk proyek skala kecil sampai menengah (di bawah 1.000 halaman), Anda mungkin tidak butuh proxy sama sekali kalau memberi jeda yang masuk akal.

Scrape Hotel, Restoran, dan Atraksi dengan Satu Script yang Bisa Dipakai Ulang

Empat dari lima panduan pesaing cuma bahas hotel. Padahal TripAdvisor punya tiga kategori konten utama, dan pola URL serta field datanya beda di tiap kategori. Ini cara membangun satu function yang bisa menangani semuanya.

Field Data yang Tersedia per Kategori

FieldHotelsRestaurantsAttractions
Name
Rating
Review count
Price/Price rangeSometimes
Address
Cuisine type
Duration/Tour type
Amenities
Coordinates

Membangun Function scrape_tripadvisor() yang Bisa Dipakai Ulang

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

Satu function, tiga kategori, nol duplikasi kode. Kalau TripAdvisor mengubah selector, Anda cukup memperbaikinya di satu tempat.

Apa yang Harus Dilakukan Saat TripAdvisor Memblokir Anda (Troubleshooting Anti-Bot)

Ini bagian yang paling saya butuhkan waktu pertama kali scraping TripAdvisor, dan juga bagian yang paling jarang disusun rapi di panduan lain. TripAdvisor memakai DataDome (menganalisis per hari) dan Cloudflare WAF secara bersamaan. Berikut tabel diagnosis untuk mode gagal yang paling umum:

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

Retry Logic dengan Exponential Backoff

Tidak banyak artikel pesaing yang benar-benar nunjukin kode ini. Berikut function retry yang bisa dipakai ulang:

1import time
2import random
3import requests
4def fetch_with_retry(session, url, max_retries=4, base_delay=2, max_delay=60):
5    """
6    Ambil URL dengan exponential backoff dan jitter.
7    Rotasi User-Agent di setiap retry.
8    """
9    user_agents = [
10        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
11        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
12        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
13        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
14    ]
15    for attempt in range(max_retries):
16        # Rotasi User-Agent saat retry
17        if attempt &gt; 0:
18            session.headers["User-Agent"] = random.choice(user_agents)
19        try:
20            response = session.get(url, timeout=30)
21            if response.status_code == 200:
22                return response
23            if response.status_code == 429:
24                # Hormati header Retry-After jika ada
25                retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
26                print(f"  Kena rate limit (429). Menunggu {retry_after}s...")
27                time.sleep(retry_after)
28                continue
29> This paragraph contains content that cannot be parsed and has been skipped.
30            # Kode error lain — jangan retry
31            print(f"  Status tak terduga {response.status_code} untuk {url}")
32            return response
33> This paragraph contains content that cannot be parsed and has been skipped.
34    print(f"  Semua {max_retries} retry habis untuk {url}")
35    return None

Rotasi Header, Proxy, dan Sesi

Untuk scraping berkelanjutan, siapkan kumpulan header dan rotasikan secara bersamaan:

1import random
2> This paragraph contains content that cannot be parsed and has been skipped.
3PROXY_LIST = [
4    "http://user:pass@residential-proxy-1:port",
5    "http://user:pass@residential-proxy-2:port",
6    # Tambahkan lebih banyak residential proxy
7]
8def get_rotated_session():
9    """Buat sesi baru dengan header dan proxy yang dirotasi."""
10    session = requests.Session()
11> This paragraph contains content that cannot be parsed and has been skipped.
12> This paragraph contains content that cannot be parsed and has been skipped.
13    return session

Jenis proxy itu penting. Datacenter proxy biasanya langsung diblokir TripAdvisor (HTTP 1020 Access Denied). Residential proxy wajib dipakai untuk scraping berkelanjutan — trafiknya lewat ISP konsumen sehingga kelihatan seperti user asli. Biaya yang umum: sekitar $2,50–$8,40/GB tergantung penyedia.

Mengekspor dan Menyimpan Data TripAdvisor yang Sudah Di-scrape

Setelah datanya terkumpul, mengubahnya jadi format yang bisa dipakai cukup gampang.

Ekspor CSV (Paling Umum)

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

encoding='utf-8-sig' itu penting — supaya Excel bisa menampilkan karakter non-Latin dengan benar (aksen Prancis, huruf Mandarin, dan lain-lain) saat membuka CSV.

Ekspor JSON (Untuk Data Bertingkat)

Kalau ulasan tersusun di bawah hotel, JSON menjaga hierarkinya:

1# Struktur hierarkis
2hotel_data = {
3    "property_id": "d194317",
4    "name": "NH City Centre Amsterdam",
5    "rating": 4.0,
6    "reviews": [
7        {"title": "Great location", "rating": 5, "date": "2025-03-15", "text": "..."},
8        {"title": "Average stay", "rating": 3, "date": "2025-03-10", "text": "..."},
9    ]
10}
11# Untuk analisis datar, gunakan json_normalize
12flat_reviews = pd.json_normalize(
13    hotel_data,
14    record_path="reviews",
15    meta=["property_id", "name"]
16)
17flat_reviews.to_csv("reviews_flat.csv", index=False)

Pendekatan Dua File untuk Data Relasional

Untuk dataset besar, saya biasanya pakai dua file CSV:

  • hotels.csv — satu baris per properti (flat)
  • reviews.csv — satu baris per ulasan, dengan property_id sebagai foreign key

Ini memudahkan join di pandas, load ke database, atau import ke alat BI.

Kalau Anda tidak mau repot dengan logika ekspor seperti ini, Thunderbit memungkinkan Anda ke Excel, Google Sheets, Airtable, atau Notion — semuanya gratis dan tanpa kode. Sangat berguna kalau Anda perlu berbagi hasil dengan rekan tim yang non-teknis.

Tips Scraping TripAdvisor yang Bertanggung Jawab dan Efisien

Scraping yang bertanggung jawab dalam enam poin:

  • Periksa robots.txt: robots.txt TripAdvisor memblokir bot pelatihan AI (GPTBot, ClaudeBot, dan sejenisnya) sepenuhnya. Crawler standar menghadapi pembatasan path tertentu. Lihat di tripadvisor.com/robots.txt.
  • Tambahkan jeda: 3–7 detik antar request adalah rentang aman. Terlalu cepat, di atas 10–15 request per menit per IP, akan memicu rate limiting.
  • Scrape hanya data publik. Jangan login untuk mengakses konten yang dibatasi.
  • Simpan data dengan aman dan patuhi GDPR/CCPA kalau menangani informasi pribadi (nama pemberi ulasan, dll.).
  • Pertimbangkan API resmi TripAdvisor jika Anda butuh data skala komersial. menyediakan akses ke detail bisnis plus hingga 5 ulasan dan 5 foto per lokasi — terbatas, tapi legal dan stabil.
  • Pahami konteks hukumnya: memperkuat larangan scraping berbasis ToS di seluruh Uni Eropa. Syarat Layanan TripAdvisor secara eksplisit melarang scraping. Lakukan dengan hati-hati dan tanggung risiko sendiri.

Penutup

Itu gambaran lengkapnya.

  • Requests + BeautifulSoup adalah jalur paling sederhana. Cocok untuk halaman listing statis, setup minimal, dan cepat. Mulailah dari sini kalau Anda scraping kurang dari 100 halaman dan tidak butuh konten yang dirender JavaScript.
  • Selenium menangani semua yang tidak bisa dilakukan requests: konten dinamis, tombol "Read more", banner cookie. Memang 5x lebih lambat dan boros resource, tapi satu-satunya opsi kalau Anda perlu interaksi dengan halaman.
  • Hidden JSON / GraphQL adalah pendekatan paling bersih dan paling cepat. Ia memberi data terstruktur tanpa parsing HTML, mengurangi jumlah request (dan kebutuhan proxy), serta mengembalikan data dalam format yang siap dianalisis. Kekurangannya: perlu reverse-engineering awal yang lebih banyak dan maintenance sesekali kalau struktur data TripAdvisor berubah.

Function scrape_tripadvisor() yang bisa dipakai ulang mencakup hotel, restoran, dan atraksi. Seharusnya Anda tidak perlu tutorial kedua.

Dan kalau di tengah tutorial ini Anda sadar coding memang bukan jalan Anda — atau Anda cuma butuh 50 hotel dalam spreadsheet sebelum sore — bisa menyelesaikannya dalam dua klik dengan deteksi field berbasis AI, pagination otomatis, dan ekspor gratis ke Excel atau Google Sheets. Tanpa perlu Python.

Kalau ingin belajar lebih dalam, kami punya lebih banyak panduan scraping di dan .

FAQ

1. Apakah legal scrape TripAdvisor?

Syarat Layanan TripAdvisor secara eksplisit melarang scraping. Namun, pengadilan umumnya menilai bahwa scraping data yang tersedia publik (tidak di balik login) tidak melanggar Computer Fraud and Abuse Act di AS. Meski begitu, putusan EU Court Ryanair tahun 2025 memperketat pembatasan berbasis ToS di Eropa. Ambil hanya data publik, hormati robots.txt, jangan memublikasikan ulang konten berhak cipta, dan konsultasikan dengan penasihat hukum kalau data dipakai secara komersial.

2. Bisa scrape TripAdvisor tanpa Python?

Bisa. Alat no-code seperti bisa scrape TripAdvisor langsung dari browser Anda dengan deteksi field berbasis AI dan pagination otomatis. Anda juga bisa memakai ekstensi browser, add-on Google Sheets, atau API scraping komersial. Python memberi kontrol dan fleksibilitas paling besar, tapi bukan satu-satunya pilihan.

3. Bagaimana cara menghindari blokir saat scraping TripAdvisor?

Taktik utamanya: gunakan header yang realistis dan konsisten (terutama User-Agent dan Sec-CH-UA), rotasi residential proxy (IP datacenter biasanya langsung diblokir), beri jeda acak 3–7 detik antar request, pakai metode hidden JSON untuk meminimalkan jumlah request, terapkan retry logic dengan exponential backoff, dan hangatkan sesi dengan buka homepage dulu sebelum mengambil halaman yang lebih dalam.

4. Data apa saja yang bisa di-scrape dari TripAdvisor?

Hotel, restoran, dan atraksi — termasuk nama, rating, jumlah ulasan, rentang harga, alamat, koordinat, fasilitas (hotel), jenis masakan (restoran), durasi tur (atraksi), dan teks ulasan lengkap beserta rating individu dan tanggal. Pendekatan hidden JSON dan GraphQL biasanya memberi data paling kaya per request.

5. Berapa banyak halaman TripAdvisor yang bisa saya scrape per hari?

Dengan satu IP dan jeda yang wajar: sekitar 600–1.000 halaman per hari. Dengan 20 residential proxy yang dirotasi: kira-kira 200.000–300.000 halaman per hari menggunakan pendekatan berbasis request. Selenium lebih lambat — perkirakan 8.000–12.000 halaman per hari per proxy. Pendekatan hidden JSON/GraphQL memberi data paling banyak per request, jadi Anda mungkin perlu jauh lebih sedikit halaman untuk mendapat jumlah informasi yang sama.

Pelajari Lebih Lanjut

Coba Thunderbit

Ekstrak leads & data lainnya hanya dengan 2 klik. Didukung AI.

Dapatkan Thunderbit Gratis
Ekstrak Data dengan AI
Mudah transfer data ke Google Sheets, Airtable, atau Notion
PRODUCT HUNT#1 Product of the Week