Η Google έκλεισε το Flights API της το 2018, όμως οι τιμές των αεροπορικών συνεχίζουν να αλλάζουν — έως και 17 φορές μέσα σε 48 ώρες για μία μόνο εσωτερική διαδρομή. Αν θέλετε προγραμματιστική πρόσβαση σε αυτά τα δεδομένα, το scraping είναι πρακτικά η μόνη ρεαλιστική επιλογή.
Έχω αφιερώσει πολύ χρόνο δοκιμάζοντας διαφορετικές προσεγγίσεις για την άντληση δεδομένων πτήσεων από την Google, και το τοπίο έχει αλλάξει δραματικά — ειδικά μετά την κυκλοφορία του SearchGuard από την Google τον Ιανουάριο του 2025. Σε αυτόν τον οδηγό, θα δείξω πώς να χτίσετε ένα λειτουργικό Python scraper για το Google Flights με Playwright, πώς να αντιμετωπίσετε τα anti-bot μέτρα που «ρίχνουν» τους περισσότερους, και πώς να το επεκτείνετε σε έναν αυτοματοποιημένο price tracker με ειδοποιήσεις. Αν προτιμάτε να παραλείψετε εντελώς τον κώδικα, θα καλύψω επίσης μια no-code λύση με Thunderbit που σας οδηγεί στο ίδιο αποτέλεσμα σε περίπου δύο λεπτά.
Γιατί να κάνετε Scrape το Google Flights με Python;
Το Google Flights κυριαρχεί στην αναζήτηση πτήσεων. Η προβολή του σε mobile στις ΗΠΑ εκτοξεύτηκε στο 47,6%, ξεπερνώντας κάθε μεγάλο OTA. Η αγορά του travel metasearch πίσω από αυτό αποτιμάται στα $8,33 δισ. το 2024 και αναπτύσσεται με CAGR 30,2%. Κι όμως, από τότε που το QPX Express API έκλεισε οριστικά στις 10 Απριλίου 2018, δεν υπάρχει επίσημος τρόπος να έχετε πρόσβαση σε αυτά τα δεδομένα προγραμματιστικά.
Την ίδια στιγμή, οι τιμές των πτήσεων μεταβάλλονται έως και 50% για το ίδιο δρομολόγιο, με μέση διαφορά περίπου 20 δολάρια ανάμεσα στη χαμηλότερη και την υψηλότερη τιμή. Αεροπορικές όπως η Delta χρησιμοποιούν 77 fare buckets για δυναμική τιμολόγηση. Το μέσο αμερικανικό εισιτήριο μετ' επιστροφής στις αρχές του 2026 βρίσκεται στα 408 δολάρια, με τις αεροπορικές τιμές να είναι 14,9% υψηλότερες σε ετήσια βάση.
Κυρίαρχη πλατφόρμα, χωρίς API, με ασταθείς τιμές. Γι’ αυτό το scraping του Google Flights με Python έχει γίνει ένα από τα πιο δημοφιλή projects τόσο στο GitHub όσο και σε ταξιδιωτικά φόρουμ.
Δείτε ποιοι ωφελούνται και πώς:
| Τύπος Χρήστη | Περίπτωση Χρήσης | Κύριο Όφελος |
|---|---|---|
| Μεμονωμένοι ταξιδιώτες | Παρακολούθηση τιμών για συγκεκριμένες διαδρομές με την πάροδο του χρόνου | Εξοικονόμηση κατά μέσο όρο $50 ανά πτήση |
| Ταξιδιωτικά γραφεία | Ανταγωνιστική τιμολογιακή πληροφόρηση | Παρακολούθηση ισοτιμίας ναύλων σε πραγματικό χρόνο |
| Εταιρικές ομάδες ταξιδιών | Βελτιστοποίηση κόστους σε διάφορες διαδρομές | 10–30% εξοικονόμηση στα εταιρικά ταξίδια |
| Developers | Δημιουργία εφαρμογών σύγκρισης ναύλων | Προγραμματιστική πρόσβαση σε δεδομένα τιμολόγησης |
| Ερευνητές | Ανάλυση της μεταβλητότητας στις τιμές αεροπορικών | Ακαδημαϊκή και αγοραία έρευνα |
Οι χρήστες στα φόρουμ είναι ξεκάθαροι για το γιατί στράφηκαν στο scraping: το "Google Flights API was discontinued and I should use web scraping instead" είναι μια άποψη που εμφανίζεται ξανά και ξανά. Και το ROI είναι πραγματικό — η Hopper ισχυρίζεται 95% ακρίβεια πρόβλεψης αναλύοντας πάνω από 5 δισ. τιμολογιακές προσφορές καθημερινά, ενώ τα δεδομένα της Expedia για το 2026 δείχνουν ότι η κράτηση 8–15 ημέρες νωρίτερα εξοικονομεί περίπου $25 στις εσωτερικές πτήσεις.
Τι Δεδομένα Μπορείτε να Κάνετε Scrape από το Google Flights;
Μια σελίδα αποτελεσμάτων του Google Flights περιέχει εντυπωσιακά πλούσιο σύνολο πεδίων δεδομένων. Συνήθως είναι διαθέσιμα τα εξής:
- Όνομα αεροπορικής (και λογότυπο)
- Ώρα αναχώρησης και κωδικός αεροδρομίου
- Ώρα άφιξης και κωδικός αεροδρομίου
- Συνολική διάρκεια πτήσης
- Αριθμός ενδιάμεσων στάσεων και λεπτομέρειες αναμονής (αεροδρόμιο, διάρκεια, αν είναι νυχτερινή)
- Τιμή εισιτηρίου (ανάλογα με το νόμισμα)
- Εκπομπές CO2 (kg CO2e, με ποσοστιαία σύγκριση με τυπικές πτήσεις)
- Κατηγορία ταξιδιού, αριθμός πτήσης, μοντέλο αεροσκάφους
- Πληροφορίες για το legroom
- Παροχές (Wi‑Fi, πρίζες, streaming περιεχομένου)
- Δείκτης επιπέδου τιμής (χαμηλή/τυπική/υψηλή)
- Προειδοποιήσεις καθυστέρησης ("Συχνά καθυστερεί πάνω από 30 λεπτά")
Η διαθεσιμότητα των δεδομένων διαφέρει ανάλογα με τη διαδρομή, την ημερομηνία και τον τύπο εισιτηρίου (απλή μετάβαση ή μετ’ επιστροφής). Δείτε πώς μοιάζει ένα μεμονωμένο scraped record πτήσης σε JSON:
{
"search_date": "2026-04-16",
"route": "SFO-JFK",
"departure_date": "2026-05-15",
"flights": [
{
"airline": "United Airlines",
"flight_number": "UA123",
"departure_time": "08:00",
"departure_airport": "SFO",
"arrival_time": "16:35",
"arrival_airport": "JFK",
"duration_minutes": 335,
"stops": 0,
"price_usd": 287,
"price_level": "low",
"co2_kg": 156,
"co2_vs_typical": "-12%",
"travel_class": "Economy"
}
]
}
Ρύθμιση του Python Περιβάλλοντός σας
Πριν γράψουμε κώδικα scraping, χρειάζεστε μερικά βασικά πράγματα στη θέση τους.
Προαπαιτούμενα:
- Δυσκολία: Ενδιάμεσο επίπεδο
- Απαιτούμενος χρόνος: ~1–2 ώρες για το πλήρες tutorial
- Τι θα χρειαστείτε: Python 3.7+, βασική γνώση Python, πρόγραμμα περιήγησης βασισμένο στο Chrome
Εγκαταστήστε τις Απαραίτητες Βιβλιοθήκες
Χρησιμοποιούμε το Playwright για browser automation (το Google Flights είναι 100% rendered από JavaScript — τα στατικά HTTP requests δεν επιστρέφουν κάτι χρήσιμο), μαζί με μερικά βοηθητικά εργαλεία:
pip install playwright playwright-stealth pandas
playwright install chromium
- Playwright — automation browser χωρίς ορατό περιβάλλον, χειρίζεται rendering JavaScript, ενσωματωμένοι μηχανισμοί αναμονής
- playwright-stealth — μειώνει κοινά σήματα ανίχνευσης bot
- pandas — για ανάλυση δεδομένων και εξαγωγή σε CSV αργότερα
Γιατί Playwright αντί για Selenium ή Requests
Το Google Flights δεν δουλεύει μόνο με requests + BeautifulSoup — το περιεχόμενο της σελίδας αποδίδεται εξ ολοκλήρου από JavaScript. Χρειάζεστε πραγματικό browser.
| Χαρακτηριστικό | Playwright | Selenium | requests + BS4 |
|---|---|---|---|
| Απόδοση JS | Πλήρης υποστήριξη | Πλήρης υποστήριξη | Καμία |
| Ταχύτητα | 42% ταχύτερο συνολικά | Βασική γραμμή | Δεν εφαρμόζεται σε αυτήν τη χρήση |
| Async υποστήριξη | Εγγενής | Μόνο σειριακή | Δεν εφαρμόζεται |
| Χρήση μνήμης | 30% λιγότερη | Υψηλότερη | Ελάχιστη |
| Παράκαμψη ανίχνευσης bot | Καλή (με stealth) | Πιο εύκολο να εντοπιστεί | Δεν εφαρμόζεται |
Το Playwright είναι ταχύτερο, πιο σύγχρονο και έχει καλύτερη υποστήριξη για async. Για το Google Flights ειδικά, είναι η ξεκάθαρη καλύτερη επιλογή.
Βήμα-Βήμα: Πώς να Κάνετε Scrape το Google Flights με Python
Αυτό είναι το βασικό κομμάτι του οδηγού. Θα χτίσουμε το scraper κομμάτι-κομμάτι.

Βήμα 1: Ορίστε τις Κλάσεις Δεδομένων σας
Ξεκινήστε οργανώνοντας τις παραμέτρους αναζήτησης και τα δεδομένα πτήσεων με Python dataclasses. Αυτό κρατά τον κώδικα καθαρό και κάνει πιο εύκολη την επέκταση αργότερα.
from dataclasses import dataclass, field
from typing import Optional, List
@dataclass
class SearchParams:
origin: str # π.χ. "SFO"
destination: str # π.χ. "JFK"
departure_date: str # π.χ. "2026-05-15"
return_date: Optional[str] = None
trip_type: str = "one-way" # "one-way" ή "round-trip"
travel_class: str = "economy"
@dataclass
class FlightData:
airline: str = ""
departure_time: str = ""
arrival_time: str = ""
duration: str = ""
stops: str = ""
price: str = ""
co2_emissions: str = ""
Κάθε πεδίο αντιστοιχεί άμεσα σε αυτό που θα εξάγουμε από τη σελίδα. Έχοντας αυτή τη δομή εξαρχής, δεν θα καταλήξετε να περνάτε ακατάστατα dictionaries αργότερα.
Βήμα 2: Κατανοήστε τη Δομή του Google Flights URL
Το Google Flights κωδικοποιεί τις παραμέτρους αναζήτησης χρησιμοποιώντας Base64-encoded Protobuf στο URL parameter tfs. Μπορείτε είτε να κάνετε reverse-engineering σε αυτή την κωδικοποίηση είτε να ακολουθήσετε την πιο απλή διαδρομή: να κατασκευάσετε ένα URL σε μορφή φυσικής γλώσσας.
Η πιο απλή μέθοδος είναι το εξής format αναζήτησης:
https://www.google.com/travel/flights?q=flights+from+SFO+to+JFK+on+2026-05-15&curr=USD
Για περισσότερο έλεγχο, μπορείτε να χτίσετε τα URLs προγραμματιστικά:
def build_flights_url(origin: str, destination: str, date: str) -> str:
base = "https://www.google.com/travel/flights"
query = f"flights from {origin} to {destination} on {date}"
return f"{base}?q={query.replace(' ', '+')}&curr=USD"
Η εναλλακτική — reverse-engineering της Protobuf κωδικοποίησης — προσφέρει μεγαλύτερο έλεγχο, αλλά χαλάει όταν η Google αλλάζει την εσωτερική μορφή. Βιβλιοθήκες όπως το fast-flights στο GitHub χρησιμοποιούν Protobuf decoding για να παρακάμψουν εντελώς το HTML parsing, αλλά είναι πιο προχωρημένη προσέγγιση.
Βήμα 3: Εκκινήστε τον Browser και Μεταβείτε στο Google Flights
Ακολουθεί η ρύθμιση του Playwright. Χρησιμοποιούμε το playwright-stealth για να μειώσουμε από την αρχή τον κίνδυνο ανίχνευσης.
import asyncio
from playwright.async_api import async_playwright
from playwright_stealth import Stealth
async def scrape_flights(params: SearchParams) -> List[FlightData]:
async with Stealth().use_async(async_playwright()) as pw:
browser = await pw.chromium.launch(
headless=True,
args=[
"--disable-blink-features=AutomationControlled",
"--disable-dev-shm-usage",
"--no-first-run",
]
)
context = await browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/125.0.0.0 Safari/537.36"
),
locale="en-US",
timezone_id="America/New_York",
)
# Προρυθμίστε το cookie consent ώστε να παραλείψετε το popup
await context.add_cookies([{
"name": "SOCS",
"value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
"domain": ".google.com",
"path": "/"
}])
page = await context.new_page()
Τρέχουμε headless για παραγωγική χρήση (αλλάξτε σε headless=False για debugging), ορίζουμε ρεαλιστικό viewport και user agent, και προρυθμίζουμε το SOCS cookie για να παρακάμψουμε το consent popup — περισσότερα στο anti-bot section.
Βήμα 4: Μεταβείτε στα Αποτελέσματα Αναζήτησης
Φορτώστε το URL που κατασκευάσατε και περιμένετε να εμφανιστούν τα αποτελέσματα των πτήσεων:
url = build_flights_url(
params.origin, params.destination, params.departure_date
)
await page.goto(url, wait_until="networkidle")
# Περιμένετε να φορτώσουν τα αποτελέσματα των πτήσεων
await page.wait_for_selector(
"li.pIav2d", timeout=15000
)
Αν δείτε timeout εδώ, συνήθως σημαίνει είτε ότι το consent popup μπλόκαρε τη σελίδα (δείτε τη διόρθωση με το cookie στο Βήμα 3) είτε ότι η Google εμφανίζει CAPTCHA. Θα καλύψουμε και τα δύο σενάρια στο anti-bot section.
Βήμα 5: Φορτώστε Όλα τα Αποτελέσματα Πτήσεων
Το Google Flights κρύβει επιπλέον αποτελέσματα πίσω από το κουμπί "Show more flights". Πρέπει να το πατάτε επανειλημμένα μέχρι να εμφανιστούν όλες οι πτήσεις:
# Πατήστε "Show more flights" μέχρι να φορτωθούν όλα τα αποτελέσματα
while True:
try:
more_button = page.locator(
'button:has-text("Show more flights")'
)
if await more_button.is_visible(timeout=3000):
await more_button.click()
await page.wait_for_timeout(2000)
else:
break
except Exception:
break
Αυτός ο βρόχος πατά το κουμπί, περιμένει 2 δευτερόλεπτα για να αποδοθούν τα νέα αποτελέσματα και σταματά όταν το κουμπί δεν είναι πλέον ορατό. Στις δοκιμές μου, οι περισσότερες διαδρομές έχουν 1–3 σελίδες αποτελεσμάτων.
Βήμα 6: Εξαγάγετε τα Δεδομένα Πτήσεων με CSS Selectors
Τώρα αναλύουμε τα πραγματικά δεδομένα πτήσεων από τη φορτωμένη σελίδα. Ακολουθούν τα selectors (επιβεβαιωμένα έως τον Απρίλιο 2026 — δείτε την ενότητα συντήρησης παρακάτω για το γιατί έχει σημασία αυτή η ημερομηνία):
flights = []
cards = await page.query_selector_all("li.pIav2d")
for card in cards:
flight = FlightData()
# Όνομα αεροπορικής
airline_el = await card.query_selector(
"div.sSHqwe span:not([class])"
)
if airline_el:
flight.airline = (await airline_el.inner_text()).strip()
# Ώρα αναχώρησης
dep_el = await card.query_selector(
'span[aria-label*="Departure time"]'
)
if dep_el:
flight.departure_time = (await dep_el.inner_text()).strip()
# Ώρα άφιξης
arr_el = await card.query_selector(
'span[aria-label*="Arrival time"]'
)
if arr_el:
flight.arrival_time = (await arr_el.inner_text()).strip()
# Διάρκεια
dur_el = await card.query_selector("div.gvkrdb")
if dur_el:
flight.duration = (await dur_el.inner_text()).strip()
# Ενδιάμεσες στάσεις
stops_el = await card.query_selector("div.EfT7Ae span")
if stops_el:
flight.stops = (await stops_el.inner_text()).strip()
# Τιμή
price_el = await card.query_selector(
"div.FpEdX span"
)
if price_el:
flight.price = (await price_el.inner_text()).strip()
# Εκπομπές CO2
co2_el = await card.query_selector("div.O7CXue")
if co2_el:
flight.co2_emissions = (
await co2_el.get_attribute("aria-label") or ""
).strip()
flights.append(flight)
await browser.close()
return flights
Προσοχή: class names όπως pIav2d, sSHqwe και FpEdX δημιουργούνται από το Closure Compiler της Google και μπορούν να αλλάξουν σε οποιοδήποτε build. Οι selectors με aria-label είναι πιο σταθεροί. Θα καλύψω μια πλήρη στρατηγική συντήρησης παρακάτω.
Βήμα 7: Αποθηκεύστε τα Αποτελέσματα σε JSON ή CSV
Τέλος, αποθηκεύστε τα scraped δεδομένα με χρονική σήμανση (κρίσιμο για την παρακολούθηση τιμών αργότερα):
import json
from datetime import datetime
from dataclasses import asdict
async def main():
params = SearchParams(
origin="SFO",
destination="JFK",
departure_date="2026-05-15"
)
flights = await scrape_flights(params)
output = {
"search_date": datetime.now().isoformat(),
"params": asdict(params),
"flights": [asdict(f) for f in flights],
}
with open("flights.json", "w") as f:
json.dump(output, f, indent=2)
# Αποθήκευση και ως CSV
import pandas as pd
df = pd.DataFrame([asdict(f) for f in flights])
df["search_date"] = datetime.now().isoformat()
df["route"] = f"{params.origin}-{params.destination}"
df.to_csv("flights.csv", index=False)
print(f"Scraped {len(flights)} flights")
asyncio.run(main())
Τρέξτε αυτό και θα πρέπει να δείτε τα flights.json και flights.csv με τα αποτελέσματά σας. Στις δοκιμές μου, μια αναζήτηση SFO-JFK επιστρέφει συνήθως 30–80 επιλογές πτήσεων και χρειάζεται περίπου 15–20 δευτερόλεπτα για να ολοκληρωθεί.
Ο Οδηγός Επιβίωσης Anti-Bot για Scraping στο Google Flights
Τα περισσότερα tutorials σταματούν εδώ. Οι περισσότεροι scrapers αποτυγχάνουν εδώ. Η Google έφερε το SearchGuard τον Ιανουάριο του 2025, που διέλυσε σχεδόν όλα τα SERP scrapers μέσα σε μια νύχτα. Η Google το περιγράφει ως «το αποτέλεσμα δεκάδων χιλιάδων ανθρωποωρών και επένδυσης εκατομμυρίων δολαρίων». Το Google Flights αξιολογείται ως 4/5 σε δυσκολία για scraping.
Κανένα ανταγωνιστικό άρθρο δεν το καλύπτει σε βάθος, κι όμως αυτός είναι ο #1 λόγος που οι scrapers των ανθρώπων σταματούν να δουλεύουν. Δείτε τι έχετε απέναντί σας και πώς να το αντιμετωπίσετε.

Τυχαίες Καθυστερήσεις Ανάμεσα στα Requests
Η πιο απλή άμυνα απέναντι στο rate limiting. Δύο γραμμές κώδικα, μέτρια αποτελεσματικότητα:
import time
import random
time.sleep(random.uniform(3, 7))
Προσθέστε το ανάμεσα στις μεταβάσεις σελίδων. Τα σταθερά διαστήματα (όπως ακριβώς 5 δευτερόλεπτα κάθε φορά) είναι ύποπτα — κάντε τα τυχαία.
Εναλλαγή User-Agent
Το να στέλνετε την ίδια συμβολοσειρά user-agent σε κάθε αίτημα είναι εύκολο να εντοπιστεί. Κάντε εναλλαγή από μια λίστα:
import random
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
]
user_agent = random.choice(USER_AGENTS)
Παράκαμψη Ανίχνευσης Headless
Η Google ελέγχει το navigator.webdriver και άλλα automation signals. Η βιβλιοθήκη playwright-stealth καλύπτει τα περισσότερα, αλλά θα πρέπει επίσης να ορίσετε τα launch arguments που εμφανίστηκαν στο Βήμα 3. Τα βασικά flags:
args=[
"--disable-blink-features=AutomationControlled",
"--disable-dev-shm-usage",
"--no-first-run",
]
Αυτό σας περνάει από τη βασική ανίχνευση. Το SearchGuard πηγαίνει βαθύτερα — παρακολουθεί ταχύτητα ποντικιού, χρονισμό πληκτρολογίου και μοτίβα κύλισης — αλλά για scraping με μέτριο όγκο, το stealth mode μαζί με ρεαλιστικές καθυστερήσεις συνήθως αρκεί.
Proxy Rotation: Datacenter vs. Residential
Για οτιδήποτε πέρα από λίγες αναζητήσεις, θα χρειαστείτε proxies. Η διαφορά έχει σημασία:
| Χαρακτηριστικό | Datacenter Proxies | Residential Proxies |
|---|---|---|
| Ταχύτητα | 100–1.000 Mbps | 10–100 Mbps |
| Ποσοστό επιτυχίας (Google) | 20–40% | 85–95% |
| Κόστος | $0.10–$0.50/IP/μήνα | $5–$15/GB |
| Κίνδυνος εντοπισμού | Υψηλός | Πολύ χαμηλός |
Τα residential proxies είναι περίπου 180 φορές φθηνότερα ανά επιτυχημένο request όταν κάνετε scraping σε προστατευμένους ιστότοπους. Τιμολόγηση παρόχων το 2026: Smartproxy από $7/GB, Bright Data $8.40/GB, Oxylabs $8/GB.
Προσθέστε proxy στο Playwright έτσι:
browser = await pw.chromium.launch(
proxy={"server": "http://proxy-host:port",
"username": "user", "password": "pass"}
)
Διαχείριση του Cookie Consent Popup
Οι χρήστες αναφέρουν συστηματικά το popup "I agree to terms" ως εμπόδιο: "first google will show you the 'I agree to terms and conditions' popup." Η πιο καθαρή λύση είναι η προρύθμιση του cookie SOCS (όπως δείχθηκε στο Βήμα 3). Αν αυτό δεν δουλέψει, κάντε κλικ για να το περάσετε:
try:
accept_btn = page.locator('button:has-text("Accept all")')
if await accept_btn.is_visible(timeout=3000):
await accept_btn.click()
await page.wait_for_timeout(1000)
except Exception:
pass # Δεν υπάρχει popup
Σημείωση: το κείμενο του κουμπιού αλλάζει ανά locale — "Alle akzeptieren" στα γερμανικά, "Tout accepter" στα γαλλικά.
Γρήγορη Αναφορά Anti-Bot
| Τεχνική | Δυσκολία | Αποτελεσματικότητα | Χρειάζεται Κώδικας; |
|---|---|---|---|
| Τυχαίες καθυστερήσεις (2–7s) | Χαμηλή | Μέτρια | 2 γραμμές |
| Εναλλαγή user-agent | Χαμηλή | Μέτρια | 5 γραμμές |
| Παράκαμψη headless detection | Μέτρια | Υψηλή | Playwright launch args |
| playwright-stealth plugin | Μέτρια | 60–80% σε βασικούς ιστότοπους | pip install |
| Proxy rotation (datacenter) | Μέτρια | Μέτρια | Ρύθμιση |
| Proxy rotation (residential) | Μέτρια | 85–95% επιτυχία | Ρύθμιση |
| Προρύθμιση cookie consent (SOCS) | Χαμηλή | Απαραίτητο | 1 γραμμή |
Για προτεινόμενους ασφαλείς ρυθμούς: κρατήστε καθυστερήσεις 10–20 δευτερολέπτων ανάμεσα στα requests με IP rotation. Τα όρια της Google είναι περίπου 100 requests/λεπτό ανά IP πριν δείτε 429, και συνεχή volumes πάνω από 1.000 requests/ημέρα ανά IP μπορούν να ενεργοποιήσουν προσωρινούς αποκλεισμούς.
Γιατί οι Selectors του Google Flights Σπάνε Συνεχώς (και Πώς να το Διορθώσετε)
Το #1 πρόβλημα, με μεγάλη διαφορά. Τα forum threads είναι γεμάτα με παραλλαγές του "το μόνο που παίρνω πίσω είναι 14 άδειες λίστες." Κάθε tutorial σας δίνει selectors. Κανένα δεν εξηγεί γιατί σπάνε.
Γιατί Αλλάζουν οι Selectors του Google Flights
Πρόκειται για τρία πράγματα:
-
Obfuscation από το Closure Compiler. Η Google χρησιμοποιεί Closure Stylesheets για να δημιουργεί class names όπως
BVAVmfκαιYMlIzμέσω τουgoog.setCssNameMapping(). Αυτά αλλάζουν σε κάθε build — μερικές φορές κάθε εβδομάδα. -
A/B testing. Διαφορετικοί χρήστες βλέπουν ταυτόχρονα διαφορετικές HTML δομές. Το scraper σας μπορεί να δουλεύει στον υπολογιστή σας αλλά να αποτυγχάνει για κάποιον σε άλλη περιοχή.
-
Διαφορές locale. Οι χρήστες στην ΕΕ βλέπουν διαφορετικούς όρους, διατάξεις και ακόμη και πεδία δεδομένων σε σχέση με τις ΗΠΑ.
Γράψτε Ανθεκτικούς Selectors
Προτιμήστε selectors που βασίζονται στο νόημα και όχι στην εμφάνιση:
# Ευάλωτο — σπάει σε κάθε build
price_el = await card.query_selector("div.BVAVmf > div.YMlIz")
# Πιο ανθεκτικό — βασίζεται σε accessibility labels
dep_el = await card.query_selector('span[aria-label*="Departure time"]')
# Επίσης ανθεκτικό — αντιστοίχιση βάσει κειμένου
more_btn = page.locator('button:has-text("Show more flights")')
Ιεραρχία σταθερότητας selectors (από το πιο σταθερό στο λιγότερο):
aria-labelattributes — συνδέονται με accessibility, αλλάζουν σπάνιαdata-*attributes — προστίθενται ρητά για λειτουργικότηταroleattributes — τα ARIA roles είναι σημασιολογικά- Text-based selectors — αντιστοιχούν στο ορατό περιεχόμενο
- Substring class matching — π.χ.
[class*="price"] - Πλήρη obfuscated class names — αποφύγετέ τα όταν γίνεται
Προσθέστε Συνάρτηση Επικύρωσης
Μην αφήνετε τους σπασμένους selectors να παράγουν σιωπηλά κενά δεδομένα. Εντοπίστε το νωρίς:
import logging
logger = logging.getLogger(__name__)
def validate_flight(data: FlightData) -> bool:
required = ["airline", "price", "departure_time",
"arrival_time", "duration"]
valid = True
for field_name in required:
if not getattr(data, field_name, ""):
logger.warning(
f"Missing '{field_name}' — selectors may need updating"
)
valid = False
return valid
Τρέξτε αυτό για κάθε scraped πτήση. Αν αρχίσετε να βλέπετε προειδοποιήσεις, ήρθε η ώρα να ελέγξετε τη σελίδα και να ενημερώσετε τους selectors σας.
Στρατηγική Συντήρησης Selectors
- Ελέγχετε τους selectors κάθε μήνα ή αμέσως όταν πέφτει η ποιότητα των αποτελεσμάτων
- Κρατήστε τους selectors σε ξεχωριστό config dictionary για εύκολες ενημερώσεις
- Οι selectors σε αυτό το άρθρο επαληθεύτηκαν τελευταία φορά: Απρίλιος 2026
- Σκεφτείτε τη βιβλιοθήκη fast-flights ως εναλλακτική — χρησιμοποιεί Protobuf decoding αντί για CSS selectors, παρακάμπτοντας εντελώς αυτό το πρόβλημα (αν και έχει τη δική της ευθραυστότητα όταν η Google αλλάζει τα εσωτερικά data formats)
Από One-Off Scrape σε Αυτοματοποιημένο Google Flights Price Tracker
Τα περισσότερα tutorials σταματούν στο "save to JSON." Ο τίτλος αυτού του άρθρου λέει "Price Alerts." Ώρα να το ολοκληρώσουμε.
![]()
Προγραμματίστε το Scraper σας να Τρέχει Αυτόματα
Επιλογή 1: Python βιβλιοθήκη schedule (η πιο απλή, λειτουργεί παντού):
import schedule
import time
def run_scraper():
asyncio.run(main())
schedule.every().day.at("06:00").do(run_scraper)
schedule.every().day.at("18:00").do(run_scraper)
while True:
schedule.run_pending()
time.sleep(60)
Επιλογή 2: cron job (Linux/Mac):
# Εκτέλεση καθημερινά στις 6 π.μ. και στις 6 μ.μ.
0 6,18 * * * cd /path/to/scraper && python scraper.py
Επιλογή 3: Windows Task Scheduler — δημιουργήστε ένα basic task που τρέχει python scraper.py στο πρόγραμμα που προτιμάτε.
Ο συμβιβασμός: όλα αυτά απαιτούν ένα μηχάνημα που μένει συνεχώς ανοιχτό. Αν το τρέχετε σε laptop που κοιμάται, θα χάνετε εκτελέσεις.
Αποθήκευση Ιστορικών Δεδομένων Τιμών
Αντί να αντικαθιστάτε ένα JSON αρχείο, περάστε σε προσθήκη εγγραφών σε SQLite database:
import sqlite3
from datetime import datetime
def init_db():
conn = sqlite3.connect("flights.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS flights (
id INTEGER PRIMARY KEY AUTOINCREMENT,
scrape_date TEXT NOT NULL,
route TEXT NOT NULL,
airline TEXT,
departure_time TEXT,
arrival_time TEXT,
duration TEXT,
stops TEXT,
price_usd REAL,
co2_emissions TEXT
)
""")
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_route_date "
"ON flights(route, scrape_date)"
)
conn.commit()
return conn
def save_flight(conn, route: str, flight: FlightData):
price_num = float(
flight.price.replace("$", "").replace(",", "")
) if flight.price else None
conn.execute(
"INSERT INTO flights "
"(scrape_date, route, airline, departure_time, "
"arrival_time, duration, stops, price_usd, co2_emissions) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
(datetime.now().isoformat(), route, flight.airline,
flight.departure_time, flight.arrival_time,
flight.duration, flight.stops, price_num,
flight.co2_emissions)
)
conn.commit()
Μετά από μία εβδομάδα με δύο scrapes την ημέρα, θα έχετε αρκετά δεδομένα για να αρχίσετε να εντοπίζετε τάσεις.
Αναλύστε Τάσεις Τιμών και Ορίστε Ειδοποιήσεις
Βρείτε τη φθηνότερη επιλογή από τα ιστορικά δεδομένα σας:
import pandas as pd
import sqlite3
conn = sqlite3.connect("flights.db")
df = pd.read_sql_query(
"SELECT * FROM flights WHERE route = 'SFO-JFK'", conn
)
summary = df.groupby("scrape_date")["price_usd"].agg(
["min", "max", "mean"]
)
cheapest = df.loc[df["price_usd"].idxmin()]
print(
f"Cheapest: ${cheapest['price_usd']:.0f} on "
f"{cheapest['scrape_date']} ({cheapest['airline']})"
)
Ενεργοποιήστε email alert όταν μια τιμή πέσει κάτω από το όριό σας:
import smtplib
from email.mime.text import MIMEText
def send_price_alert(route, price, threshold, recipient):
msg = MIMEText(
f"Price drop alert! {route}: ${price:.0f} "
f"(below your ${threshold:.0f} threshold)"
)
msg["Subject"] = f"Flight Deal: {route} at ${price:.0f}"
msg["From"] = "alerts@example.com"
msg["To"] = recipient
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login("alerts@example.com", "your_app_password")
server.send_message(msg)
# Μετά από κάθε scrape, ελέγξτε για προσφορές
min_price = df["price_usd"].min()
threshold = 250
if min_price < threshold:
send_price_alert("SFO-JFK", min_price, threshold,
"you@email.com")
Για προτεινόμενη συχνότητα scraping: δύο φορές την ημέρα είναι αρκετές για προσωπική παρακολούθηση τιμών (ο τυχαίος χρονισμός μειώνει τον κίνδυνο εντοπισμού). Κάθε 4–6 ώρες αν παρακολουθείτε για επαγγελματική χρήση. Ωριαία μόνο σε περιόδους έντονης προσφοράς, και μόνο προσωρινά.
Ο Εύκολος Δρόμος: Thunderbit Scheduled Scraper
Αν η διαχείριση cron jobs, server που τρέχει συνεχώς και proxy configs μοιάζει με περισσότερη υποδομή απ’ όση θέλετε να συντηρείτε, το Thunderbit's Scheduled Scraper χειρίζεται την ίδια χρήση χωρίς όλο αυτό το βάρος. Περιγράφετε το διάστημα scraping με απλά λόγια, βάζετε τα URLs του Google Flights και το scraper τρέχει αυτόματα πάνω στο cloud infrastructure του Thunderbit — με ενσωματωμένη διαχείριση anti-bot και απευθείας εξαγωγή αποτελεσμάτων σε Google Sheets, Excel ή Airtable. Δεν αντικαθιστά την πλήρη Python προσέγγιση (χάνετε σε παραμετροποίηση), αλλά για το συγκεκριμένο use case «θέλω ένα spreadsheet για παρακολούθηση τιμών», είναι η ταχύτερη διαδρομή. Μπορείτε να το δοκιμάσετε στο free tier.
Όταν η Python Είναι Υπερβολική: No-Code Τρόποι για Scrape του Google Flights
Αφού χτίσετε όλα τα παραπάνω, θα είμαι ειλικρινής: είναι πολλά τα κινούμενα μέρη. Δεν χρειάζονται όλοι αυτό το επίπεδο ελέγχου. Οι selectors σπάνε, οι proxies θέλουν εναλλαγή, τα cron jobs χρειάζονται παρακολούθηση. Αν ο στόχος σας είναι «να μπαίνουν οι τιμές πτήσεων σε ένα spreadsheet τακτικά», υπάρχουν πιο γρήγορες επιλογές.
Σύγκριση: DIY Python vs. API Services vs. Thunderbit
| Προσέγγιση | Χρόνος Ρύθμισης | Απαιτεί Κώδικα | Χειρισμός Anti-Bot | Προγραμματισμός | Κόστος |
|---|---|---|---|---|---|
| DIY Playwright (αυτό το tutorial) | 1–2 ώρες | Python (ενδιάμεσο) | Χειροκίνητη ρύθμιση | Χειροκίνητα (cron) | Δωρεάν + κόστος proxies |
| SerpApi Google Flights endpoint | 15 λεπτά | Μόνο API calls | Χειρίζεται | Μέσω API | ~$50+/μήνα |
| Thunderbit Chrome Extension | 2 λεπτά | Καθόλου | Cloud scraping | Ενσωματωμένος scheduler | Διαθέσιμο δωρεάν πλάνο |
Μια σημείωση για το SerpApi: η Google κατέθεσε μήνυση DMCA εναντίον του SerpApi τον Δεκέμβριο του 2025, ισχυριζόμενη ότι τα requests τους αυξήθηκαν κατά 25.000% μέσα σε δύο χρόνια. Αυτή η νομική αβεβαιότητα αξίζει προσοχή αν αξιολογείτε API providers.
Πώς το Thunderbit Κάνει Scrape το Google Flights
Ανοίξτε τα αποτελέσματα αναζήτησης του Google Flights στο Chrome, πατήστε το κουμπί Thunderbit "AI Suggest Fields" — το AI διαβάζει τη σελίδα και προτείνει στήλες όπως airline, price, departure time, stops — ελέγξτε τα προτεινόμενα πεδία και πατήστε "Scrape." Τα αποτελέσματα εμφανίζονται σε πίνακα που μπορείτε να εξαγάγετε σε Excel, Google Sheets, Airtable ή Notion — όλα στο free tier.
Για το use case της παρακολούθησης τιμών ειδικά, το Scheduled Scraper του Thunderbit και το Cloud Scraping (που μπορεί να χειριστεί 50 σελίδες ταυτόχρονα) αντικαθιστούν ολόκληρη την υποδομή cron + proxy + server.
Η Python σας δίνει πλήρη έλεγχο και απεριόριστη παραμετροποίηση. Το Thunderbit σας δίνει ταχύτητα και μηδενική συντήρηση. Επιλέξτε με βάση τον πραγματικό σας στόχο. Αν θέλετε να μάθετε περισσότερα για no-code προσεγγίσεις scraping, δείτε τον οδηγό μας για τα καλύτερα no-code web scrapers.

Είναι Νόμιμο το Scraping του Google Flights; Τι Πρέπει να Γνωρίζετε
Οι χρήστες στα φόρουμ το ρωτούν συχνά: "scraping Google Flights directly violates the TOS that Google has." Δίκαιος προβληματισμός — ειδικά αφού το API καταργήθηκε και δεν υπάρχει εγκεκριμένη εναλλακτική.
Παράβαση Όρων Χρήσης vs. Νομική Ευθύνη
Οι Όροι Χρήσης της Google (ενημερώθηκαν στις 22 Μαΐου 2024) ορίζουν ότι οι χρήστες δεν πρέπει να "access or use the Services or any content through the use of any automated means (such as robots, spiders or scrapers)." Η παραβίαση των ToS είναι παραβίαση σύμβασης (αστικό θέμα) — δεν είναι το ίδιο με την παραβίαση του νόμου.
Το βασικό νομικό προηγούμενο: η υπόθεση hiQ v. LinkedIn (Ninth Circuit, 2022) καθόρισε ότι το scraping δημόσια διαθέσιμων δεδομένων δεν παραβιάζει το Computer Fraud and Abuse Act (CFAA). Ωστόσο, η υπόθεση έληξε με συμβιβασμό, και η αγωγή της Google τον Δεκέμβριο του 2025 κατά του SerpApi βασίζεται σε διαφορετική νομική θεωρία — το DMCA Section 1201 (παράκαμψη τεχνολογικών μέτρων προστασίας) — κάτι που μπορεί να είναι πιο σοβαρό.
Καλές Πρακτικές για Υπεύθυνο Scraping
- Βάλτε όρια στα requests σας — καθυστερήσεις 10–20 δευτερολέπτων με IP rotation
- Μην κάνετε scrape προσωπικά δεδομένα — οι τιμές πτήσεων είναι δημόσια εμφανιζόμενα συγκεντρωτικά δεδομένα
- Μην παρακάμπτετε CAPTCHA προγραμματιστικά (αυτό είναι το σημείο κινδύνου του DMCA)
- Χρησιμοποιείτε τα δεδομένα για προσωπική έρευνα, όχι για να χτίσετε ανταγωνιστικό εμπορικό προϊόν χωρίς σωστή άδεια
- Εξετάστε επίσημα APIs όπου υπάρχουν διαθέσιμα
Εναλλακτικές Πηγές Δεδομένων
Αν το scraping σάς φαίνεται πολύ ριψοκίνδυνο για το use case σας, υπάρχουν νόμιμες επιλογές API:
| Πάροχος | Κόστος | Free Tier | Σημειώσεις |
|---|---|---|---|
| SerpApi | $75–$3.750+/μήνα | 250 αναζητήσεις/μήνα | Άμεσο JSON του Google Flights (υπό νομικό έλεγχο) |
| Kiwi Tequila | Δωρεάν (affiliate model) | Απεριόριστο | Ιδανικό για startups και δοκιμές |
| Amadeus | Pay-as-you-go | 2.000 req/μήνα | 400+ αεροπορικές, δυνατότητα κράτησης |
| Skyscanner | Προσαρμοσμένο | Απαιτεί έγκριση | 52 αγορές, 30 γλώσσες |
Γράψαμε και πιο αναλυτική ανάλυση για τις νομικές επιπτώσεις του web scraping αν θέλετε την πλήρη εικόνα.
Συμπέρασμα και Βασικά Συμπεράσματα
Αυτά ήταν πολλά. Να τι έχει σημασία:
- Η Python + Playwright είναι η πιο ευέλικτη προσέγγιση για scraping του Google Flights, αλλά απαιτεί συνεχή συντήρηση
- Τα anti-bot μέτρα (καθυστερήσεις, εναλλαγή user-agent, residential proxies) δεν είναι προαιρετικά — είναι απαραίτητα για αξιοπιστία, ειδικά μετά το SearchGuard
- Οι selectors σπάνε συχνά — χρησιμοποιήστε
aria-labelκαι text-based selectors όπου γίνεται, επικυρώνετε τα αποτελέσματά σας και κρατήστε πρόγραμμα συντήρησης - Αυτοματοποιήστε με
scheduleή cron για να μετατρέψετε ένα one-off scrape σε πραγματικό price tracker με ιστορικά δεδομένα και email alerts - Το Thunderbit προσφέρει no-code εναλλακτική με ενσωματωμένο scheduling, cloud scraping και anti-bot handling — ιδανικό αν ο στόχος σας είναι ένα spreadsheet παρακολούθησης τιμών και όχι ένα coding project
- Σεβαστείτε τα νομικά όρια — βάλτε rate limiting, κάντε scrape μόνο δημόσια δεδομένα και εξετάστε εναλλακτικές API για εμπορική χρήση
Κατεβάστε τον κώδικα από αυτό το tutorial ή εγκαταστήστε το Thunderbit Chrome extension για τη γρήγορη λύση. Σε κάθε περίπτωση, θα παρακολουθείτε τις τιμές των πτήσεων αντί να ανανεώνετε χειροκίνητα το Google Flights.
Για περισσότερες τεχνικές Python scraping, δείτε τους οδηγούς μας για το πώς να κάνετε web scrape με Python και τα καλύτερα Python web scraping tools.
Συχνές Ερωτήσεις
1. Μπορώ να κάνω scrape το Google Flights χωρίς Python;
Ναι. API services όπως το SerpApi και το Kiwi Tequila παρέχουν δομημένα δεδομένα πτήσεων μέσω API calls (χωρίς να χρειάζεται browser automation). Για μια πλήρως no-code προσέγγιση, το Thunderbit Chrome extension μπορεί να κάνει scrape τα αποτελέσματα του Google Flights απευθείας από το browser σας με πεδία που προτείνονται από AI και εξαγωγή με ένα κλικ.
2. Η Google μπλοκάρει το scraping πτήσεων;
Η Google χρησιμοποιεί ανίχνευση bot (SearchGuard), CAPTCHA και rate limiting. Με σωστά anti-bot μέτρα — τυχαίες καθυστερήσεις, εναλλαγή user-agent, residential proxies και stealth browser settings — μπορείτε να διατηρήσετε αξιόπιστο scraping σε μέτριους όγκους. Δείτε την παραπάνω ενότητα anti-bot για συγκεκριμένες τεχνικές και όρια.
3. Πόσο συχνά πρέπει να κάνω scrape το Google Flights για παρακολούθηση τιμών;
Δύο φορές την ημέρα (σε τυχαίες ώρες) αρκούν για προσωπική παρακολούθηση τιμών και κρατούν χαμηλό τον κίνδυνο εντοπισμού. Για επιχειρησιακή παρακολούθηση, κάθε 4–6 ώρες με proxy rotation. Αποφύγετε το ωριαίο scraping εκτός από σύντομες περιόδους προσφορών — αυξάνει σημαντικά την πιθανότητα αποκλεισμού.
4. Υπάρχει δωρεάν Google Flights API;
Το επίσημο Google QPX Express API καταργήθηκε τον Απρίλιο του 2018. Δεν υπάρχει δωρεάν επίσημη αντικατάσταση. Η πιο κοντινή δωρεάν επιλογή είναι το Kiwi Tequila API (affiliate model, απεριόριστες αναζητήσεις). Το SerpApi προσφέρει 250 δωρεάν αναζητήσεις τον μήνα. Για τους περισσότερους χρήστες, το scraping ή ένα no-code εργαλείο όπως το Thunderbit είναι η πρακτική λύση.
5. Γιατί οι CSS selectors μου στο Google Flights επιστρέφουν συνέχεια κενά δεδομένα;
Η Google χρησιμοποιεί Closure Compiler για να δημιουργεί obfuscated class names που αλλάζουν σε κάθε build. Το A/B testing και οι διαφορές locale προκαλούν επίσης μεταβολές στη δομή HTML μεταξύ χρηστών. Η λύση: χρησιμοποιήστε aria-label attributes και text-based selectors αντί για class names, προσθέστε συνάρτηση επικύρωσης για να πιάνετε γρήγορα τις αστοχίες και ελέγχετε τους selectors σας κάθε μήνα. Δείτε την ενότητα συντήρησης selectors για αναλυτική στρατηγική.
Μάθετε Περισσότερα


