Slik skraper du Google Flights med Python: Fra kode til prisvarsler

Sist oppdatert April 16, 2026
Slik skraper du Google Flights med Python: Fra kode til prisvarsler

Google la ned Flights API-et sitt i 2018, men flyprisene fortsetter å endre seg — opptil 17 ganger i løpet av 48 timer for én enkelt innenriksrute. Hvis du vil ha programmatisk tilgang til disse dataene, er skraping i praksis den eneste reelle løsningen.

Jeg har brukt mye tid på å teste ulike måter å hente flydata fra Google på, og landskapet har endret seg kraftig — særlig etter at Google rullet ut SearchGuard i januar 2025. I denne guiden viser jeg deg hvordan du bygger en fungerende Python-scraper for Google Flights med Playwright, hvordan du håndterer anti-bot-mekanismene som stopper de fleste, og hvordan du kan utvide løsningen til en automatisert prisovervåker med varsler. Hvis du helst vil hoppe over koding helt, viser jeg også en no-code snarvei med Thunderbit som gir deg samme resultat på omtrent to minutter.

Hvorfor skrape Google Flights med Python?

Google Flights dominerer flysøk. Synligheten på mobil i USA økte til 47,6 %, og passerte alle de store OTA-ene. Markedet for reisesøkemotorer bak dette er verdsatt til $8,33 milliarder i 2024, med en årlig vekst på 30,2 %. Likevel finnes det ingen offisiell måte å hente disse dataene programmatisk på, siden QPX Express API-et ble stengt for godt 10. april 2018.

Samtidig kan flypriser svinge opptil 50 % for samme reise, med et gjennomsnittlig spenn på rundt 20 dollar mellom laveste og høyeste pris. Flyselskaper som Delta bruker 77 prisnivåer for dynamisk prising. Gjennomsnittlig tur-retur i USA tidlig i 2026 ligger på 408 dollar, og billettprisene er 14,9 % høyere enn året før.

Dominerende plattform, ingen API, ustabile priser. Derfor har skraping av Google Flights med Python blitt et av de mest populære prosjektene på GitHub og i reiseforum.

Dette er hvem som har nytte av det, og hvordan:

BrukertypeBruksområdeViktig gevinst
Private reisendeFølge prisene på bestemte ruter over tidSpar i snitt $50 per flyreise
ReisebyråerInnsikt i konkurransedyktig prisingOvervåking av prisparitet i sanntid
Bedrifters reiseteamKostnadsoptimalisering på tvers av ruter10–30 % lavere reisekostnader
UtviklereBygge apper for prissammenligningProgrammatisk tilgang til prisdata
ForskereAnalyse av prisvolatilitet i luftfartAkademisk og markedsrettet analyse

Brukere i forum er ærlige om hvorfor de går over til skraping: "Google Flights API ble lagt ned, så jeg må bruke webskraping i stedet" er et utsagn som går igjen. Og avkastningen er reell — Hopper hevder 95 % treffsikkerhet ved å analysere over 5 milliarder pristilbud daglig, mens Expedia-data for 2026 viser at bestilling 8–15 dager i forveien kan spare rundt $25 på innenriksflyvninger.

Hvilke data kan du skrape fra Google Flights?

En resultatside i Google Flights inneholder overraskende mye informasjon. Dette er det du vanligvis kan hente ut:

  • Navn på flyselskap (og logo)
  • Avgangstid og flyplasskode
  • Ankomsttid og flyplasskode
  • Total reisetid
  • Antall stopp og detaljer om mellomlandingene (flyplass, varighet, overnatting)
  • Billettpris (tilpasset valuta)
  • CO2-utslipp (kg CO2e, med prosentvis avvik fra typiske flyvninger)
  • Reiseklasse, flynummer, flymodell
  • Benplass
  • Tilleggstjenester (Wi‑Fi, strømuttak, mediestrømming)
  • Prisnivå (lavt/typisk/høyt)
  • Forsinkelsesvarsel ("Ofte forsinket med over 30 min")

Tilgjengeligheten varierer etter rute, dato og billettype (én vei vs. tur-retur). Slik kan én skrapet flypost se ut som 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"
    }
  ]
}

Slik setter du opp Python-miljøet ditt

Før vi skriver noe skrapekode, må du ha noen ting på plass.

Forutsetninger:

  • Vanskelighetsgrad: Middels
  • Tidsbruk: ca. 1–2 timer for hele guiden
  • Dette trenger du: Python 3.7+, grunnleggende Python-kunnskap, en nettleser basert på Chrome

Installer nødvendige biblioteker

Vi bruker Playwright til nettleserautomatisering (Google Flights er 100 % rendret i JavaScript — vanlige HTTP-forespørsler gir ikke noe nyttig), pluss noen hjelpeverktøy:

pip install playwright playwright-stealth pandas
playwright install chromium
  • Playwright — automatisering av nettleser uten synlig vindu, håndterer JavaScript-rendering og innebygde ventemekanismer
  • playwright-stealth — demper vanlige signaler som avslører bot-bruk
  • pandas — for dataanalyse og CSV-eksport senere

Hvorfor Playwright og ikke Selenium eller requests

Google Flights fungerer ikke med bare requests + BeautifulSoup — innholdet rendres helt av JavaScript. Du trenger en ekte nettleser.

FunksjonPlaywrightSeleniumrequests + BS4
JS-renderingFull støtteFull støtteIngen
Hastighet42 % raskere totaltGrunnlinjeIkke relevant her
Asynkron støtteInnebygdKun sekvensieltIkke relevant
Minnebruk30 % lavereHøyereMinimal
Omgåelse av bot-deteksjonGod (med stealth)Lettere å oppdageIkke relevant

Playwright er raskere, mer moderne og har bedre støtte for async. For Google Flights er det det klare valget.

Steg for steg: Slik skraper du Google Flights med Python

Dette er kjernen i guiden. Vi bygger scrapersystemet bit for bit.

google-flights-scraping-workflow.webp

Steg 1: Definer dataklassene dine

Start med å strukturere søkeparametrene og flydataene dine med Python-dataclasses. Det holder koden ryddig og gjør det enklere å utvide senere.

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

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

Hvert felt tilsvarer direkte det vi skal hente ut fra siden. Med denne strukturen på plass slipper du å slepe rundt på rotete ordbøker senere.

Steg 2: Forstå URL-strukturen i Google Flights

Google Flights koder søkeparametere i Base64-kodet Protobuf i tfs-URL-parameteren. Du kan enten reverse-engineere denne kodingen, eller ta den enklere veien: bygge en URL med naturlig språk.

Den enkleste metoden er søkeformatet:

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

For mer kontroll kan du bygge URL-er programmatisk:

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"

Alternativet — å reverse-engineere Protobuf-kodingen — gir mer presis kontroll, men går i stykker når Google endrer internformatet. Biblioteker som fast-flights på GitHub bruker Protobuf-dekoding for å slippe HTML-parsing helt, men det er en mer avansert tilnærming.

Steg 3: Start nettleseren og åpne Google Flights

Her er Playwright-oppsettet. Vi bruker playwright-stealth for å redusere risikoen for å bli oppdaget med én gang.

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",
        )
        # Pre-set cookie consent to skip the popup
        await context.add_cookies([{
            "name": "SOCS",
            "value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
            "domain": ".google.com",
            "path": "/"
        }])
        page = await context.new_page()

Vi kjører headless i produksjon (bytt til headless=False for feilsøking), setter en realistisk viewport og user-agent, og legger inn SOCS-cookien på forhånd for å hoppe over samtykkepopupen — mer om dette i anti-bot-delen.

Steg 4: Gå til søkeresultatene

Last inn den konstruerte URL-en og vent til flyresultatene vises:

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

        # Wait for flight results to load
        await page.wait_for_selector(
            "li.pIav2d", timeout=15000
        )

Hvis du får timeout her, betyr det som regel enten at samtykkepopupen blokkerte siden (se cookie-fiksen i steg 3), eller at Google serverer en CAPTCHA. Vi dekker begge deler i anti-bot-delen.

Steg 5: Last inn alle flyresultater

Google Flights skjuler flere resultater bak knappen "Show more flights". Du må klikke på den gjentatte ganger til alle resultatene er synlige:

        # Click "Show more flights" until all results are loaded
        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

Denne løkken klikker på knappen, venter 2 sekunder på at nye resultater skal rendres, og stopper når knappen ikke lenger er synlig. I testene mine har de fleste ruter 1–3 sider med resultater.

Steg 6: Hent ut flydata med CSS-selektorer

Nå parser vi de faktiske flydataene fra siden som er lastet inn. Her er selektorene (verifisert per april 2026 — se vedlikeholdsseksjonen nedenfor for hvorfor denne datoen er viktig):

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

        for card in cards:
            flight = FlightData()

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

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

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

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

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

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

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

Viktig advarsel: Klasser som pIav2d, sSHqwe og FpEdX genereres av Googles Closure Compiler og kan endres i hvilken som helst build. aria-label-selektorer er mer stabile. Jeg tar for meg en full vedlikeholdsstrategi lenger ned.

Steg 7: Lagre resultatene til JSON eller CSV

Til slutt lagrer du de skrapede dataene med tidsstempel (kritisk for prisovervåking senere):

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)

    # Also save as 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())

Kjør dette, så skal du få en flights.json og en flights.csv med resultatene dine. I testene mine gir et SFO-JFK-søk vanligvis 30–80 flyalternativer og tar omtrent 15–20 sekunder å fullføre.

Overlevelsesguide mot anti-bot når du skraper Google Flights

De fleste guider stopper her. De fleste scrapers feiler her. Google lanserte SearchGuard i januar 2025, og det ødela nesten alle SERP-scrapere over natten. Google beskriver det som "produktet av titusenvis av arbeidstimer og millioner av dollar i investering." Google Flights er vurdert til 4 av 5 i vanskelighetsgrad når det gjelder skraping.

Ingen konkurrentartikkel dekker dette skikkelig, men dette er den viktigste grunnen til at scrapers slutter å fungere. Her er hva du møter, og hvordan du håndterer det.

anti-bot-survival-guide.webp

Tilfeldige pauser mellom forespørsler

Det enkleste forsvaret mot rate limiting. To linjer kode, middels effekt:

import time
import random

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

Legg dette inn mellom sideseilasjene. Faste intervaller (som nøyaktig 5 sekunder hver gang) er et rødt flagg — varier tidsbruken.

Rotasjon av user-agent

Å sende samme user-agent-streng på hver forespørsel er lett å avsløre. Roter fra en 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)

Omgåelse av headless-deteksjon

Google sjekker navigator.webdriver og andre automatiseringssignaler. Biblioteket playwright-stealth håndterer det meste av dette, men du bør også sette oppstartsargumentene fra steg 3. De viktigste flaggene:

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

Dette får deg forbi enkel deteksjon. SearchGuard går dypere — det overvåker musehastighet, tastaturtiming og scroll-mønstre — men for skraping i moderat volum holder stealth-modus pluss realistiske pauser som regel.

Proxy-rotasjon: datasenter vs. residential

For alt utover noen få søk trenger du proxies. Forskjellen er viktig:

FunksjonDatasenter-proxyerResidential proxyer
Hastighet100–1 000 Mbps10–100 Mbps
Suksessrate (Google)20–40 %85–95 %
Kostnad$0,10–$0,50/IP/måned$5–$15/GB
Risiko for oppdagelseHøySvært lav

Residential-proxyer er omtrent 180 ganger billigere per vellykkede forespørsel når du skraper beskyttede nettsteder. Prisnivå i 2026: Smartproxy fra $7/GB, Bright Data $8,40/GB, Oxylabs $8/GB.

Legg til proxy i Playwright slik:

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

Håndtering av samtykkepopup for cookies

Brukere peker konsekvent på "I agree to terms"-popupen som et hinder: "først vil Google vise deg 'I agree to terms and conditions'-popupen." Den reneste løsningen er å forhåndssette SOCS-cookien (vist i steg 3). Hvis det ikke fungerer, kan du klikke den bort:

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  # No popup present

Merk: Knappeteksten varierer etter språk — "Alle akzeptieren" på tysk, "Tout accepter" på fransk.

Kjapp oversikt over anti-bot-metoder

MetodeVanskelighetsgradEffektivitetKrever kode?
Tilfeldige pauser (2–7s)LavMiddels2 linjer
Rotasjon av user-agentLavMiddels5 linjer
Omgåelse av headless-deteksjonMiddelsHøyOppstartsargumenter i Playwright
playwright-stealth-pluginMiddels60–80 % på enkle siderpip install
Proxy-rotasjon (datasenter)MiddelsMiddelsKonfigurasjon
Proxy-rotasjon (residential)Middels85–95 % suksessKonfigurasjon
Forhåndsinnstilt cookie-samtykke (SOCS)LavPåkrevd1 linje

Anbefalte trygge hastigheter: legg inn 10–20 sekunder mellom forespørsler med IP-rotasjon. Googles terskler ligger omtrent på 100 forespørsler/minutt per IP før du får en 429, og vedvarende volum over 1 000 forespørsler/dag per IP kan utløse midlertidige blokkeringer.

Hvorfor Google Flights-selektorene dine stadig ryker (og hvordan du fikser det)

Dette er klart den største smertepunkten. Forumtråder er fulle av varianter av "alt jeg får tilbake er 14 tomme lister." Hver tutorial gir deg selektorer. Ingen forklarer hvorfor de slutter å virke.

Hvorfor selektorene i Google Flights endrer seg

Det skyldes tre ting:

  1. Obfuskering via Closure Compiler. Google bruker Closure Stylesheets til å generere klassenavn som BVAVmf og YMlIz via goog.setCssNameMapping(). Disse endrer seg i hver build — noen ganger ukentlig.

  2. A/B-testing. Ulike brukere ser ulike HTML-strukturer samtidig. Scraperen din kan fungere på maskinen din, men feile for noen i en annen region.

  3. Lokale forskjeller. Brukere i EU ser andre ordvalg, oppsett og til og med datafelter enn brukere i USA.

Skriv mer robuste selektorer

Velg selektorer som er knyttet til mening, ikke utseende:

# Fragile — breaks on every build
price_el = await card.query_selector("div.BVAVmf > div.YMlIz")

# More resilient — tied to accessibility labels
dep_el = await card.query_selector('span[aria-label*="Departure time"]')

# Also resilient — text-based matching
more_btn = page.locator('button:has-text("Show more flights")')

Hierarki for selektorstabilitet (mest til minst stabil):

  1. aria-label-attributter — knyttet til tilgjengelighet, endres sjelden
  2. data-*-attributter — lagt inn eksplisitt for funksjonalitet
  3. role-attributter — ARIA-roller er semantiske
  4. Tekstbaserte selektorer — matcher synlig innhold
  5. Delvis matching av klassenavn — f.eks. [class*="price"]
  6. Fullt obfuskert klassenavn — unngå dette hvis mulig

Legg til en valideringsfunksjon

Ikke la ødelagte selektorer stille produsere tomme data. Fang det opp tidlig:

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

Kjør dette på hvert skrapede fly. Hvis du begynner å se advarsler, er det på tide å inspisere siden og oppdatere selektorene.

Strategi for vedlikehold av selektorer

  • Sjekk selektorene månedlig, eller umiddelbart når datakvaliteten faller
  • Hold selektorene i en egen konfigurasjonsordbok for enkel oppdatering
  • Selektorene i denne artikkelen ble sist verifisert: april 2026
  • Vurder fast-flights-biblioteket som et alternativ — det bruker Protobuf-dekoding i stedet for CSS-selektorer, og slipper dermed dette problemet helt (selv om det også er sårbart når Google endrer interne dataformater)

Fra ett enkelt skrap til en automatisert Google Flights-prisovervåker

De fleste guider slutter ved "lagre til JSON." Tittelen på denne artikkelen sier "Prisvarsler." På tide å levere.

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

Planlegg at scraperen skal kjøre automatisk

Alternativ 1: Python-biblioteket schedule (enklest, fungerer på tvers av plattformer):

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)

Alternativ 2: cron-jobb (Linux/Mac):

# Run at 6 AM and 6 PM daily
0 6,18 * * * cd /path/to/scraper && python scraper.py

Alternativ 3: Windows Task Scheduler — lag en enkel oppgave som kjører python scraper.py etter ønsket tidsplan.

Ulempen: Alle disse krever en maskin som alltid er på. Hvis du kjører dette på en laptop som går i dvale, mister du kjøringer.

Lagre historiske prisdata

Bytt ut å overskrive en JSON-fil med å legge til nye rader i en 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()

Etter en uke med to daglige kjøringer har du nok data til å begynne å se trender.

Analyser pristrender og sett opp varsler

Finn det billigste alternativet fra historiske data:

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']})"
)

Send et e-postvarsel når prisen faller under terskelen din:

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)

# After each scrape, check for deals
min_price = df["price_usd"].min()
threshold = 250
if min_price < threshold:
    send_price_alert("SFO-JFK", min_price, threshold,
                     "you@email.com")

Anbefalt skrapefrekvens: to ganger daglig er nok for personlig prisovervåking (tilfeldig timing reduserer oppdagelsesrisikoen). Hver 4.–6. time hvis du overvåker for en bedrift. Kun hver time i korte perioder med salg, og bare midlertidig.

Den enkle veien: Thunderbits planlagte scraper

Hvis cron-jobber, en server som må stå på, og proxy-oppsett høres ut som mer infrastruktur enn du vil vedlikeholde, kan Thunderbits Scheduled Scraper håndtere samme brukstilfelle uten alt det ekstra arbeidet. Du beskriver skrapeintervallet med vanlige ord, legger inn Google Flights-URL-ene dine, og scraperen kjører automatisk på Thunderbits skyløsning — med anti-bot-håndtering innebygd og resultater eksportert direkte til Google Sheets, Excel eller Airtable. Det er ikke en erstatning for hele Python-tilnærmingen (du mister noe fleksibilitet), men for det spesifikke behovet "jeg vil ha et regneark med prisovervåking" er dette den raskeste veien. Du kan prøve det på gratisnivået.

Når Python er overkill: No-code måter å skrape Google Flights på

Etter å ha bygget alt dette, skal jeg være ærlig: det er mange bevegelige deler. Ikke alle trenger dette nivået av kontroll. Selektorer ryker, proxyer må roteres, cron-jobber må overvåkes. Hvis målet ditt er "få flypriser inn i et regneark jevnlig", finnes det raskere alternativer.

Sammenligning: DIY Python vs. API-tjenester vs. Thunderbit

TilnærmingOppsettstidKrever kodingHåndterer anti-botPlanleggingKostnad
DIY Playwright (denne guiden)1–2 timerPython (middels)Manuell konfigurasjonManuelt (cron)Gratis + proxykostnader
SerpApi Google Flights-endepunkt15 minKun API-kallHåndtertVia APIca. $50+/mnd
Thunderbit Chrome-utvidelse2 minIngenSkybasert skrapingInnebygd planleggerGratis nivå tilgjengelig

En kommentar om SerpApi: Google saksøkte SerpApi i desember 2025 med DMCA-søksmål, og hevdet at forespørslene deres økte med 25 000 % over to år. Den juridiske usikkerheten er verdt å ta med i vurderingen hvis du vurderer API-leverandører.

Slik skraper Thunderbit Google Flights

Åpne søkeresultatene dine i Google Flights i Chrome, klikk på Thunderbits "AI Suggest Fields"-knapp — AI-en leser siden og foreslår kolonner som flyselskap, pris, avgangstid og stopp — gå gjennom feltene som foreslås, og klikk "Scrape." Resultatene vises i en tabell du kan eksportere til Excel, Google Sheets, Airtable eller Notion — alt på gratisnivået.

For akkurat prisovervåking erstatter Thunderbits Scheduled Scraper og Cloud Scraping (som kan håndtere 50 sider samtidig) hele infrastrukturen med cron, proxyer og server.

Python gir deg full kontroll og uendelig med tilpasning. Thunderbit gir deg fart og null vedlikehold. Velg ut fra det faktiske målet ditt. Hvis du vil lære mer om no-code-skraping, kan du se guiden vår om de beste no-code webscraperne.

flight-data-options-comparison.webp

Er det lov å skrape Google Flights? Dette bør du vite

Forumbrukere tar dette opp ofte: "å skrape Google Flights direkte bryter bruksvilkårene til Google." Et relevant poeng — særlig siden API-et ble lagt ned og det ikke finnes noe godkjent alternativ.

Brudd på vilkår vs. juridisk ansvar

Googles bruksvilkår (oppdatert 22. mai 2024) sier at brukere ikke skal "access or use the Services or any content through the use of any automated means (such as robots, spiders or scrapers)." Å bryte ToS er et kontraktsbrudd (sivilrettslig sak) — det er ikke det samme som å bryte loven.

Det viktigste juridiske presedens-tilfellet er hiQ v. LinkedIn (Ninth Circuit, 2022), som slo fast at skraping av offentlig tilgjengelige data ikke bryter Computer Fraud and Abuse Act (CFAA). Saken endte riktignok i forlik, og Googles søksmål mot SerpApi i desember 2025 bygger på en annen juridisk teori — DMCA § 1201 (omgåelse av teknologiske beskyttelsestiltak) — som potensielt er mer alvorlig.

Beste praksis for ansvarlig skraping

  • Begrens forespørslene dine — 10–20 sekunders pauser med IP-rotasjon
  • Ikke skrap persondata — flypriser er offentlig viste, aggregerte data
  • Ikke omgå CAPTCHA-er programmatisk (dette er den delen av DMCA-risikoen)
  • Bruk dataene til egen research, ikke for å bygge et konkurrerende kommersielt produkt uten riktig lisens
  • Vurder offisielle API-er der de finnes

Alternative datakilder

Hvis skraping føles for risikabelt for ditt brukstilfelle, finnes det legitime API-alternativer:

LeverandørKostnadGratisnivåMerknader
SerpApi$75–$3 750+/mnd250 søk/mndDirekte Google Flights-JSON (under juridisk granskning)
Kiwi TequilaGratis (affiliate-modell)UbegrensetBest for oppstartsbedrifter og testing
AmadeusBetal per bruk2 000 req/mnd400+ flyselskaper, bookingfunksjon
SkyscannerTilpassetKrever godkjenning52 markeder, 30 språk

Vi skrev en dypere gjennomgang av juridiske konsekvenser ved webskraping hvis du vil ha hele bildet.

Konklusjon og viktige læringspunkter

Det var mye. Her er det viktigste:

  • Python + Playwright er den mest fleksible måten å skrape Google Flights på, men krever jevnlig vedlikehold
  • Anti-bot-tiltak (pauser, rotasjon av user-agent, residential proxyer) er ikke valgfrie — de er avgjørende for driftssikkerhet, særlig etter SearchGuard
  • Selektorer ryker ofte — bruk aria-label og tekstbaserte selektorer der det er mulig, valider resultatene og legg inn en vedlikeholdsplan
  • Automatiser med schedule eller cron for å gjøre et engangsscrape om til en ekte prisovervåker med historikk og e-postvarsler
  • Thunderbit gir et no-code-alternativ med innebygd planlegging, skybasert skraping og anti-bot-håndtering — ideelt hvis målet ditt er et prisovervåkingsregneark, ikke et kodeprosjekt
  • Respekter juridiske grenser — begrens forespørslene, skrap bare offentlige data, og vurder API-alternativer for kommersiell bruk

Hent koden fra denne guiden, eller installer Thunderbit Chrome-utvidelsen hvis du vil ha den raske veien. Uansett vil du overvåke flypriser i stedet for å oppdatere Google Flights manuelt hele tiden.

For flere Python-teknikker innen skraping, se guidene våre om hvordan skrape nettsider med Python og de beste Python-verktøyene for webskraping.

Vanlige spørsmål

1. Kan jeg skrape Google Flights uten Python?

Ja. API-tjenester som SerpApi og Kiwi Tequila gir strukturert flydata via API-kall (ingen nettleserautomatisering nødvendig). For en helt no-code-tilnærming kan Thunderbits Chrome-utvidelse skrape Google Flights-resultater direkte fra nettleseren din med AI-forslåtte felt og eksport med ett klikk.

2. Blokkerer Google skraping av flydata?

Google bruker bot-deteksjon (SearchGuard), CAPTCHA-er og rate limiting. Med riktige anti-bot-tiltak — tilfeldige pauser, rotasjon av user-agent, residential proxyer og stealth-innstillinger i nettleseren — kan du opprettholde pålitelig skraping i moderat volum. Se anti-bot-delen over for konkrete teknikker og terskler.

3. Hvor ofte bør jeg skrape Google Flights for prisovervåking?

To ganger daglig (til tilfeldige tidspunkter) er nok for personlig prisoppfølging og holder oppdagelsesrisikoen lav. For bedriftsbruk: hver 4.–6. time med proxy-rotasjon. Unngå skraping hver time, unntatt ved kortvarige salg — det øker sjansen for blokkering betydelig.

4. Finnes det en gratis Google Flights API?

Det offisielle Google QPX Express API-et ble lagt ned i april 2018. Det finnes ingen gratis, offisiell erstatning. Det nærmeste gratis alternativet er Kiwi Tequila API (affiliate-modell, ubegrensede søk). SerpApi tilbyr 250 gratis søk per måned. For de fleste brukere er skraping eller et no-code-verktøy som Thunderbit den praktiske løsningen.

5. Hvorfor får CSS-selektorene mine i Google Flights stadig tomme resultater?

Google bruker Closure Compiler til å generere obfuskert klassenavn som endres i hver build. A/B-testing og lokale forskjeller gjør også at HTML-strukturen varierer mellom brukere. Løsningen: bruk aria-label-attributter og tekstbaserte selektorer i stedet for klassenavn, legg til en valideringsfunksjon som fanger opp feil tidlig, og kontroller selektorene månedlig. Se delen om vedlikehold av selektorer for en detaljert strategi.

Les mer

Ke
Ke
CTO i Thunderbit | Senior Data Scientist og ML-ekspert Med nesten ti års erfaring innen maskinlæring og datavitenskap er Ke Shen alumnus fra Columbia University og tidligere Senior Data Scientist hos Walmart Labs. Med solid, anerkjent ekspertise i Python, R, Java og statistikk deler han velprøvde innsikter om hvordan komplekse AI-algoritmer kan tas fra teori til produksjonsklar arkitektur.
Innholdsfortegnelse

Hent en nettside bare ved å spørre

Si hva du trenger på vanlig norsk. Eller enda bedre, si ingenting i det hele tatt.

Prøv Thunderbit gratis
Hent data med AI
Overfør enkelt data til Google Sheets, Airtable eller Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week