Πώς να κάνετε Scrape στο TripAdvisor με Python (Χωρίς να μπλοκαριστείτε)

Τελευταία ενημέρωση: April 17, 2026
Πώς να κάνετε Scrape στο TripAdvisor με Python (Χωρίς να μπλοκαριστείτε)

Την περασμένη εβδομάδα προσπάθησα να τραβήξω βαθμολογίες ξενοδοχείων και αριθμούς κριτικών για περίπου 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 και να προτείνει αυτόματα τις σωστές στήλες για εξαγωγή. Η ροή είναι πραγματικά δύο κλικ:

  1. Ανοίξτε μια σελίδα καταχωρίσεων του TripAdvisor (π.χ. αποτελέσματα αναζήτησης “Hotels in Paris”).
  2. Κάντε κλικ στο “AI Suggest Fields” από το sidebar του Thunderbit. Το AI σαρώνει τη σελίδα και προτείνει στήλες όπως Hotel Name, Rating, Review Count, Price και Location.
  3. Κάντε κλικ στο “Scrape.” Το Thunderbit εξάγει δεδομένα από κάθε καταχώριση της σελίδας — και χειρίζεται αυτόματα και το pagination αν χρειάζεστε περισσότερα αποτελέσματα.
  4. Εξαγάγετε σε 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Πλήρες κείμενο κριτικής, βαθμολογίες, ημερομηνίες
Δημιουργία leadsSales 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 HTML
  • selenium (4.43.0) — αυτοματοποίηση browser, απαιτεί Python 3.10+
  • httpx (0.28.1) — async HTTP client
  • parsel (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 σας δίνει τα περισσότερα δεδομένα ανά αίτημα, οπότε ίσως χρειαστείτε πολύ λιγότερες συνολικές σελίδες για να πάρετε την ίδια ποσότητα πληροφορίας.

Μάθετε περισσότερα

Ke
Ke
CTO στη Thunderbit | Senior Data Scientist & ML Expert Με σχεδόν μια δεκαετία εμπειρίας στη μηχανική μάθηση και την επιστήμη δεδομένων, ο Ke Shen είναι απόφοιτος του Columbia University και πρώην Senior Data Scientist στα Walmart Labs. Με βαθιά, αναγνωρισμένη από συναδέλφους τεχνογνωσία σε Python, R, Java και Στατιστική, μοιράζεται δοκιμασμένες στην πράξη γνώσεις για το πώς οι σύνθετοι αλγόριθμοι τεχνητής νοημοσύνης περνούν από τη θεωρία σε αρχιτεκτονική έτοιμη για παραγωγή.
Πίνακας περιεχομένων

Πάρε δεδομένα από μια ιστοσελίδα, απλώς ζητώντας το

Πες αυτό που χρειάζεσαι με απλά λόγια. Ή ακόμα καλύτερα, πες και τίποτα.

Δοκίμασε το Thunderbit δωρεάν
Εξήγαγε δεδομένα με AI
Μετέφερε εύκολα δεδομένα σε Google Sheets, Airtable ή Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week