Την περασμένη εβδομάδα προσπάθησα να τραβήξω βαθμολογίες ξενοδοχείων και αριθμούς κριτικών για περίπου 200 καταλύματα σε τρεις ευρωπαϊκές πόλεις από το TripAdvisor. Το πρώτο μου script — ένα απλό requests.get() με τα default headers — γύρναγε σε κάθε αίτημα ένα ωραιότατο 403 Forbidden. Ούτε ένα byte χρήσιμων δεδομένων.
Το TripAdvisor είναι μία από τις πιο πλούσιες δημόσιες πηγές δεδομένων στον τουρισμό: πάνω από 1 δισεκατομμύριο κριτικές, 8+ εκατομμύρια επαγγελματικές καταχωρίσεις και περίπου 460 εκατομμύρια μοναδικοί επισκέπτες κάθε μήνα. Επηρεάζει πάνω από $60 δισεκατομμύρια σε ετήσιες ταξιδιωτικές δαπάνες. Αλλά η προγραμματισμένη πρόσβαση σε αυτά τα δεδομένα; Εκεί αρχίζει το ζόρι. Το TripAdvisor χρησιμοποιεί DataDome για ανίχνευση bot, Cloudflare WAF, TLS fingerprinting και JavaScript challenges — ένα πολυεπίπεδο αμυντικό σύστημα που σταματά τις περισσότερες αφελείς προσπάθειες scraping πριν καν ξεκινήσουν. Αυτός ο οδηγός είναι η μία και μοναδική πηγή που θα ήθελα να είχα: μια άμεση σύγκριση τριών προσεγγίσεων scraping με Python (συν μία no-code επιλογή), πλήρη κώδικα για καθεμία, μια οργανωμένη ενότητα αντιμετώπισης anti-bot προβλημάτων και επαναχρησιμοποιήσιμα patterns που δουλεύουν σε ξενοδοχεία, εστιατόρια και αξιοθέατα. Είτε είστε αρχάριος στην Python είτε έμπειρος developer, αυτό το άρθρο θα σας γλιτώσει από πολλές άσκοπες 403 απαντήσεις.
Δεν θέλετε να γράψετε κώδικα; Κάντε Scrape στο TripAdvisor με τον εύκολο τρόπο
Θέλω να είμαι ξεκάθαρος: πολλοί από όσους ψάχνουν “scrape TripAdvisor με Python” δεν είναι στην πραγματικότητα δεμένοι με την ιδέα να γράψουν κώδικα. Απλώς θέλουν τα δεδομένα — ονόματα ξενοδοχείων, βαθμολογίες, αριθμό κριτικών, τιμές — γρήγορα μέσα σε ένα spreadsheet. Αν αυτό σας ταιριάζει, υπάρχει πολύ πιο σύντομος δρόμος.
Το Thunderbit είναι ένα Chrome extension με AI που φτιάξαμε και μπορεί να διαβάσει οποιαδήποτε σελίδα του TripAdvisor και να προτείνει αυτόματα τις σωστές στήλες για εξαγωγή. Η ροή είναι πραγματικά δύο κλικ:
- Ανοίξτε μια σελίδα καταχωρίσεων του TripAdvisor (π.χ. αποτελέσματα αναζήτησης “Hotels in Paris”).
- Κάντε κλικ στο “AI Suggest Fields” από το sidebar του Thunderbit. Το AI σαρώνει τη σελίδα και προτείνει στήλες όπως Hotel Name, Rating, Review Count, Price και Location.
- Κάντε κλικ στο “Scrape.” Το Thunderbit εξάγει δεδομένα από κάθε καταχώριση της σελίδας — και χειρίζεται αυτόματα και το pagination αν χρειάζεστε περισσότερα αποτελέσματα.
- Εξαγάγετε σε Excel, Google Sheets, Airtable ή Notion. Οι εξαγωγές είναι δωρεάν σε όλα τα πακέτα.
Το Thunderbit δουλεύει σε ξενοδοχεία, εστιατόρια και αξιοθέατα χωρίς να αλλάξετε τίποτα στις ρυθμίσεις — το AI προσαρμόζεται σε ό,τι βλέπει στη σελίδα. Για αποτελέσματα με πολλές σελίδες, εντοπίζει αυτόματα κουμπιά “Next” και infinite scroll. Και επειδή τρέχει μέσα στο πραγματικό σας Chrome browser, κληρονομεί τα session cookies και το browser fingerprint σας, κάτι που του δίνει φυσικό πλεονέκτημα απέναντι στην ανίχνευση bot.
Μπορείτε να το δοκιμάσετε με το Thunderbit Chrome Extension — το δωρεάν πλάνο δίνει 6 σελίδες/μήνα, αρκετές για να τεστάρετε τη ροή.
Αν χρειάζεστε προγραμματιστικό έλεγχο, custom λογική parsing ή σκοπεύετε να κάνετε scrape σε 10.000+ σελίδες, τότε η Python είναι η σωστή επιλογή. Συνεχίστε να διαβάζετε.
Γιατί να κάνετε Scrape στο TripAdvisor με Python;
Τα δεδομένα του TripAdvisor έχουν άμεσο και μετρήσιμο επιχειρηματικό αντίκτυπο. Μια μελέτη του Cornell University έδειξε ότι αύξηση κατά 1 μονάδα στον Global Review Index ενός ξενοδοχείου σε κλίμακα 100 οδηγεί σε αύξηση 0,89% στη μέση ημερήσια τιμή και 1,42% στο Revenue Per Available Room. Μια ξεχωριστή μελέτη στο ScienceDirect έδειξε ότι μια εξωγενής αύξηση 1 αστεριού στη βαθμολογία TripAdvisor μεταφράζεται σε επιπλέον ετήσια έσοδα $55.000–$75.000 για ένα μέσο ξενοδοχείο. Οι κριτικές δεν είναι απλώς δείκτες εντυπώσεων — είναι μοχλός εσόδων.
Δείτε πώς χρησιμοποιούν τα δεδομένα του TripAdvisor διαφορετικές ομάδες:
| Περίπτωση Χρήσης | Ποιοι Ωφελούνται | Απαιτούμενα Δεδομένα |
|---|---|---|
| Ανάλυση ανταγωνισμού ξενοδοχείων | Αλυσίδες ξενοδοχείων, revenue managers | Βαθμολογίες, τιμές, όγκος κριτικών, παροχές |
| Έρευνα αγοράς εστιατορίων | Όμιλοι εστίασης, food brands | Τύποι κουζίνας, εύρος τιμών, sentiment κριτικών |
| Παρακολούθηση τάσεων αξιοθέατων | Tour operators, οργανισμοί τουρισμού | Κατάταξη δημοφιλίας, εποχικά μοτίβα |
| Ανάλυση συναισθήματος | Ερευνητές, data analysts | Πλήρες κείμενο κριτικής, βαθμολογίες, ημερομηνίες |
| Δημιουργία leads | Sales teams, ταξιδιωτικά πρακτορεία | Ονόματα επιχειρήσεων, στοιχεία επικοινωνίας, τοποθεσίες |
Γιατί συγκεκριμένα Python; Τρεις λόγοι. Πρώτον, το οικοσύστημα: BeautifulSoup, Selenium, Playwright, Scrapy, httpx, pandas — η Python έχει πιο ώριμες βιβλιοθήκες scraping και ανάλυσης δεδομένων από οποιαδήποτε άλλη γλώσσα. Δεύτερον, το 71,7% των developers web scraping χρησιμοποιεί Python, άρα υπάρχει περισσότερη υποστήριξη από την κοινότητα, περισσότερες απαντήσεις στο StackOverflow και πιο ενημερωμένοι οδηγοί. Τρίτον, το πλεονέκτημα του pipeline: μπορείτε να κάνετε scrape με BeautifulSoup, καθαρισμό με pandas, sentiment analysis με Hugging Face Transformers και dashboards — όλα στην ίδια γλώσσα. Χωρίς να πηδάτε από περιβάλλον σε περιβάλλον.
Τρεις τρόποι για να κάνετε Scrape στο TripAdvisor με Python (Σύγκριση)
Κάθε ανταγωνιστικός οδηγός διαλέγει μία προσέγγιση και μένει εκεί. Αυτό δεν βοηθά όταν προσπαθείτε να αποφασίσετε πριν γράψετε κώδικα. Ορίστε ο πίνακας σύγκρισης που θα ήθελα να μου είχε δώσει κάποιος:
| Προσέγγιση | Ταχύτητα | Υποστήριξη JS | Αντοχή σε Anti-Bot | Πολυπλοκότητα | Ιδανική για |
|---|---|---|---|---|---|
requests + BeautifulSoup | ⚡ Γρήγορη (~120–200 σελίδες/λεπτό ακατέργαστα) | ❌ Καμία | ⚠️ Χαμηλή | Εύκολη | Στατικές σελίδες καταχωρίσεων, μικρά projects |
| Selenium / Headless Browser | 🐢 Αργή (~8–20 σελίδες/λεπτό) | ✅ Πλήρης | ⚠️ Μεσαία | Μεσαία | Δυναμικό περιεχόμενο, κλικ στο “Read more”, banners cookies |
| Κρυφό JSON / GraphQL API | ⚡⚡ Η πιο γρήγορη (~200–600 σελίδες/λεπτό ακατέργαστα) | N/A | ✅ Υψηλότερη | Δύσκολη | Εξαγωγή κριτικών/ξενοδοχείων σε μεγάλη κλίμακα |
| No-code (Thunderbit) | ⚡ Γρήγορη | ✅ Ενσωματωμένη | ✅ Ενσωματωμένη | Η πιο εύκολη | Μη τεχνικοί χρήστες, γρήγορες εφάπαξ εξαγωγές |
Μερικές σημαντικές επισημάνσεις. Αυτές οι ακατέργαστες ταχύτητες είναι θεωρητικές — τα rate limits του TripAdvisor (~10–15 αιτήματα ανά λεπτό ανά IP) περιορίζουν την πραγματική απόδοση περίπου στις 10 σελίδες/λεπτό ανά IP, ανεξάρτητα από την προσέγγιση. Η μέθοδος με κρυφό JSON σας δίνει περισσότερα δεδομένα ανά αίτημα, άρα χρειάζεστε λιγότερα συνολικά αιτήματα και εκτίθεστε λιγότερο σε rate limiting. Το Selenium είναι 5 φορές πιο αργό από τις request-based προσεγγίσεις σε πραγματικές συνθήκες, αλλά είναι η μόνη επιλογή όταν πρέπει να πατήσετε κουμπιά ή να αποδώσετε JavaScript.
Το υπόλοιπο άρθρο περνά και από τις τρεις μεθόδους σε Python με πλήρη κώδικα. Επιλέξτε αυτή που ταιριάζει στην περίπτωσή σας ή συνδυάστε τες (συχνά χρησιμοποιώ requests+BS4 για σελίδες καταχωρίσεων και κρυφό JSON για detail pages).
Ρύθμιση του περιβάλλοντος Python
Πριν ξεκινήσουμε, ας στήσουμε το περιβάλλον. Θα χρειαστείτε Python 3.10+ (προτείνω 3.12 ή 3.13 — όλα τα βασικά πακέτα τα υποστηρίζουν χωρίς γνωστά προβλήματα).
Εγκαταστήστε τα όλα μαζί:
pip install requests beautifulsoup4 selenium httpx parsel pandas curl-cffi
Σημειώσεις πακέτων:
requests(2.33.1) — HTTP requests, απαιτεί Python 3.10+beautifulsoup4(4.14.3) — parsing HTMLselenium(4.43.0) — αυτοματοποίηση browser, απαιτεί Python 3.10+httpx(0.28.1) — async HTTP clientparsel(1.11.0) — selectors CSS/XPath (πιο ελαφρύ από το BS4)pandas(3.0.2) — εξαγωγή δεδομένων, απαιτεί Python 3.11+curl_cffi(0.15.0) — impersonation TLS fingerprint (κρίσιμο για παράκαμψη του Cloudflare)
ChromeDriver: Αν χρησιμοποιείτε Selenium, καλά νέα — από το Selenium 4.6+ το Selenium Manager κατεβάζει και αποθηκεύει αυτόματα το σωστό ChromeDriver binary. Δεν χρειάζεται χειροκίνητη εγκατάσταση. Η αντιστοίχιση έκδοσης γίνεται δυναμικά, οπότε δεν χρειάζεται να ανησυχείτε για ασυμβατότητες με την έκδοση του Chrome.
Virtual environment (προτεινόμενο):
python -m venv tripadvisor-scraper
source tripadvisor-scraper/bin/activate # macOS/Linux
tripadvisor-scraper\Scripts\activate # Windows
Προσέγγιση 1: Scrape στο TripAdvisor με Requests και BeautifulSoup
Αυτή είναι η πιο απλή προσέγγιση. Λειτουργεί καλά για scraping σελίδων καταχωρίσεων (αποτελέσματα αναζήτησης ξενοδοχείων, λίστες εστιατορίων) όπου τα δεδομένα που χρειάζεστε βρίσκονται στο στατικό HTML. Χωρίς browser, χωρίς απόδοση JavaScript, με ελάχιστη κατανάλωση πόρων.
Κατανόηση των προτύπων URL του TripAdvisor
Τα URLs του TripAdvisor ακολουθούν προβλέψιμα μοτίβα ανά κατηγορία:
- 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 χρησιμοποιεί την παράμετρο oa (offset anchors), η οποία μπαίνει στο URL. Κάθε σελίδα δείχνει 30 αποτελέσματα:
- Σελίδα 1: βασικό URL (χωρίς παράμετρο
oa) - Σελίδα 2:
Hotels-g187768-oa30-Italy-Hotels.html - Σελίδα 3:
Hotels-g187768-oa60-Italy-Hotels.html
Για τις σελίδες κριτικών, η παράμετρος offset είναι or με βήμα 10:
- Σελίδα 1:
Reviews-or0-Hotel_Name.html - Σελίδα 2:
Reviews-or10-Hotel_Name.html
Για να λάβετε κριτικές σε όλες τις γλώσσες, προσθέστε ?filterLang=ALL στο URL.
Αποστολή αιτημάτων με ρεαλιστικά headers
Το TripAdvisor ελέγχει πολύ επιθετικά τα headers. Ένα request με τα default Python headers κόβεται αμέσως. Πρέπει να μιμηθείτε έναν πραγματικό Chrome browser:
import requests
import time
import random
session = requests.Session()
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Referer": "https://www.tripadvisor.com/",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-CH-UA": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"Sec-CH-UA-Mobile": "?0",
"Sec-CH-UA-Platform": '"Windows"',
}
session.headers.update(headers)
url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
response = session.get(url)
print(f"Status: {response.status_code}")
print(f"Content length: {len(response.text)} characters")
Βασική λεπτομέρεια: Το TripAdvisor ελέγχει ότι τα User-Agent και Sec-CH-UA Client Hints headers είναι συνεπή. Αν δηλώνετε ότι είστε Chrome 135 στο User-Agent αλλά το Sec-CH-UA δείχνει Chrome 120, θα σας σημειώσει ως ύποπτους. Να κάνετε πάντα rotation σε ολόκληρα header sets μαζί, όχι σε μεμονωμένα headers.
Parsing των καταχωρίσεων με BeautifulSoup
Αφού πάρετε κανονικά την απάντηση, εξάγετε τα δεδομένα με BeautifulSoup. Το TripAdvisor χρησιμοποιεί χαρακτηριστικά data-automation και data-test-attribute που είναι πολύ πιο σταθερά από τα CSS class names (τα οποία αλλάζουν συχνά):
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, "html.parser")
# Βρες όλες τις κάρτες καταχωρίσεων ξενοδοχείων
cards = soup.select('div[data-test-attribute="location-results-card"]')
hotels = []
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
# Σύνδεσμος προς τη σελίδα λεπτομερειών
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
# Βαθμολογία
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
hotels.append({
"name": name,
"rating": rating,
"review_count": review_count,
"url": link,
})
print(f"Βρέθηκαν {len(hotels)} ξενοδοχεία σε αυτή τη σελίδα")
for h in hotels[:3]:
print(h)
Σημείωση για τους selectors: Το TripAdvisor χρησιμοποιεί obfuscated CSS class names (όπως FGwzt, yyzcQ) που αλλάζουν με κάθε update του site. Τα data-automation και data-test-target attributes είναι πολύ πιο σταθερά. Προτιμάτε πάντα data attributes αντί για class names.
Χειρισμός της pagination
Για να κάνετε scrape σε πολλές σελίδες, βάλτε loop στην παράμετρο offset με ένα ευγενικό διάλειμμα ανάμεσα στα requests:
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): # Πρώτες 5 σελίδες
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"Σελίδα {page + 1}: λήφθηκε status {response.status_code}, διακοπή.")
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"Σελίδα {page + 1}: βρέθηκαν {len(cards)} ξενοδοχεία")
time.sleep(random.uniform(3, 7)) # Τυχαίο διάλειμμα για αποφυγή rate limiting
df = pd.DataFrame(all_hotels)
print(f"\nΣυνολικά ξενοδοχεία που έγινε scrape: {len(df)}")
Το time.sleep(random.uniform(3, 7)) είναι σημαντικό. Το όριο rate limit του TripAdvisor είναι περίπου 10–15 αιτήματα το λεπτό ανά IP. Αν πάτε πιο γρήγορα, ενεργοποιούνται CAPTCHA ή 429 errors.
Περιορισμοί αυτής της προσέγγισης
Πού κολλάει αυτή η μέθοδος; Το requests+BS4 αποτυγχάνει όταν:
- Το TripAdvisor σερβίρει περιεχόμενο που αποδίδεται με JavaScript (ορισμένες σελίδες αποτελεσμάτων θέλουν JS)
- Το κείμενο των κριτικών κρύβεται πίσω από κουμπιά “Read more”
- Τα anti-bot μέτρα ανεβαίνουν σε JavaScript challenges ή CAPTCHA
- Χρειάζεστε δεδομένα που εμφανίζονται μόνο μετά από client-side rendering (τιμές, διαθεσιμότητα)
Για αυτά τα σενάρια, χρειάζεστε είτε Selenium (Προσέγγιση 2) είτε τη μέθοδο με κρυφό JSON (Προσέγγιση 3).
Προσέγγιση 2: Scrape στο TripAdvisor με Selenium (Headless Browser)
Το Selenium ανοίγει έναν πραγματικό browser, πράγμα που σημαίνει ότι μπορεί να αποδώσει JavaScript, να πατήσει κουμπιά, να χειριστεί banners συναίνεσης cookies και να αλληλεπιδράσει με δυναμικό περιεχόμενο. Το κόστος: είναι περίπου 5 φορές πιο αργό και χρησιμοποιεί 300–500MB RAM ανά browser instance.
Ρύθμιση του Selenium με anti-detection επιλογές
Από μόνο του το Selenium εντοπίζεται εύκολα. Το fingerprinting του TripAdvisor το πιάνει κατευθείαν. Πρέπει να απενεργοποιήσετε flags αυτοματοποίησης:
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") # Χρησιμοποιήστε το νέο headless mode (Chrome 112+)
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("--window-size=1920,1080")
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
driver = webdriver.Chrome(options=options)
# Αφαιρέστε την ιδιότητα webdriver από το navigator
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
})
Αρκεί αυτό για το TripAdvisor; Για scraping μικρής κλίμακας (κάτω από 50 σελίδες), αυτό το setup με residential proxies συνήθως δουλεύει. Για μεγαλύτερους όγκους, ίσως χρειαστείτε undetected-chromedriver ή nodriver — η προστασία DataDome του TripAdvisor αναλύει πάνω από 1.000 signals ανά αίτημα, συμπεριλαμβανομένων TLS fingerprints που το vanilla Selenium δεν μπορεί να μιμηθεί.
Scraping των αποτελεσμάτων αναζήτησης ξενοδοχείων με Selenium
import time
import random
url = "https://www.tripadvisor.com/Hotels-g187147-Paris_Ile_de_France-Hotels.html"
driver.get(url)
# Περιμένετε να φορτώσουν οι κάρτες ξενοδοχείων
wait = WebDriverWait(driver, 15)
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div[data-test-attribute="location-results-card"]')))
# Χειριστείτε το popup συναίνεσης cookies (αν εμφανιστεί)
try:
cookie_btn = driver.find_element(By.ID, "onetrust-accept-btn-handler")
cookie_btn.click()
time.sleep(1)
except:
pass # Δεν υπάρχει popup cookies
# Εξαγωγή δεδομένων ξενοδοχείων
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"Έγινε scrape σε {len(hotels)} ξενοδοχεία")
for h in hotels[:3]:
print(h)
Αυτό πήρε περίπου 8 δευτερόλεπτα για μία σελίδα στο δικό μου μηχάνημα — σε σύγκριση με λιγότερο από 1 δευτερόλεπτο με requests+BS4. Αυτή η διαφορά 8x μεγαλώνει γρήγορα όταν κάνετε scrape σε εκατοντάδες σελίδες.
Άνοιγμα του “Read More” και scraping πλήρων κριτικών
Οι σελίδες κριτικών κόβουν τις μεγάλες κριτικές πίσω από ένα κουμπί “Read more”. Το Selenium μπορεί να το πατήσει:
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)
# Πατήστε όλα τα κουμπιά “Read more”
read_more_buttons = driver.find_elements(By.XPATH, '//button//*[contains(text(), "Read more")]/..')
for btn in read_more_buttons:
try:
driver.execute_script("arguments[0].click();", btn)
time.sleep(0.3)
except:
pass
# Εξαγωγή κριτικών
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")
# Η βαθμολογία κωδικοποιείται σε class όπως "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"Έγινε scrape σε {len(reviews)} κριτικές")
Προσθήκη proxy rotation στο Selenium
Για συνεχή scraping θα χρειαστείτε rotation proxies. Αφού το selenium-wire είναι deprecated από τον Ιανουάριο του 2024, χρησιμοποιήστε τη built-in υποστήριξη proxy του Chrome:
# Με proxy χωρίς authentication
proxy = "http://your-proxy-address:port"
options.add_argument(f"--proxy-server={proxy}")
# Για proxies με authentication, χρησιμοποιήστε Chrome extension ή το BiDi protocol του Selenium 4
Για rotating proxies προγραμματιστικά, δημιουργήστε νέο driver instance με διαφορετικό proxy για κάθε batch αιτημάτων. Δεν είναι το πιο κομψό, αλλά είναι αξιόπιστο.
Προσέγγιση 3: Η μέθοδος με κρυφό JSON (χωρίς parsing HTML)
Οι περισσότεροι οδηγοί την παραλείπουν εντελώς αυτή τη μέθοδο, και είναι κρίμα — είναι η πιο γρήγορη και καθαρή από τις τρεις. Το TripAdvisor ενσωματώνει δομημένα δεδομένα ως JSON απευθείας στις σελίδες του — μέσα σε <script> tags ως JavaScript variables όπως pageManifest και urqlCache. Η εξαγωγή αυτού του JSON σας δίνει καθαρότερα δεδομένα (βαθμολογίες ως αριθμούς, ημερομηνίες σε ISO format) με λιγότερα αιτήματα και χωρίς ανάγκη απόδοσης JavaScript.
Εντοπισμός του ενσωματωμένου JSON στον πηγαίο κώδικα
Το βασικό insight: μπορείτε να χρησιμοποιήσετε ένα απλό requests.get() για να φέρετε τη σελίδα και μετά να εξαγάγετε το JSON από το raw HTML χωρίς να αποδώσετε ποτέ JavaScript.
import requests
import re
import json
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Referer": "https://www.tripadvisor.com/",
"Sec-CH-UA": '"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
"Sec-CH-UA-Mobile": "?0",
"Sec-CH-UA-Platform": '"macOS"',
}
url = "https://www.tripadvisor.com/Hotel_Review-g188590-d194317-Reviews-NH_City_Centre_Amsterdam.html"
response = requests.get(url, headers=headers)
# Εξαγωγή του JSON blob pageManifest
match = re.search(r"pageManifest:({.+?})};", response.text)
if match:
page_data = json.loads(match.group(1))
print("Βρέθηκαν δεδομένα pageManifest")
print(f"Keys: {list(page_data.keys())[:10]}")
Πώς να βρείτε μόνοι σας το όνομα της μεταβλητής: Ανοίξτε οποιαδήποτε σελίδα ξενοδοχείου του TripAdvisor στο Chrome, κάντε δεξί κλικ → View Page Source, και μετά Ctrl+F για pageManifest ή urqlCache ή aggregateRating. Τα δεδομένα υπάρχουν εκεί και περιμένουν να τα αναλύσετε.
Parsing του JSON και εξαγωγή δομημένων δεδομένων
Το TripAdvisor ενσωματώνει επίσης schema.org application/ld+json που είναι ακόμα πιο εύκολο να το τραβήξετε:
from parsel import Selector
sel = Selector(text=response.text)
# Εξαγωγή JSON-LD δομημένων δεδομένων
json_ld_scripts = sel.xpath("//script[@type='application/ld+json']/text()").getall()
for script in json_ld_scripts:
data = json.loads(script)
if isinstance(data, dict) and data.get("@type") in ["Hotel", "Restaurant", "TouristAttraction"]:
print(f"Όνομα: {data.get('name')}")
print(f"Βαθμολογία: {data.get('aggregateRating', {}).get('ratingValue')}")
print(f"Αριθμός κριτικών: {data.get('aggregateRating', {}).get('reviewCount')}")
print(f"Εύρος τιμών: {data.get('priceRange')}")
print(f"Διεύθυνση: {data.get('address', {}).get('streetAddress')}")
print(f"Συντεταγμένες: {data.get('geo', {}).get('latitude')}, {data.get('geo', {}).get('longitude')}")
break
Τα JSON-LD δεδομένα είναι ενσωματωμένα στο στατικό HTML και ΔΕΝ χρειάζονται JavaScript rendering. Σας δίνουν το όνομα του καταλύματος, τη συνολική βαθμολογία, τον αριθμό κριτικών, τη διεύθυνση, τις συντεταγμένες, το εύρος τιμών και τις φωτογραφίες — όλα χωρίς να χρειαστεί να κάνετε parsing ούτε ένα HTML tag.
Για πιο πλούσια δεδομένα (μεμονωμένες κριτικές, αναλυτική κατανομή βαθμολογιών, λίστες παροχών), χρειάζεστε το αντικείμενο urqlCache:
# Εξαγωγή του urqlCache για λεπτομερή δεδομένα κριτικών
cache_match = re.search(r'"urqlCache"\s*:\s*({.+?})\s*,\s*"redux"', response.text)
if cache_match:
cache_data = json.loads(cache_match.group(1))
# Πλοηγηθείτε στο cache για να βρείτε δεδομένα κριτικών
for key, value in cache_data.items():
if "reviews" in str(value).lower()[:100]:
reviews_data = json.loads(value.get("data", "{}")) if isinstance(value, dict) else None
if reviews_data:
print(f"Βρέθηκε καταχώριση cache για κριτικές: {key[:50]}...")
break
Οι ακριβείς διαδρομές JSON αλλάζουν πού και πού όταν το TripAdvisor ανανεώνει το frontend του, αλλά η γενική δομή — JSON-LD για συνοπτικά δεδομένα, urqlCache για πιο αναλυτικά δεδομένα — είναι σταθερή εδώ και χρόνια.
Αντίστροφη ανάλυση του GraphQL API του TripAdvisor (Προχωρημένο)
Για εξαγωγή μεγάλης κλίμακας, τα GraphQL endpoints του TripAdvisor επιστρέφουν δομημένα δεδομένα απευθείας. Αυτή είναι η ταχύτερη μέθοδος αλλά απαιτεί τη μεγαλύτερη συντήρηση.
import httpx
import random
import string
def generate_request_id():
"""Δημιουργεί την τιμή του X-Requested-By header"""
random_chars = ''.join(random.choices(string.ascii_letters + string.digits, k=180))
return f"TNI1625!{random_chars}"
# Αναζήτηση ξενοδοχείων στο Παρίσι
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 request απέτυχε: {response.status_code}")
Για λήψη κριτικών μέσω GraphQL:
review_payload = [{
"variables": {
"locationId": 194317, # NH City Centre Amsterdam
"offset": 0,
"limit": 20,
"filters": {},
"sortType": None,
"sortBy": "date",
"language": "en",
"doMachineTranslation": False,
"photosPerReviewLimit": 3
},
"extensions": {
"preRegisteredQueryId": "ef1a9f94012220d3"
}
}]
with httpx.Client() as client:
response = client.post(
"https://www.tripadvisor.com/data/graphql/ids",
json=review_payload,
headers=graphql_headers
)
if response.status_code == 200:
data = response.json()
reviews = data[0]["data"]["locations"][0]["reviewListPage"]["reviews"]
total = data[0]["data"]["locations"][0]["reviewListPage"]["totalCount"]
print(f"Συνολικές κριτικές: {total}")
for r in reviews[:3]:
print(f" [{r['rating']}/5] {r['title']} - {r['createdDate']}")
Σημαντική επιφύλαξη: Τα preRegisteredQueryId values (όπως 84b17ed122fbdbd4 για αναζήτηση και ef1a9f94012220d3 για κριτικές) μπορεί να χαλάσουν όταν το TripAdvisor κάνει redeploy. Όταν συμβεί αυτό, τα requests σας θα αποτυγχάνουν σιωπηλά. Θα χρειαστεί να ξαναβρείτε τα query IDs παρακολουθώντας τα network requests στο DevTools του browser.
Γιατί αυτή η μέθοδος μειώνει την ανάγκη για proxies
Τα μαθηματικά είναι απλά. Με requests+BS4, το scraping 100 σελίδων λεπτομερειών ξενοδοχείων απαιτεί 100 αιτήματα. Με τη μέθοδο κρυφού JSON, κάθε αίτημα επιστρέφει όλα τα δεδομένα που χρειάζεστε από ένα μόνο page load — χωρίς επιπλέον αιτήματα για άνοιγμα κριτικών ή φόρτωση δυναμικού περιεχομένου. Με GraphQL, ένα API call μπορεί να επιστρέψει 20 κριτικές μαζί. Λιγότερα αιτήματα = μικρότερη έκθεση σε rate limiting = μικρότερη ανάγκη για proxy rotation. Για μικρά έως μεσαία projects (κάτω από 1.000 σελίδες), ίσως να μη χρειαστείτε καθόλου proxies αν βάλετε λογικά delays.
Scrape ξενοδοχεία, εστιατόρια και αξιοθέατα με ένα επαναχρησιμοποιήσιμο script
Τέσσερις στους πέντε ανταγωνιστικούς οδηγούς καλύπτουν μόνο ξενοδοχεία. Όμως το TripAdvisor έχει τρεις βασικές κατηγορίες περιεχομένου και τα URL patterns και τα πεδία δεδομένων διαφέρουν μεταξύ τους. Δείτε πώς να φτιάξετε μία συνάρτηση που τα χειρίζεται και τα τρία.
Πεδία δεδομένων που είναι διαθέσιμα ανά κατηγορία
| Πεδία | Ξενοδοχεία | Εστιατόρια | Αξιοθέατα |
|---|---|---|---|
| Όνομα | ✅ | ✅ | ✅ |
| Βαθμολογία | ✅ | ✅ | ✅ |
| Αριθμός κριτικών | ✅ | ✅ | ✅ |
| Τιμή/Εύρος τιμών | ✅ | ✅ | Μερικές φορές |
| Διεύθυνση | ✅ | ✅ | ✅ |
| Τύπος κουζίνας | ❌ | ✅ | ❌ |
| Διάρκεια/Τύπος περιήγησης | ❌ | ❌ | ✅ |
| Παροχές | ✅ | ❌ | ❌ |
| Συντεταγμένες | ✅ | ✅ | ✅ |
Δημιουργία επαναχρησιμοποιήσιμης συνάρτησης scrape_tripadvisor()
import requests
from bs4 import BeautifulSoup
import pandas as pd
import time
import random
import re
import json
def scrape_tripadvisor(category, location_id, location_name, num_pages=3):
"""
Scrape καταχωρίσεων του TripAdvisor για ξενοδοχεία, εστιατόρια ή αξιοθέατα.
Args:
category: "hotels", "restaurants", ή "attractions"
location_id: TripAdvisor geo ID (π.χ. "187147" για Παρίσι)
location_name: URL-friendly όνομα (π.χ. "Paris_Ile_de_France")
num_pages: Αριθμός σελίδων προς scrape
"""
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" Σελίδα {page + 1}: status {response.status_code}, διακοπή.")
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" Σελίδα {page + 1}: βρέθηκαν {len(cards)} στοιχεία")
time.sleep(random.uniform(3, 7))
return pd.DataFrame(all_items)
# Παραδείγματα χρήσης
print("=== Ξενοδοχεία στο Παρίσι ===")
hotels_df = scrape_tripadvisor("hotels", "187147", "Paris_Ile_de_France", num_pages=2)
print(hotels_df.head())
print("\n=== Εστιατόρια στη Ρώμη ===")
restaurants_df = scrape_tripadvisor("restaurants", "187791", "Rome_Lazio", num_pages=2)
print(restaurants_df.head())
print("\n=== Αξιοθέατα στη Βαρκελώνη ===")
attractions_df = scrape_tripadvisor("attractions", "187497", "Barcelona_Catalonia", num_pages=2)
print(attractions_df.head())
Μία συνάρτηση, τρεις κατηγορίες, μηδέν επανάληψη κώδικα. Αν το TripAdvisor αλλάξει κάποιο selector, το διορθώνετε σε ένα μόνο σημείο.
Τι να κάνετε όταν το TripAdvisor σας μπλοκάρει (Anti-Bot Troubleshooting)
Αυτό είναι το κομμάτι που χρειαζόμουν περισσότερο όταν άρχισα να κάνω scraping στο TripAdvisor, και είναι το κομμάτι που κανένας ανταγωνιστικός οδηγός δεν δίνει με δομημένο τρόπο. Το TripAdvisor χρησιμοποιεί DataDome (αναλύοντας 5+ τρισεκατομμύρια σημεία δεδομένων κάθε μέρα) και Cloudflare WAF μαζί. Παρακάτω υπάρχει ένας διαγνωστικός πίνακας για τις πιο συχνές μορφές αποτυχίας:
| Σύμπτωμα | Πιθανή Αιτία | Διόρθωση |
|---|---|---|
| HTTP 403 response | Λείπουν ή είναι ύποπτα τα headers· Cloudflare JS challenge | Ορίστε ρεαλιστικά User-Agent, Accept-Language, Referer και Sec-CH-UA headers. Εξασφαλίστε συνέπεια μεταξύ των headers. |
| Σελίδα CAPTCHA αντί για δεδομένα | Rate limiting ή browser fingerprinting | Κάντε rotation σε residential proxies, προσθέστε τυχαίες καθυστερήσεις (2–7 δευτερόλεπτα μεταξύ requests) |
| Κενό HTML ή άδειο page body | Το JavaScript δεν αποδόθηκε από το requests | Περάστε σε Selenium ή εξαγάγετε από το κρυφό JSON στον πηγαίο κώδικα |
| Μερικές κριτικές / το “Read more” δεν ανοίγει | Το περιεχόμενο φορτώνεται με click event | Χρησιμοποιήστε Selenium .click() ή εξαγάγετε από το ενσωματωμένο JSON blob |
| Οι κριτικές εμφανίζονται μόνο σε μία γλώσσα | Λείπει η παράμετρος γλώσσας | Προσθέστε ?filterLang=ALL στο review URL |
| Τα δεδομένα σταματούν να φορτώνονται μετά από N σελίδες | Rate limit σε επίπεδο session | Κάντε rotation σε sessions, καθαρίστε cookies μεταξύ batches |
| HTTP 1020 Access Denied | Το IP/ASN έχει αποκλειστεί από το Cloudflare | Μετακινηθείτε από datacenter σε residential proxies |
| Challenge loop (ατελείωτο CAPTCHA) | Σπασμένη διατήρηση cookies | Ζεστάνετε τα sessions επισκεπτόμενοι πρώτα την αρχική σελίδα· διατηρήστε cookie jar |
Retry logic με exponential backoff
Κανένα ανταγωνιστικό άρθρο δεν δείχνει πραγματικά αυτόν τον κώδικα. Ορίστε μια επαναχρησιμοποιήσιμη συνάρτηση retry:
import time
import random
import requests
def fetch_with_retry(session, url, max_retries=4, base_delay=2, max_delay=60):
"""
Φέρνει ένα URL με exponential backoff και jitter.
Κάνει rotation στο User-Agent σε κάθε retry.
"""
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):
# Rotation του User-Agent στο retry
if attempt > 0:
session.headers["User-Agent"] = random.choice(user_agents)
try:
response = session.get(url, timeout=30)
if response.status_code == 200:
return response
if response.status_code == 429:
# Σεβαστείτε το Retry-After header αν υπάρχει
retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
print(f" Rate limited (429). Αναμονή {retry_after}s...")
time.sleep(retry_after)
continue
if response.status_code in (403, 503):
wait = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)
print(f" Λήφθηκε {response.status_code}. Επανάληψη {attempt + 1}/{max_retries} σε {wait:.1f}s...")
time.sleep(wait)
continue
# Άλλοι κωδικοί σφάλματος — χωρίς retry
print(f" Απροσδόκητο status {response.status_code} για {url}")
return response
except requests.exceptions.Timeout:
wait = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)
print(f" Timeout. Επανάληψη {attempt + 1}/{max_retries} σε {wait:.1f}s...")
time.sleep(wait)
print(f" Εξαντλήθηκαν όλα τα {max_retries} retries για το {url}")
return None
Rotation σε headers, proxies και sessions
Για συνεχή scraping, κρατήστε ένα pool από header sets και κάντε rotation μαζί:
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",
# Προσθέστε περισσότερα residential proxies
]
def get_rotated_session():
"""Δημιουργεί νέο session με rotated headers και proxy."""
session = requests.Session()
# Επιλέξτε τυχαίο header set
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)
# Επιλέξτε τυχαίο proxy
if PROXY_LIST:
proxy = random.choice(PROXY_LIST)
session.proxies = {"http": proxy, "https": proxy}
return session
Ο τύπος proxy έχει σημασία. Τα datacenter proxies μπλοκάρονται σχεδόν αμέσως από το TripAdvisor (HTTP 1020 Access Denied). Τα residential proxies είναι απαραίτητα για συνεχή scraping — βγαίνουν μέσω consumer ISPs και μοιάζουν πολύ περισσότερο με πραγματικούς χρήστες. Περιμένετε κόστος $2,50–$8,40/GB ανάλογα με τον πάροχο.
Εξαγωγή και αποθήκευση των δεδομένων TripAdvisor που κάνατε scrape
Μόλις έχετε τα δεδομένα, η μετατροπή τους σε χρήσιμη μορφή είναι απλή.
Εξαγωγή σε CSV (η πιο συνηθισμένη)
import pandas as pd
df = pd.DataFrame(all_hotels)
df.to_csv("tripadvisor_hotels_paris.csv", index=False, encoding="utf-8-sig")
print(f"Εξήχθησαν {len(df)} γραμμές σε CSV")
Το encoding='utf-8-sig' είναι σημαντικό — εξασφαλίζει ότι το Excel εμφανίζει σωστά μη λατινικούς χαρακτήρες (γαλλικούς τόνους, κινέζικους χαρακτήρες κ.λπ.) όταν ανοίγετε το CSV.
Εξαγωγή σε JSON (για nested δεδομένα)
Όταν έχετε κριτικές μέσα σε ξενοδοχεία, το JSON κρατάει την ιεραρχία:
# Ιεραρχική δομή
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": "..."},
]
}
# Για flat ανάλυση, χρησιμοποιήστε json_normalize
flat_reviews = pd.json_normalize(
hotel_data,
record_path="reviews",
meta=["property_id", "name"]
)
flat_reviews.to_csv("reviews_flat.csv", index=False)
Προσέγγιση δύο αρχείων για σχεσιακά δεδομένα
Για μεγάλα datasets, χρησιμοποιώ δύο CSV files:
hotels.csv— μία γραμμή ανά property (flat)reviews.csv— μία γραμμή ανά κριτική, μεproperty_idως foreign key
Αυτό κάνει εύκολο το join στο pandas, το φόρτωμα σε βάση δεδομένων ή την εισαγωγή σε BI εργαλεία.
Αν δεν θέλετε να μπλέξετε με όλη αυτή τη λογική εξαγωγής, το Thunderbit σας επιτρέπει να εξάγετε τα δεδομένα απευθείας σε Excel, Google Sheets, Airtable ή Notion — δωρεάν, χωρίς κώδικα. Χρήσιμο όταν πρέπει να μοιραστείτε αποτελέσματα με μη τεχνικούς συναδέλφους.
Συμβουλές για υπεύθυνο και αποδοτικό scraping στο TripAdvisor
Υπεύθυνο scraping σε έξι σημεία:
- Ελέγξτε το
robots.txt: Τοrobots.txtτου TripAdvisor μπλοκάρει πλήρως bots εκπαίδευσης AI (GPTBot, ClaudeBot κ.λπ.). Οι συνηθισμένοι crawlers αντιμετωπίζουν περιορισμούς σε επιλεγμένες διαδρομές. Δείτε το στοtripadvisor.com/robots.txt. - Προσθέστε καθυστερήσεις: 3–7 δευτερόλεπτα ανάμεσα σε requests είναι ένα ασφαλές εύρος. Αν πάτε πιο γρήγορα από 10–15 αιτήματα το λεπτό ανά IP, θα ενεργοποιηθεί rate limiting.
- Κάντε scrape μόνο δημόσια δεδομένα. Μην συνδέεστε για να αποκτήσετε πρόσβαση σε περιορισμένο περιεχόμενο.
- Αποθηκεύστε τα δεδομένα με ασφάλεια και συμμορφωθείτε με GDPR/CCPA αν χειρίζεστε προσωπικά δεδομένα (ονόματα reviewers κ.λπ.).
- Σκεφτείτε το επίσημο API του TripAdvisor αν χρειάζεστε δεδομένα εμπορικής κλίμακας. Το Developer Portal προσφέρει πρόσβαση σε πληροφορίες επιχειρήσεων και έως 5 κριτικές και 5 φωτογραφίες ανά τοποθεσία — περιορισμένο, αλλά νόμιμο και σταθερό.
- Λάβετε υπόψη το νομικό πλαίσιο: Η απόφαση του Δικαστηρίου της ΕΕ για τη Ryanair (Δεκέμβριος 2025) ενίσχυσε τους περιορισμούς scraping που βασίζονται σε ToS στην Ευρώπη. Οι Όροι Χρήσης του TripAdvisor απαγορεύουν ρητά το scraping. Κάντε scraping υπεύθυνα και με δική σας ευθύνη.
Κλείνοντας
Αυτή είναι η πλήρης εικόνα.
- Requests + BeautifulSoup είναι η πιο απλή διαδρομή. Δουλεύει για στατικές σελίδες καταχωρίσεων, θέλει ελάχιστη ρύθμιση και είναι γρήγορη. Ξεκινήστε από εδώ αν κάνετε scrape σε λιγότερες από 100 σελίδες και δεν χρειάζεστε περιεχόμενο που αποδίδεται με JavaScript.
- Selenium χειρίζεται ό,τι δεν μπορεί το requests: δυναμικό περιεχόμενο, κουμπιά “Read more”, banners cookies. Είναι 5 φορές πιο αργό και βαρύ σε πόρους, αλλά είναι η μόνη επιλογή όταν πρέπει να αλληλεπιδράσετε με τη σελίδα.
- Κρυφό JSON / GraphQL είναι η πιο καθαρή και γρήγορη προσέγγιση. Σας δίνει δομημένα δεδομένα χωρίς parsing HTML, μειώνει τον αριθμό αιτημάτων (και άρα την ανάγκη για proxies) και επιστρέφει δεδομένα έτοιμα για ανάλυση. Θέλει περισσότερο reverse-engineering στην αρχή και πού και πού συντήρηση όταν το TripAdvisor αλλάζει τη δομή των δεδομένων του.
Η επαναχρησιμοποιήσιμη συνάρτηση scrape_tripadvisor() καλύπτει ξενοδοχεία, εστιατόρια και αξιοθέατα. Δεν θα έπρεπε να χρειάζεστε δεύτερο tutorial.
Και αν στη μέση του tutorial αποφασίσετε ότι ο προγραμματισμός δεν είναι για εσάς — ή απλώς χρειάζεστε 50 ξενοδοχεία σε ένα spreadsheet μέχρι το τέλος της ημέρας — το Chrome extension του Thunderbit μπορεί να το κάνει με δύο κλικ, με AI-powered ανίχνευση πεδίων, αυτόματη pagination και δωρεάν export σε Excel ή Google Sheets. Δεν χρειάζεται Python.
Αν θέλετε να το ψάξετε πιο βαθιά, έχουμε περισσότερους οδηγούς στο blog του Thunderbit και στο κανάλι μας στο YouTube.
Συχνές ερωτήσεις
1. Είναι νόμιμο το scraping στο TripAdvisor;
Οι Όροι Χρήσης του TripAdvisor απαγορεύουν ρητά το scraping. Ωστόσο, τα δικαστήρια έχουν γενικά κρίνει ότι το scraping δημόσια διαθέσιμων δεδομένων (εκτός login) δεν παραβιάζει το Computer Fraud and Abuse Act στις ΗΠΑ. Παρ’ όλα αυτά, η απόφαση του δικαστηρίου της ΕΕ για τη Ryanair το 2025 ενίσχυσε τους περιορισμούς που βασίζονται σε ToS στην Ευρώπη. Κάντε scrape μόνο δημόσια δεδομένα, σεβαστείτε το robots.txt, μην αναδημοσιεύετε προστατευόμενο περιεχόμενο και συμβουλευτείτε νομικό σύμβουλο αν χρησιμοποιείτε τα δεδομένα εμπορικά.
2. Μπορώ να κάνω scrape στο TripAdvisor χωρίς Python;
Ναι. No-code εργαλεία όπως το Thunderbit μπορούν να κάνουν scrape στο TripAdvisor απευθείας από τον browser σας με AI-powered ανίχνευση πεδίων και αυτόματη pagination. Μπορείτε επίσης να χρησιμοποιήσετε browser extensions, Google Sheets add-ons ή εμπορικά scraping APIs. Η Python σάς δίνει τον περισσότερο έλεγχο και ευελιξία, αλλά δεν είναι η μόνη επιλογή.
3. Πώς αποφεύγω το μπλοκάρισμα όταν κάνω scrape στο TripAdvisor;
Οι βασικές τακτικές: χρησιμοποιήστε ρεαλιστικά και συνεπή headers (ειδικά User-Agent και Sec-CH-UA), κάντε rotation σε residential proxies (τα datacenter IPs μπλοκάρονται αμέσως), προσθέστε τυχαίες καθυστερήσεις 3–7 δευτερολέπτων ανάμεσα στα requests, χρησιμοποιήστε τη μέθοδο με κρυφό JSON για να μειώσετε τον συνολικό αριθμό αιτημάτων, εφαρμόστε retry logic με exponential backoff και κάντε warm up τα sessions επισκεπτόμενοι πρώτα την αρχική σελίδα πριν από βαθύ scraping.
4. Τι δεδομένα μπορώ να κάνω scrape από το TripAdvisor;
Ξενοδοχεία, εστιατόρια και αξιοθέατα — συμπεριλαμβανομένων ονομάτων, βαθμολογιών, αριθμού κριτικών, εύρους τιμών, διευθύνσεων, συντεταγμένων, παροχών (ξενοδοχεία), τύπων κουζίνας (εστιατόρια), διάρκειας περιηγήσεων (αξιοθέατα) και πλήρους κειμένου κριτικών με μεμονωμένες βαθμολογίες και ημερομηνίες. Οι προσεγγίσεις hidden JSON και GraphQL επιστρέφουν τα πλουσιότερα δεδομένα ανά αίτημα.
5. Πόσες σελίδες μπορώ να κάνω scrape από το TripAdvisor ανά ημέρα;
Με ένα IP και λογικές καθυστερήσεις: περίπου 600–1.000 σελίδες την ημέρα. Με 20 rotating residential proxies: περίπου 200.000–300.000 σελίδες την ημέρα χρησιμοποιώντας request-based προσεγγίσεις. Το Selenium είναι πιο αργό — αναμένετε 8.000–12.000 σελίδες την ημέρα ανά proxy. Η προσέγγιση hidden JSON/GraphQL σας δίνει τα περισσότερα δεδομένα ανά αίτημα, οπότε ίσως χρειαστείτε πολύ λιγότερες συνολικές σελίδες για να πάρετε την ίδια ποσότητα πληροφορίας.
Μάθετε περισσότερα


