Πώς να κάνετε Scrape το Google Flights με Python: Από τον Κώδικα στις Ειδοποιήσεις Τιμών

Τελευταία ενημέρωση: April 16, 2026
Πώς να κάνετε Scrape το Google Flights με Python: Από τον Κώδικα στις Ειδοποιήσεις Τιμών

Η 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.

ΧαρακτηριστικόPlaywrightSeleniumrequests + BS4
Απόδοση JSΠλήρης υποστήριξηΠλήρης υποστήριξηΚαμία
Ταχύτητα42% ταχύτερο συνολικάΒασική γραμμήΔεν εφαρμόζεται σε αυτήν τη χρήση
Async υποστήριξηΕγγενήςΜόνο σειριακήΔεν εφαρμόζεται
Χρήση μνήμης30% λιγότερηΥψηλότερηΕλάχιστη
Παράκαμψη ανίχνευσης botΚαλή (με stealth)Πιο εύκολο να εντοπιστείΔεν εφαρμόζεται

Το Playwright είναι ταχύτερο, πιο σύγχρονο και έχει καλύτερη υποστήριξη για async. Για το Google Flights ειδικά, είναι η ξεκάθαρη καλύτερη επιλογή.

Βήμα-Βήμα: Πώς να Κάνετε Scrape το Google Flights με Python

Αυτό είναι το βασικό κομμάτι του οδηγού. Θα χτίσουμε το scraper κομμάτι-κομμάτι.

google-flights-scraping-workflow.webp

Βήμα 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 των ανθρώπων σταματούν να δουλεύουν. Δείτε τι έχετε απέναντί σας και πώς να το αντιμετωπίσετε.

anti-bot-survival-guide.webp

Τυχαίες Καθυστερήσεις Ανάμεσα στα 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 ProxiesResidential Proxies
Ταχύτητα100–1.000 Mbps10–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

Πρόκειται για τρία πράγματα:

  1. Obfuscation από το Closure Compiler. Η Google χρησιμοποιεί Closure Stylesheets για να δημιουργεί class names όπως BVAVmf και YMlIz μέσω του goog.setCssNameMapping(). Αυτά αλλάζουν σε κάθε build — μερικές φορές κάθε εβδομάδα.

  2. A/B testing. Διαφορετικοί χρήστες βλέπουν ταυτόχρονα διαφορετικές HTML δομές. Το scraper σας μπορεί να δουλεύει στον υπολογιστή σας αλλά να αποτυγχάνει για κάποιον σε άλλη περιοχή.

  3. Διαφορές 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 (από το πιο σταθερό στο λιγότερο):

  1. aria-label attributes — συνδέονται με accessibility, αλλάζουν σπάνια
  2. data-* attributes — προστίθενται ρητά για λειτουργικότητα
  3. role attributes — τα ARIA roles είναι σημασιολογικά
  4. Text-based selectors — αντιστοιχούν στο ορατό περιεχόμενο
  5. Substring class matching — π.χ. [class*="price"]
  6. Πλήρη 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-to-price-tracker-sfo-jfk.webp

Προγραμματίστε το 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 endpoint15 λεπτάΜόνο API callsΧειρίζεταιΜέσω API~$50+/μήνα
Thunderbit Chrome Extension2 λεπτάΚαθόλου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.

flight-data-options-comparison.webp

Είναι Νόμιμο το 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 και δοκιμές
AmadeusPay-as-you-go2.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 για αναλυτική στρατηγική.

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

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