Google Flights mit Python scrapen: Von Code bis zu Preisalarmen

Zuletzt aktualisiert am June 18, 2026
Google Flights mit Python scrapen: Von Code bis zu Preisalarmen

Google hat seine Flights API schon 2018 dichtgemacht, doch die Flugpreise tanzen munter weiter — auf einer einzigen Inlandsroute mitunter bis zu 17 Mal in 48 Stunden. Wer programmgesteuert an diese Daten will, kommt am Scrapen kaum vorbei.

Ich habe über längere Zeit diverse Wege ausprobiert, Flugdaten aus Google herauszuholen, und das Spielfeld hat sich gehörig verschoben — vor allem, seit Google im Januar 2025 SearchGuard scharf geschaltet hat. In diesem Leitfaden zeige ich dir, wie du mit Playwright einen funktionierenden Python-Scraper für Google Flights aufsetzt, wie du die Anti-Bot-Mechanismen aushebelst, an denen die meisten hängen bleiben, und wie du das Ganze zu einem automatisierten Preis-Tracker mit Warnmeldungen ausbaust. Magst du es ganz ohne Code, gibt es zum Schluss noch einen No-Code-Shortcut mit Thunderbit, der dich in etwa zwei Minuten ans gleiche Ziel bringt.

Warum Google Flights mit Python scrapen?

Google Flights gibt bei der Flugsuche den Ton an. Die Sichtbarkeit in den US-Mobil-Suchergebnissen kletterte auf 47,6 % und ließ damit alle großen OTAs hinter sich. Der dahinterstehende Metasuchmarkt im Reisebereich wird 2024 auf 8,33 Milliarden $ taxiert und legt mit 30,2 % CAGR zu. Seit dem endgültigen Aus der QPX-Express-API am 10. April 2018 führt allerdings kein offizieller Weg mehr zu diesen Daten.

Parallel schwanken die Preise für ein und dieselbe Verbindung um bis zu 50 % — die Spanne zwischen günstigstem und teuerstem Tarif beträgt im Schnitt rund 20 US-Dollar. Airlines wie Delta jonglieren mit 77 Tarifstufen für ihre dynamische Preisgestaltung. Der durchschnittliche US-Returnflug Anfang 2026 schlägt mit 408 US-Dollar zu Buche, und die Flugpreise liegen 14,9 % über dem Vorjahreswert.

Marktbeherrschende Plattform, keine API, Preise im Dauerflimmern — kein Wunder, dass das Scrapen von Google Flights mit Python zu den beliebtesten Projekten auf GitHub und in Reise-Foren zählt.

Davon profitieren vor allem diese Gruppen:

NutzertypAnwendungsfallHauptvorteil
PrivatreisendePreise für bestimmte Strecken über längere Zeit beobachtenIm Schnitt $50 pro Flug sparen
ReisebürosWettbewerbsfähige PreisanalysenPreisparität in Echtzeit überwachen
Corporate-Travel-TeamsKostenoptimierung über verschiedene Routen hinweg10–30 % Ersparnis bei Geschäftsreisen
EntwicklerFlugpreis-Vergleichs-Apps bauenProgrammgesteuerter Zugriff auf Preisdaten
ForschendeAnalyse von Preisschwankungen bei AirlinesFür akademische und Marktstudien

In Foren machen Nutzer kein Geheimnis daraus, warum sie zum Scraping gewechselt sind: „Die Google Flights API wurde eingestellt, also sollte ich stattdessen Web Scraping verwenden“ liest man immer wieder. Und der Aufwand zahlt sich aus: Hopper beziffert seine Prognosegenauigkeit auf 95 % und wertet täglich über 5 Milliarden Preisabfragen aus, während die Expedia-Daten von 2026 zeigen, dass eine Buchung 8–15 Tage im Voraus bei Inlandsflügen rund $25 spart.

Welche Daten kannst du aus Google Flights scrapen?

Eine Ergebnisseite von Google Flights steckt voller Datenfelder. Typischerweise verfügbar sind:

  • Name der Airline (inklusive Logo)
  • Abflugzeit und Flughafencode
  • Ankunftszeit und Flughafencode
  • Gesamtdauer des Fluges
  • Anzahl der Stopps und Umstiegsdetails (Flughafen, Dauer, Übernachtung)
  • Ticketpreis (je nach Währung)
  • CO2-Emissionen (kg CO2e, mit prozentualem Vergleich zu typischen Flügen)
  • Reiseklasse, Flugnummer, Flugzeugmodell
  • Beinfreiheit
  • Ausstattung (WLAN, Steckdosen, Media-Streaming)
  • Preisniveau (niedrig/typisch/hoch)
  • Hinweise zu Verspätungen („Oft mehr als 30 Min. verspätet“)

Was am Ende verfügbar ist, hängt von Route, Datum und Ticketart ab (Einweg vs. Hin- und Rückflug). So sieht ein einzelner gescrapter Flugdatensatz als JSON aus:

{
  "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-Umgebung einrichten

Bevor der Scraping-Code losgeht, brauchst du ein paar Grundlagen.

Voraussetzungen:

  • Schwierigkeitsgrad: Mittel
  • Benötigte Zeit: ca. 1–2 Stunden für das komplette Tutorial
  • Was du brauchst: Python 3.7+, grundlegende Python-Kenntnisse, ein Browser auf Chromium-Basis

Benötigte Bibliotheken installieren

Wir setzen auf Playwright für die Browserautomatisierung (Google Flights wird zu 100 % per JavaScript gerendert — einfache HTTP-Requests liefern nichts Brauchbares), dazu ein paar Helfer:

pip install playwright playwright-stealth pandas
playwright install chromium
  • Playwright — Browser-Automatisierung ohne GUI, verarbeitet JavaScript-Rendering, integrierte Warte-Mechanismen
  • playwright-stealth — kaschiert typische Bot-Erkennungssignale
  • pandas — später für Datenanalyse und CSV-Export

Warum Playwright statt Selenium oder Requests?

Mit requests und BeautifulSoup allein kommst du bei Google Flights nicht weit — der Inhalt entsteht komplett per JavaScript. Du brauchst einen echten Browser.

FeaturePlaywrightSeleniumrequests + BS4
JS-RenderingVoll unterstütztVoll unterstütztKeins
GeschwindigkeitInsgesamt 42 % schnellerBasiswertFür diesen Anwendungsfall nicht geeignet
Async-SupportNativNur sequenziellNicht relevant
Speicherverbrauch30 % geringerHöherMinimal
Umgehung von Bot-ErkennungGut (mit Stealth)Leichter erkennbarNicht relevant

Playwright ist schneller, moderner und beim Async-Support deutlich überlegen. Für Google Flights also die klare Wahl.

Schritt für Schritt: Google Flights mit Python scrapen

Jetzt zum Herzstück. Wir setzen den Scraper Stück für Stück zusammen.

google-flights-scraping-workflow.webp

Schritt 1: Datenklassen definieren

Den Anfang machen Suchparameter und Flugdaten, sauber strukturiert über Python-Dataclasses. Das hält den Code übersichtlich und erleichtert spätere Erweiterungen.

from dataclasses import dataclass, field
from typing import Optional, List

@dataclass
class SearchParams:
    origin: str          # z. B. "SFO"
    destination: str     # z. B. "JFK"
    departure_date: str  # z. B. "2026-05-15"
    return_date: Optional[str] = None
    trip_type: str = "one-way"  # "one-way" oder "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 = ""

Jedes Feld spiegelt unmittelbar wider, was wir von der Seite herausholen. Mit dieser Struktur musst du später keine unübersichtlichen Dictionaries mehr durch den Code schleusen.

Schritt 2: Die URL-Struktur von Google Flights verstehen

Google Flights packt die Suchparameter als Base64-kodierte Protobuf-Daten in den URL-Parameter tfs. Diese Kodierung kannst du reverse-engineeren — oder du nimmst den bequemen Weg und baust eine URL in natürlicher Sprache.

Am einfachsten ist dieses Suchmuster:

https://www.google.com/travel/flights?q=flights+from+SFO+to+JFK+on+2026-05-15&curr=USD

Für mehr Kontrolle lässt sich die URL auch programmatisch erzeugen:

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"

Die Alternative — das Reverse-Engineering der Protobuf-Kodierung — liefert mehr Präzision, fliegt aber in dem Moment auseinander, in dem Google das interne Format ändert. Bibliotheken wie fast-flights auf GitHub dekodieren das Protobuf und umgehen so das HTML-Parsing komplett, doch das ist der Ansatz für Fortgeschrittene.

Schritt 3: Browser starten und zu Google Flights navigieren

Jetzt das Playwright-Setup. Mit playwright-stealth drücken wir das Erkennungsrisiko von vornherein.

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 für Consent vorab setzen, um das Popup zu überspringen
        await context.add_cookies([{
            "name": "SOCS",
            "value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
            "domain": ".google.com",
            "path": "/"
        }])
        page = await context.new_page()

Im Produktivbetrieb starten wir headless (zum Debuggen einfach auf headless=False umstellen), legen ein realistisches Viewport- und User-Agent-Profil an und setzen das SOCS-Cookie vorab, damit das Consent-Popup gar nicht erst aufpoppt — mehr dazu im Anti-Bot-Abschnitt.

Schritt 4: Zu den Suchergebnissen navigieren

Lade die erzeugte URL und warte, bis die Flugergebnisse auftauchen:

        url = build_flights_url(
            params.origin, params.destination, params.departure_date
        )
        await page.goto(url, wait_until="networkidle")

        # Warten, bis die Flugdaten geladen sind
        await page.wait_for_selector(
            "li.pIav2d", timeout=15000
        )

Ein Timeout an dieser Stelle hat meist eine von zwei Ursachen: Entweder blockiert das Consent-Popup die Seite (siehe den Cookie-Fix in Schritt 3) oder Google serviert dir eine CAPTCHA-Seite. Beides nehmen wir uns im Anti-Bot-Abschnitt vor.

Schritt 5: Alle Flugergebnisse laden

Google Flights versteckt zusätzliche Treffer hinter einem Button mit der Aufschrift „More flights anzeigen“. Den musst du so oft klicken, bis alle Flüge sichtbar sind:

        # „Show more flights“ klicken, bis alle Ergebnisse geladen sind
        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

Die Schleife klickt den Button, gibt dem Nachladen 2 Sekunden und steigt aus, sobald der Button verschwindet. In meinen Tests hatten die meisten Routen 1–3 Ergebnisseiten.

Schritt 6: Flugdaten mit CSS-Selektoren extrahieren

Jetzt lesen wir die eigentlichen Flugdaten aus der geladenen Seite. Hier die Selektoren (Stand April 2026 verifiziert — warum dieses Datum zählt, erkläre ich gleich im Wartungsabschnitt):

        flights = []
        cards = await page.query_selector_all("li.pIav2d")

        for card in cards:
            flight = FlightData()

            # Name der Airline
            airline_el = await card.query_selector(
                "div.sSHqwe span:not([class])"
            )
            if airline_el:
                flight.airline = (await airline_el.inner_text()).strip()

            # Abflugzeit
            dep_el = await card.query_selector(
                'span[aria-label*="Departure time"]'
            )
            if dep_el:
                flight.departure_time = (await dep_el.inner_text()).strip()

            # Ankunftszeit
            arr_el = await card.query_selector(
                'span[aria-label*="Arrival time"]'
            )
            if arr_el:
                flight.arrival_time = (await arr_el.inner_text()).strip()

            # Dauer
            dur_el = await card.query_selector("div.gvkrdb")
            if dur_el:
                flight.duration = (await dur_el.inner_text()).strip()

            # Stopps
            stops_el = await card.query_selector("div.EfT7Ae span")
            if stops_el:
                flight.stops = (await stops_el.inner_text()).strip()

            # Preis
            price_el = await card.query_selector(
                "div.FpEdX span"
            )
            if price_el:
                flight.price = (await price_el.inner_text()).strip()

            # CO2-Emissionen
            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

Wichtig: Klassennamen wie pIav2d, sSHqwe und FpEdX stammen aus Googles Closure Compiler und können sich mit jedem Build verschieben. Die aria-label-Selektoren halten deutlich länger. Eine belastbare Wartungsstrategie folgt weiter unten.

Schritt 7: Ergebnisse als JSON oder CSV speichern

Zum Abschluss sichern wir die gescrapten Daten mit Zeitstempel — unverzichtbar fürs spätere Preis-Tracking:

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)

    # Zusätzlich als CSV speichern
    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"{len(flights)} Flüge gescrapt")

asyncio.run(main())

Lass das laufen, und du erhältst flights.json sowie flights.csv mit deinen Ergebnissen. In meinen Tests liefert eine SFO-JFK-Suche typischerweise 30–80 Flugoptionen und braucht etwa 15–20 Sekunden.

Überlebensleitfaden gegen Bot-Erkennung beim Google Flights Scraping

An dieser Stelle hören die meisten Tutorials auf. Und genau hier scheitern die meisten Scraper. Google hat SearchGuard im Januar 2025 eingeführt und damit über Nacht fast jeden SERP-Scraper lahmgelegt. Google selbst spricht von „das Ergebnis von zehntausenden Arbeitsstunden und Investitionen in Millionenhöhe“. Beim Scraping bewerten Experten Google Flights mit Schwierigkeitsgrad 4/5.

Kein Konkurrenzartikel geht hier wirklich in die Tiefe — dabei ist genau das der Hauptgrund, warum Scraper plötzlich stillstehen. Hier siehst du, womit du rechnen musst und wie du damit umgehst.

anti-bot-survival-guide.webp

Zufällige Pausen zwischen Anfragen

Der simpelste Schutz gegen Rate Limiting. Zwei Zeilen Code, ordentlicher Effekt:

import time
import random

time.sleep(random.uniform(3, 7))

Setz das zwischen Seitenaufrufe. Feste Intervalle (immer exakt 5 Sekunden) sind verräterisch — zufällig ist besser.

User-Agent-Rotation

Bei jeder Anfrage denselben User-Agent-String mitzuschicken, fällt sofort auf. Nimm eine rotierende Liste:

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-Erkennung umgehen

Google klopft das Flag navigator.webdriver und weitere Automatisierungssignale ab. Die Bibliothek playwright-stealth kaschiert viele davon; ergänzend setzt du die in Schritt 3 gezeigten Startargumente. Die wichtigsten Flags:

args=[
    "--disable-blink-features=AutomationControlled",
    "--disable-dev-shm-usage",
    "--no-first-run",
]

Damit umschiffst du die einfache Erkennung. SearchGuard geht weiter — es belauert Mausgeschwindigkeit, Tastatur-Timing und Scrollmuster —, aber für Scraping in moderatem Umfang reichen Stealth-Modus plus realistische Pausen oft aus.

Proxy-Rotation: Datacenter vs. Residential

Für mehr als nur eine Handvoll Abfragen brauchst du Proxies. Der Unterschied ist nicht nebensächlich:

FeatureDatacenter-ProxiesResidential-Proxies
Geschwindigkeit100–1.000 Mbps10–100 Mbps
Erfolgsquote bei Google20–40 %85–95 %
Kosten0,10–0,50 $/IP/Monat5–15 $/GB
ErkennungsrisikoHochSehr niedrig

Beim Scrapen geschützter Seiten sind Residential Proxies pro erfolgreicher Anfrage etwa 180-mal günstiger. Preisbeispiele 2026: Smartproxy ab 7 $/GB, Bright Data 8,40 $/GB, Oxylabs 8 $/GB.

So bindest du einen Proxy in Playwright ein:

browser = await pw.chromium.launch(
    proxy={"server": "http://proxy-host:port",
           "username": "user", "password": "pass"}
)

Umgang mit dem Cookie-Consent-Popup

Nutzer melden es immer wieder: Das Zustimmungs-Popup verstellt die Sicht — „zuerst zeigt Google das Popup ‚I agree to terms and conditions‘ an.“ Am saubersten löst du das mit dem vorab gesetzten SOCS-Cookie (siehe Schritt 3). Reicht das nicht, klickst du es einfach weg:

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  # Kein Popup vorhanden

Beachte: Der Button-Text richtet sich nach der Sprache — auf Deutsch heißt er etwa „Alle akzeptieren“, auf Französisch „Tout accepter“.

Schnellübersicht Anti-Bot-Techniken

TechnikSchwierigkeitsgradWirksamkeitCode nötig?
Zufällige Pausen (2–7 Sek.)NiedrigMittel2 Zeilen
User-Agent-RotationNiedrigMittel5 Zeilen
Headless-Erkennung umgehenMittelHochPlaywright-Startargumente
playwright-stealth-PluginMittel60–80 % bei einfachen Sitespip install
Proxy-Rotation (Datacenter)MittelMittelKonfiguration
Proxy-Rotation (Residential)Mittel85–95 % ErfolgKonfiguration
Consent-Cookie vorab setzen (SOCS)NiedrigErforderlich1 Zeile

Empfohlene sichere Raten: 10–20 Sekunden Verzögerung zwischen den Anfragen bei IP-Rotation. Googles Schwellen liegen ungefähr bei 100 Anfragen pro Minute und IP, bevor ein 429 droht; dauerhaft mehr als 1.000 Anfragen pro Tag und IP können zu temporären Sperren führen.

Warum deine Google-Flights-Selektoren ständig kaputtgehen – und wie du das behebst

Das mit Abstand größte Problem. Foren quellen über vor Varianten von „Ich bekomme nur 14 leere Listen zurück.“ Jeder Leitfaden reicht dir Selektoren. Kaum einer erklärt, warum sie zerbrechen.

Warum sich Google-Flights-Selektoren ändern

Dahinter stecken drei Hauptgründe:

  1. Obfuskation durch den Closure Compiler. Google erzeugt per Closure Stylesheets über goog.setCssNameMapping() Klassennamen wie BVAVmf und YMlIz. Die wechseln mit jedem Build — manchmal wöchentlich.

  2. A/B-Tests. Verschiedene Nutzer bekommen zeitgleich verschiedene HTML-Strukturen serviert. Dein Scraper läuft bei dir tadellos und versagt bei jemand anderem in einer anderen Region.

  3. Lokale Unterschiede. Nutzer in der EU sehen andere Begriffe, Layouts und sogar andere Datenfelder als Nutzer in den USA.

Robuste Selektoren schreiben

Setz auf Selektoren, die an Bedeutung statt an Optik hängen:

# Fragil — bricht bei jedem Build
price_el = await card.query_selector("div.BVAVmf > div.YMlIz")

# Robuster — an Accessibility-Labels gebunden
dep_el = await card.query_selector('span[aria-label*="Departure time"]')

# Ebenfalls robust — textbasierte Suche
more_btn = page.locator('button:has-text("Show more flights")')

Hierarchie der Selektor-Stabilität (von stabil nach instabil):

  1. aria-label-Attribute — für Barrierefreiheit gedacht, selten geändert
  2. data-*-Attribute — explizit für Funktionen ergänzt
  3. role-Attribute — ARIA-Rollen sind semantisch
  4. textbasierte Selektoren — passen auf sichtbaren Inhalt
  5. Teilstring-Matching bei Klassen — z. B. [class*="price"]
  6. Vollständig obfuskierte Klassennamen — wenn möglich vermeiden

Eine Validierungsfunktion hinzufügen

Lass kaputte Selektoren nicht klammheimlich leere Daten produzieren. Fang das früh ab:

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"Fehlendes Feld '{field_name}' — Selektoren müssen möglicherweise aktualisiert werden"
            )
            valid = False
    return valid

Lass das bei jedem gescrapten Flug mitlaufen. Tauchen Warnungen auf, schaust du dir die Seite an und ziehst deine Selektoren nach.

Strategie zur Wartung von Selektoren

  • Selektoren monatlich prüfen oder sofort, wenn die Ausgabequalität sinkt
  • Selektoren in einem separaten Konfigurations-Dictionary halten, damit Updates leicht von der Hand gehen
  • Die Selektoren in diesem Artikel wurden zuletzt im April 2026 verifiziert
  • Als Alternative kommt die fast-flights library in Frage — sie setzt auf Protobuf-Dekodierung statt CSS-Selektoren und umschifft dieses Problem ganz (bleibt aber ebenfalls anfällig, sobald Google interne Datenformate ändert)

Vom einmaligen Scrape zum automatisierten Google-Flights-Preis-Tracker

Die meisten Tutorials enden bei „als JSON speichern“. Im Titel dieses Artikels stehen aber „Preisalarme“. Zeit, das einzulösen.

scraper-to-price-tracker-sfo-jfk.webp

Den Scraper automatisch planen

Option 1: Python-Bibliothek schedule (am einfachsten, plattformübergreifend):

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)

Option 2: Cron-Job (Linux/Mac):

# Täglich um 6:00 und 18:00 ausführen
0 6,18 * * * cd /path/to/scraper && python scraper.py

Option 3: Windows Aufgabenplanung — leg eine einfache Aufgabe an, die python scraper.py nach deinem Zeitplan startet.

Der Haken: Alle drei Varianten setzen einen Rechner voraus, der durchgehend läuft. Machst du das auf einem Laptop, der in den Standby geht, fallen Läufe aus.

Historische Preisdaten speichern

Statt eine JSON-Datei immer wieder zu überschreiben, kannst du Daten an eine SQLite-Datenbank anhängen:

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()

Nach einer Woche mit zwei Scrapes pro Tag hast du genug Daten beisammen, um Trends abzulesen.

Preistrends analysieren und Alarme setzen

Finde aus deinen historischen Daten die günstigste Option heraus:

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"Am günstigsten: ${cheapest['price_usd']:.0f} am "
    f"{cheapest['scrape_date']} ({cheapest['airline']})"
)

Stoß eine E-Mail-Benachrichtigung an, sobald ein Preis unter deine Schwelle rutscht:

import smtplib
from email.mime.text import MIMEText

def send_price_alert(route, price, threshold, recipient):
    msg = MIMEText(
        f"Preisalarm! {route}: ${price:.0f} "
        f"(unter deinem Schwellenwert von ${threshold:.0f})"
    )
    msg["Subject"] = f"Flugangebot: {route} für ${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)

# Nach jedem Scrape nach günstigen Angeboten prüfen
min_price = df["price_usd"].min()
threshold = 250
if min_price < threshold:
    send_price_alert("SFO-JFK", min_price, threshold,
                     "you@email.com")

Empfohlene Scraping-Frequenz: Für privates Preis-Monitoring genügen zwei Läufe pro Tag völlig (zufällige Zeitpunkte drücken das Erkennungsrisiko). Für geschäftliche Überwachung alle 4–6 Stunden. Stündliches Scraping nur während kurzer Rabattaktionen und nur vorübergehend.

Der einfache Weg: Thunderbits geplanter Scraper

Klingen Cron-Jobs, ein dauerhaft laufender Server und Proxy-Konfigurationen nach mehr Infrastruktur, als du pflegen magst, deckt Thunderbits Scheduled Scraper denselben Anwendungsfall ohne diesen Ballast ab. Du beschreibst das Intervall in natürlicher Sprache, fütterst deine Google-Flights-URLs ein, und der Scraper läuft automatisch in Thunderbits Cloud-Infrastruktur — mit eingebauter Anti-Bot-Behandlung und direktem Export nach Google Sheets, Excel oder Airtable. Den vollen Python-Ansatz ersetzt er nicht, weil du weniger Freiheiten hast, aber für den konkreten Fall „Ich will eine Preis-Tracking-Tabelle“ ist er der schnellste Weg. Ausprobieren kannst du ihn mit dem kostenlosen Tarif.

Wenn Python überdimensioniert ist: No-Code-Wege, um Google Flights zu scrapen

Seien wir ehrlich: Das ist eine Menge Technik. Nicht jeder braucht so viel Kontrolle. Selektoren brechen, Proxies wollen rotiert, Cron-Jobs überwacht werden. Lautet dein Ziel schlicht „Flugpreise regelmäßig in eine Tabelle bringen“, gibt es schnellere Wege.

Vergleich: Python selbst bauen vs. API-Dienste vs. Thunderbit

AnsatzEinrichtungszeitProgrammierung nötigAnti-Bot abgedecktPlanungKosten
DIY mit Playwright (dieses Tutorial)1–2 StundenPython (Mittelstufe)Manuelle KonfigurationManuell (Cron)Kostenlos + Proxy-Kosten
SerpApi Google Flights Endpoint15 Min.Nur API-AufrufeInbegriffenÜber APIca. 50 $/Monat oder mehr
Thunderbit Chrome Extension2 Min.KeineCloud-ScrapingIntegrierter SchedulerKostenloser Tarif verfügbar

Ein Wort zu SerpApi: Google reichte im Dezember 2025 eine DMCA-Klage gegen SerpApi ein mit der Behauptung, deren Anfragen seien binnen zwei Jahren um 25.000 % gestiegen. Diese rechtliche Unsicherheit solltest du im Hinterkopf behalten, wenn du API-Anbieter abwägst.

So scrapt Thunderbit Google Flights

Öffne deine Google-Flights-Suchergebnisse in Chrome, klick auf Thunderbits Schaltfläche „KI-Felder vorschlagen“ — die KI liest die Seite aus und schlägt Spalten wie Airline, Preis, Abflugzeit und Stopps vor —, prüf die Vorschläge und klick dann auf „Scrapen“. Die Ergebnisse erscheinen in einer Tabelle, die du nach Excel, Google Sheets, Airtable oder Notion exportierst — alles im kostenlosen Tarif.

Für genau den Preis-Tracking-Fall ersetzen Thunderbits Scheduled Scraper und Cloud Scraping (mit bis zu 50 Seiten parallel) die komplette Cron-/Proxy-/Server-Infrastruktur.

Python gibt dir volle Kontrolle und grenzenlose Anpassbarkeit. Thunderbit liefert Tempo und null Wartungsaufwand. Wähle nach deinem echten Ziel. Magst du mehr über No-Code-Scraping erfahren, lies unseren Leitfaden zu den besten No-Code-Web-Scrapern.

flight-data-options-comparison.webp

Ist das Scrapen von Google Flights legal? Das solltest du wissen

In Foren kommt das regelmäßig hoch: „Google Flights direkt zu scrapen verstößt gegen Googles AGB.“ Ein berechtigter Einwand — gerade weil die API eingestellt wurde und es keine offiziell gesegnete Alternative gibt.

AGB-Verstoß vs. rechtliche Haftung

Googles Nutzungsbedingungen (aktualisiert am 22. Mai 2024) halten fest, dass Nutzer die Dienste oder Inhalte nicht „mithilfe automatisierter Mittel (z. B. Roboter, Spider oder Scraper) abrufen oder verwenden“ dürfen. Ein AGB-Verstoß ist eine Vertragsverletzung (zivilrechtlich) — und damit nicht dasselbe wie ein Gesetzesverstoß.

Der wichtigste juristische Präzedenzfall ist hiQ v. LinkedIn (Ninth Circuit, 2022): Demnach verstößt das Scraping öffentlich zugänglicher Daten nicht gegen den Computer Fraud and Abuse Act (CFAA). Der Fall endete jedoch mit einem Vergleich, und Googles Klage gegen SerpApi vom Dezember 2025 fußt auf einer anderen juristischen Theorie — DMCA Section 1201 (Umgehung technischer Schutzmaßnahmen) — und wiegt potenziell schwerer.

Best Practices für verantwortungsvolles Scraping

  • Anfragen drosseln — 10–20 Sekunden Verzögerung mit IP-Rotation
  • Keine personenbezogenen Daten scrapen — Flugpreise sind öffentlich angezeigte, aggregierte Daten
  • CAPTCHAs nicht programmgesteuert umgehen (genau hier lauert das DMCA-Risiko)
  • Daten für persönliche Recherche nutzen, nicht für ein konkurrierendes kommerzielles Produkt ohne passende Lizenz
  • Offizielle APIs prüfen, sofern verfügbar

Alternative Datenquellen

Wirkt Scraping für deinen Fall zu riskant, gibt es legale API-Optionen:

AnbieterKostenKostenloser TarifHinweise
SerpApi75–3.750 $/Monat oder mehr250 Suchen/MonatDirektes Google-Flights-JSON (rechtlich umstritten)
Kiwi TequilaKostenlos (Affiliate-Modell)UnbegrenztGut für Startups und Tests
AmadeusPay-as-you-go2.000 Anfragen/Monat400+ Airlines, Buchungsfunktionen
SkyscannerIndividuellGenehmigung erforderlich52 Märkte, 30 Sprachen

Wir haben außerdem einen ausführlicheren Artikel zu den rechtlichen Auswirkungen von Web Scraping, falls du das Thema bis ins Detail aufdröseln willst.

Fazit und wichtigste Erkenntnisse

Das war viel auf einmal. Worauf es ankommt:

  • Python + Playwright ist der flexibelste Ansatz für Google Flights Scraping, verlangt aber laufende Wartung
  • Anti-Bot-Maßnahmen wie Verzögerungen, User-Agent-Rotation und Residential Proxies sind kein Nice-to-have — sie sind für Stabilität essenziell, gerade nach SearchGuard
  • Selektoren brechen häufig — nutze nach Möglichkeit aria-label- und textbasierte Selektoren, prüfe deine Ergebnisse und halte Wartungsintervalle ein
  • Automatisiere mit schedule oder Cron, um aus einem einmaligen Scrape einen echten Preis-Tracker mit historischen Daten und E-Mail-Warnungen zu formen
  • Thunderbit bietet die No-Code-Alternative mit integrierter Planung, Cloud-Scraping und Anti-Bot-Unterstützung — ideal, wenn du eine Preis-Tracking-Tabelle statt eines Programmierprojekts willst
  • Bleib innerhalb der rechtlichen Grenzen — drossele Anfragen, scrape nur öffentliche Daten und prüfe für kommerzielle Nutzung API-Alternativen

Nimm den Code aus diesem Tutorial oder installier die Thunderbit Chrome Extension für den Schnellweg. So oder so verfolgst du künftig Flugpreise, statt Google Flights pausenlos von Hand zu aktualisieren.

Mehr Python-Scraping-Techniken findest du in unseren Leitfäden zu Web Scraping mit Python und den besten Python-Web-Scraping-Tools.

FAQs

1. Kann ich Google Flights ohne Python scrapen?

Ja. API-Dienste wie SerpApi und Kiwi Tequila liefern strukturierte Flugdaten über API-Aufrufe (ohne Browser-Automatisierung). Für einen komplett No-Code-Ansatz kann die Thunderbit Chrome Extension Google-Flights-Ergebnisse direkt aus dem Browser scrapen — mit KI-gestützten Feldern und Export per Klick.

2. Blockiert Google das Scraping von Flugdaten?

Google setzt Bot-Erkennung (SearchGuard), CAPTCHAs und Rate Limiting ein. Mit den richtigen Anti-Bot-Maßnahmen — zufällige Verzögerungen, User-Agent-Rotation, Residential Proxies und Stealth-Browser-Einstellungen — lässt sich Scraping in moderatem Umfang zuverlässig betreiben. Konkrete Techniken und Schwellenwerte findest du im Anti-Bot-Abschnitt oben.

3. Wie oft sollte ich Google Flights für Preis-Tracking scrapen?

Zweimal täglich zu zufälligen Zeiten reicht fürs private Preis-Monitoring und hält das Erkennungsrisiko niedrig. Für geschäftliche Überwachung alle 4–6 Stunden mit Proxy-Rotation. Stündliches Scraping nur während kurzfristiger Tarifaktionen — sonst steigt die Blockierwahrscheinlichkeit deutlich.

4. Gibt es eine kostenlose Google-Flights-API?

Die offizielle Google QPX Express API wurde im April 2018 eingestellt. Einen kostenlosen offiziellen Ersatz gibt es nicht. Am nächsten kommt die kostenlose Kiwi Tequila API (Affiliate-Modell, unbegrenzte Suchen). SerpApi bietet 250 kostenlose Suchen pro Monat. Für die meisten ist Scraping oder ein No-Code-Tool wie Thunderbit der praktikabelste Weg.

5. Warum liefern meine Google-Flights-CSS-Selektoren immer wieder leere Daten?

Google erzeugt mit dem Closure Compiler obfuskierte Klassennamen, die sich mit jedem Build ändern. A/B-Tests und lokale Unterschiede sorgen zudem dafür, dass die HTML-Struktur zwischen Nutzern variiert. Die Lösung: aria-label-Attribute und textbasierte Selektoren statt Klassennamen verwenden, eine Validierungsfunktion einbauen, um Probleme früh zu erkennen, und die Selektoren monatlich prüfen. Eine ausführliche Strategie findest du im Abschnitt zur Selektor-Wartung.

Mehr erfahren

Ke
Ke
CTO bei Thunderbit | Senior Data Scientist & ML-Experte Mit fast zehn Jahren Erfahrung in Machine Learning und Data Science ist Ke Shen Absolvent der Columbia University und ehemaliger Senior Data Scientist bei Walmart Labs. Mit tiefgreifender, von Fachkollegen anerkannter Expertise in Python, R, Java und Statistik teilt er praxiserprobte Einblicke dazu, wie sich komplexe KI-Algorithmen von der Theorie in eine produktionsreife Architektur überführen lassen.
Inhaltsverzeichnis

Eine Webseite einfach per Anfrage scrapen

Sag einfach in normalem Deutsch, was du brauchst. Oder noch besser: sag gar nichts.

Thunderbit ausprobieren kostenlos
Daten mit KI extrahieren
Daten einfach zu Google Sheets, Airtable oder Notion übertragen
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week