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:
| Brukertype | Bruksområde | Viktig gevinst |
|---|---|---|
| Private reisende | Følge prisene på bestemte ruter over tid | Spar i snitt $50 per flyreise |
| Reisebyråer | Innsikt i konkurransedyktig prising | Overvåking av prisparitet i sanntid |
| Bedrifters reiseteam | Kostnadsoptimalisering på tvers av ruter | 10–30 % lavere reisekostnader |
| Utviklere | Bygge apper for prissammenligning | Programmatisk tilgang til prisdata |
| Forskere | Analyse av prisvolatilitet i luftfart | Akademisk 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.
| Funksjon | Playwright | Selenium | requests + BS4 |
|---|---|---|---|
| JS-rendering | Full støtte | Full støtte | Ingen |
| Hastighet | 42 % raskere totalt | Grunnlinje | Ikke relevant her |
| Asynkron støtte | Innebygd | Kun sekvensielt | Ikke relevant |
| Minnebruk | 30 % lavere | Høyere | Minimal |
| Omgåelse av bot-deteksjon | God (med stealth) | Lettere å oppdage | Ikke 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.

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.

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:
| Funksjon | Datasenter-proxyer | Residential proxyer |
|---|---|---|
| Hastighet | 100–1 000 Mbps | 10–100 Mbps |
| Suksessrate (Google) | 20–40 % | 85–95 % |
| Kostnad | $0,10–$0,50/IP/måned | $5–$15/GB |
| Risiko for oppdagelse | Høy | Svæ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
| Metode | Vanskelighetsgrad | Effektivitet | Krever kode? |
|---|---|---|---|
| Tilfeldige pauser (2–7s) | Lav | Middels | 2 linjer |
| Rotasjon av user-agent | Lav | Middels | 5 linjer |
| Omgåelse av headless-deteksjon | Middels | Høy | Oppstartsargumenter i Playwright |
| playwright-stealth-plugin | Middels | 60–80 % på enkle sider | pip install |
| Proxy-rotasjon (datasenter) | Middels | Middels | Konfigurasjon |
| Proxy-rotasjon (residential) | Middels | 85–95 % suksess | Konfigurasjon |
| Forhåndsinnstilt cookie-samtykke (SOCS) | Lav | Påkrevd | 1 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:
-
Obfuskering via Closure Compiler. Google bruker Closure Stylesheets til å generere klassenavn som
BVAVmfogYMlIzviagoog.setCssNameMapping(). Disse endrer seg i hver build — noen ganger ukentlig. -
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.
-
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):
aria-label-attributter — knyttet til tilgjengelighet, endres sjeldendata-*-attributter — lagt inn eksplisitt for funksjonalitetrole-attributter — ARIA-roller er semantiske- Tekstbaserte selektorer — matcher synlig innhold
- Delvis matching av klassenavn — f.eks.
[class*="price"] - 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.
![]()
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ærming | Oppsettstid | Krever koding | Håndterer anti-bot | Planlegging | Kostnad |
|---|---|---|---|---|---|
| DIY Playwright (denne guiden) | 1–2 timer | Python (middels) | Manuell konfigurasjon | Manuelt (cron) | Gratis + proxykostnader |
| SerpApi Google Flights-endepunkt | 15 min | Kun API-kall | Håndtert | Via API | ca. $50+/mnd |
| Thunderbit Chrome-utvidelse | 2 min | Ingen | Skybasert skraping | Innebygd planlegger | Gratis 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.

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ør | Kostnad | Gratisnivå | Merknader |
|---|---|---|---|
| SerpApi | $75–$3 750+/mnd | 250 søk/mnd | Direkte Google Flights-JSON (under juridisk granskning) |
| Kiwi Tequila | Gratis (affiliate-modell) | Ubegrenset | Best for oppstartsbedrifter og testing |
| Amadeus | Betal per bruk | 2 000 req/mnd | 400+ flyselskaper, bookingfunksjon |
| Skyscanner | Tilpasset | Krever godkjenning | 52 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-labelog tekstbaserte selektorer der det er mulig, valider resultatene og legg inn en vedlikeholdsplan - Automatiser med
scheduleeller 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


