Como Extrair Dados do Google Flights com Python: do Código aos Alertas de Preço

Última atualização em April 16, 2026

O Google encerrou a API do Flights lá em 2018, mas os preços das passagens continuam mexendo sem parar — numa única rota doméstica. Se você quer acesso programático a esses dados, a extração de dados é praticamente o único caminho.

Passei um bom tempo testando formas diferentes de coletar dados de voos no Google, e o cenário mudou bastante — principalmente depois que o Google lançou o SearchGuard em janeiro de 2025. Neste guia, vou te mostrar como montar um extrator em Python funcional para o Google Flights usando Playwright, explicar como driblar as barreiras anti-bot que travam a maioria das pessoas e, depois, transformar tudo isso em um rastreador automático de preços com alertas. Se você preferir pular o código, também vou mostrar uma alternativa sem programação usando que entrega o mesmo resultado em cerca de dois minutos.

Por que usar Python para extrair dados do Google Flights?

O Google Flights domina as buscas por voos. A visibilidade móvel nos EUA , ultrapassando todas as principais OTAs. O mercado de metabusca de viagens por trás disso foi avaliado em , com crescimento anual composto de 30,2%. Mesmo assim, desde que a API QPX Express foi , não existe um jeito oficial de acessar esses dados programaticamente.

Ao mesmo tempo, os preços dos voos oscilam para o mesmo itinerário, com uma diferença média de cerca de US$ 20 entre a tarifa mais baixa e a mais alta. Companhias como a Delta usam 77 faixas tarifárias para precificação dinâmica. A passagem média de ida e volta nos EUA no início de 2026 ficou em US$ 408, com as tarifas .

Plataforma dominante, sem API e com preços voláteis. É por isso que extrair dados do Google Flights com Python virou um dos projetos mais populares no GitHub e em fóruns de viagem.

Veja quem se beneficia e de que forma:

Tipo de usuárioCaso de usoPrincipal benefício
Viajantes individuaisAcompanhar preços de rotas específicas ao longo do tempoEconomizar em média US$ 50 por voo
Agências de viagemInteligência competitiva de preçosMonitoramento em tempo real da paridade tarifária
Equipes de viagens corporativasOtimização de custos entre rotasEconomia de 10% a 30% em viagens corporativas
DesenvolvedoresCriar apps de comparação de tarifasAcesso programático aos dados de preço
PesquisadoresAnálise da volatilidade de preços das companhias aéreasPesquisa acadêmica e de mercado

Usuários em fóruns são diretos sobre o motivo de terem recorrido à extração: "O Google Flights API foi descontinuado e eu deveria usar web scraping" é um sentimento que aparece o tempo todo. E o retorno compensa — ao analisar mais de 5 bilhões de cotações por dia, enquanto os dados de 2026 da Expedia mostram que reservar com 8 a 15 dias de antecedência economiza cerca de .

Que dados você pode extrair do Google Flights?

A página de resultados do Google Flights traz um conjunto de dados surpreendentemente rico. Normalmente, é possível obter:

  • Nome da companhia aérea (e logotipo)
  • Horário de partida e código do aeroporto
  • Horário de chegada e código do aeroporto
  • Duração total do voo
  • Número de escalas e detalhes das conexões (aeroporto, duração, se é pernoite)
  • Preço da passagem (específico por moeda)
  • Emissões de CO2 (kg CO2e, com diferença percentual em relação a voos típicos)
  • Classe da viagem, número do voo, modelo da aeronave
  • Especificação de espaço para as pernas
  • Recursos adicionais (Wi‑Fi, tomadas, streaming de mídia)
  • Indicador de nível de preço (baixo/normal/alto)
  • Avisos de atraso ("Frequentemente atrasado em mais de 30 min")

A disponibilidade dos dados varia conforme a rota, a data e o tipo de passagem (só ida ou ida e volta). Veja como fica um único registro de voo extraído em JSON:

1{
2  "search_date": "2026-04-16",
3  "route": "SFO-JFK",
4  "departure_date": "2026-05-15",
5  "flights": [
6    {
7      "airline": "United Airlines",
8      "flight_number": "UA123",
9      "departure_time": "08:00",
10      "departure_airport": "SFO",
11      "arrival_time": "16:35",
12      "arrival_airport": "JFK",
13      "duration_minutes": 335,
14      "stops": 0,
15      "price_usd": 287,
16      "price_level": "low",
17      "co2_kg": 156,
18      "co2_vs_typical": "-12%",
19      "travel_class": "Economy"
20    }
21  ]
22}

Configurando seu ambiente Python

Antes de escrever qualquer código de extração, você vai precisar de alguns itens básicos.

Pré-requisitos:

  • Nível de dificuldade: Intermediário
  • Tempo necessário: cerca de 1 a 2 horas para seguir o tutorial completo
  • O que você vai precisar: Python 3.7+, conhecimentos básicos de Python e um navegador baseado em Chrome

Instale as bibliotecas necessárias

Vamos usar Playwright para automação de navegador (o Google Flights é 100% renderizado em JavaScript — requisições HTTP estáticas não retornam nada útil), além de alguns auxiliares:

1pip install playwright playwright-stealth pandas
2playwright install chromium
  • Playwright — automação de navegador headless, lida com renderização em JavaScript e mecanismos nativos de espera
  • playwright-stealth — reduz sinais comuns de detecção de bots
  • pandas — para análise de dados e exportação para CSV depois

Por que Playwright em vez de Selenium ou requests

O Google Flights não funciona só com requests + BeautifulSoup — o conteúdo da página é renderizado totalmente por JavaScript. Você precisa de um navegador de verdade.

RecursoPlaywrightSeleniumrequests + BS4
Renderização de JSSuporte totalSuporte totalNenhum
Velocidade42% mais rápido no geralBaseN/A para este caso
Suporte assíncronoNativoApenas sequencialN/A
Uso de memória30% menorMaiorMínimo
Bypass de detecção de botBom (com stealth)Mais fácil de detectarN/A

O Playwright é mais rápido, mais moderno e tem suporte melhor para async. Para o Google Flights, é a escolha mais clara.

Passo a passo: como extrair dados do Google Flights com Python

Esta é a parte central do tutorial. Vamos construir o extrator passo a passo.

google-flights-scraping-workflow.webp

Passo 1: Defina suas classes de dados

Comece estruturando seus parâmetros de busca e os dados do voo com dataclasses do Python. Isso mantém o código organizado e facilita futuras expansões.

1from dataclasses import dataclass, field
2from typing import Optional, List
3@dataclass
4class SearchParams:
5    origin: str          # ex.: "SFO"
6    destination: str     # ex.: "JFK"
7    departure_date: str  # ex.: "2026-05-15"
8    return_date: Optional[str] = None
9    trip_type: str = "one-way"  # "one-way" ou "round-trip"
10    travel_class: str = "economy"
11@dataclass
12class FlightData:
13    airline: str = ""
14    departure_time: str = ""
15    arrival_time: str = ""
16    duration: str = ""
17    stops: str = ""
18    price: str = ""
19    co2_emissions: str = ""

Cada campo corresponde diretamente ao que vamos extrair da página. Ter essa estrutura desde o começo evita ter que lidar com dicionários bagunçados depois.

Passo 2: Entenda a estrutura da URL do Google Flights

O Google Flights codifica os parâmetros de busca usando Protobuf em Base64 no parâmetro de URL tfs. Você pode tentar reverter essa codificação ou seguir um caminho mais simples: montar uma URL em linguagem natural.

O método mais simples usa este formato de busca:

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

Se quiser mais controle, você pode gerar URLs programaticamente:

1def build_flights_url(origin: str, destination: str, date: str) -> str:
2    base = "https://www.google.com/travel/flights"
3    query = f"flights from {origin} to {destination} on {date}"
4    return f"{base}?q={query.replace(' ', '+')}&curr=USD"

A alternativa — reverter a codificação Protobuf — dá mais precisão, mas quebra quando o Google altera o formato interno. Bibliotecas como no GitHub usam decodificação Protobuf para evitar a análise de HTML por completo, mas essa é uma abordagem mais avançada.

Passo 3: Inicie o navegador e acesse o Google Flights

Aqui está a configuração com Playwright. Vamos usar playwright-stealth para reduzir o risco de detecção desde o início.

1import asyncio
2from playwright.async_api import async_playwright
3from playwright_stealth import Stealth
4async def scrape_flights(params: SearchParams) -> List[FlightData]:
5    async with Stealth().use_async(async_playwright()) as pw:
6        browser = await pw.chromium.launch(
7            headless=True,
8            args=[
9                "--disable-blink-features=AutomationControlled",
10                "--disable-dev-shm-usage",
11                "--no-first-run",
12            ]
13        )
14        context = await browser.new_context(
15            viewport={"width": 1920, "height": 1080},
16            user_agent=(
17                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
18                "AppleWebKit/537.36 (KHTML, like Gecko) "
19                "Chrome/125.0.0.0 Safari/537.36"
20            ),
21            locale="en-US",
22            timezone_id="America/New_York",
23        )
24        # Pré-defina o cookie de consentimento para pular o pop-up
25        await context.add_cookies([{
26            "name": "SOCS",
27            "value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
28            "domain": ".google.com",
29            "path": "/"
30        }])
31        page = await context.new_page()

Estamos rodando em modo headless para produção (mude para headless=False ao depurar), definindo uma viewport e um user agent realistas e pré-configurando o cookie SOCS para pular o pop-up de consentimento — mais detalhes na seção anti-bot.

Passo 4: Vá para os resultados da busca

Carregue a URL construída e espere os resultados dos voos aparecerem:

1        url = build_flights_url(
2            params.origin, params.destination, params.departure_date
3        )
4        await page.goto(url, wait_until="networkidle")
5        # Aguarde o carregamento dos resultados
6        await page.wait_for_selector(
7            "li.pIav2d", timeout=15000
8        )

Se ocorrer timeout aqui, normalmente significa que o pop-up de consentimento bloqueou a página (veja a correção com cookie no Passo 3) ou que o Google está exibindo um CAPTCHA. Vamos cobrir os dois cenários na seção anti-bot.

Passo 5: Carregue todos os resultados de voo

O Google Flights oculta resultados extras atrás do botão "Mostrar mais voos". Você precisa clicar nele repetidamente até que todos os voos fiquem visíveis:

1        # Clique em "Mostrar mais voos" até carregar todos os resultados
2        while True:
3            try:
4                more_button = page.locator(
5                    'button:has-text("Show more flights")'
6                )
7                if await more_button.is_visible(timeout=3000):
8                    await more_button.click()
9                    await page.wait_for_timeout(2000)
10                else:
11                    break
12            except Exception:
13                break

Esse loop clica no botão, espera 2 segundos para os novos resultados renderizarem e para quando o botão não estiver mais visível. Nos meus testes, a maioria das rotas tem de 1 a 3 páginas de resultados.

Passo 6: Extraia os dados do voo com seletores CSS

Agora vamos analisar os dados reais de voo na página carregada. Aqui estão os seletores (verificados em abril de 2026 — veja a seção de manutenção abaixo para entender por que essa data importa):

1        flights = []
2        cards = await page.query_selector_all("li.pIav2d")
3        for card in cards:
4            flight = FlightData()
5            # Nome da companhia aérea
6            airline_el = await card.query_selector(
7                "div.sSHqwe span:not([class])"
8            )
9            if airline_el:
10                flight.airline = (await airline_el.inner_text()).strip()
11            # Horário de partida
12            dep_el = await card.query_selector(
13                'span[aria-label*="Departure time"]'
14            )
15            if dep_el:
16                flight.departure_time = (await dep_el.inner_text()).strip()
17            # Horário de chegada
18            arr_el = await card.query_selector(
19                'span[aria-label*="Arrival time"]'
20            )
21            if arr_el:
22                flight.arrival_time = (await arr_el.inner_text()).strip()
23            # Duração
24            dur_el = await card.query_selector("div.gvkrdb")
25            if dur_el:
26                flight.duration = (await dur_el.inner_text()).strip()
27            # Escalas
28            stops_el = await card.query_selector("div.EfT7Ae span")
29            if stops_el:
30                flight.stops = (await stops_el.inner_text()).strip()
31            # Preço
32            price_el = await card.query_selector(
33                "div.FpEdX span"
34            )
35            if price_el:
36                flight.price = (await price_el.inner_text()).strip()
37            # Emissões de CO2
38            co2_el = await card.query_selector("div.O7CXue")
39            if co2_el:
40                flight.co2_emissions = (
41                    await co2_el.get_attribute("aria-label") or ""
42                ).strip()
43            flights.append(flight)
44        await browser.close()
45        return flights

Aviso importante: classes como pIav2d, sSHqwe e FpEdX são geradas pelo Closure Compiler do Google e podem mudar a qualquer compilação. Já os seletores baseados em aria-label tendem a ser mais estáveis. Vou cobrir uma estratégia completa de manutenção abaixo.

Passo 7: Salve os resultados em JSON ou CSV

Por fim, salve os dados extraídos com um timestamp (isso é essencial para o rastreamento de preços depois):

1import json
2from datetime import datetime
3from dataclasses import asdict
4async def main():
5    params = SearchParams(
6        origin="SFO",
7        destination="JFK",
8        departure_date="2026-05-15"
9    )
10    flights = await scrape_flights(params)
11    output = {
12        "search_date": datetime.now().isoformat(),
13        "params": asdict(params),
14        "flights": [asdict(f) for f in flights],
15    }
16    with open("flights.json", "w") as f:
17        json.dump(output, f, indent=2)
18    # Também salva como CSV
19    import pandas as pd
20    df = pd.DataFrame([asdict(f) for f in flights])
21    df["search_date"] = datetime.now().isoformat()
22    df["route"] = f"{params.origin}-{params.destination}"
23    df.to_csv("flights.csv", index=False)
24    print(f"Scraped {len(flights)} flights")
25asyncio.run(main())

Rode isso e você deve ver um flights.json e um flights.csv com os resultados. Nos meus testes, uma busca SFO-JFK normalmente retorna de 30 a 80 opções de voo e leva cerca de 15 a 20 segundos para concluir.

Guia de sobrevivência anti-bot para extrair dados do Google Flights

A maioria dos tutoriais para por aqui. A maioria dos extratores quebra aqui. O Google lançou o , e isso derrubou quase todos os scrapers de SERP da noite para o dia. O Google descreve a solução como "o resultado de dezenas de milhares de horas de trabalho e milhões de dólares em investimento". O Google Flights recebe nota para extração.

Nenhum artigo concorrente aprofunda esse ponto, mas essa é a principal razão pela qual os scrapers deixam de funcionar. Veja o que você está enfrentando e como lidar com isso.

anti-bot-survival-guide.webp

Atrasos aleatórios entre requisições

A defesa mais simples contra limitação de taxa. Duas linhas de código, eficácia média:

1import time
2import random
3time.sleep(random.uniform(3, 7))

Adicione isso entre as navegações de página. Intervalos fixos (como exatamente 5 segundos toda vez) são um sinal de alerta — use valores aleatórios.

Rotação de User-Agent

Enviar a mesma string de user-agent em todas as requisições é um indício fácil de detectar. Alterne entre uma lista:

1import random
2USER_AGENTS = [
3    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36",
4    "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5) AppleWebKit/605.1.15 Safari/605.1.15",
5    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0.0.0 Safari/537.36",
6    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0",
7]
8user_agent = random.choice(USER_AGENTS)

Bypass de detecção em modo headless

O Google verifica o sinal navigator.webdriver e outros indícios de automação. A biblioteca playwright-stealth cobre a maioria deles, mas você também deve definir os argumentos de inicialização mostrados no Passo 3. Os principais flags:

1args=[
2    "--disable-blink-features=AutomationControlled",
3    "--disable-dev-shm-usage",
4    "--no-first-run",
5]

Isso já passa pela detecção básica. O SearchGuard vai além — monitora velocidade do mouse, tempo entre teclas e padrões de rolagem — mas, para volumes moderados, o modo stealth com atrasos realistas costuma ser suficiente.

Rotação de proxy: datacenter vs. residencial

Para algo além de algumas buscas, você vai precisar de proxies. A diferença importa:

RecursoProxies de datacenterProxies residenciais
Velocidade100–1.000 Mbps10–100 Mbps
Taxa de sucesso (Google)20–40%85–95%
CustoUS$ 0,10–US$ 0,50/IP/mêsUS$ 5–US$ 15/GB
Risco de detecçãoAltoMuito baixo

Proxies residenciais saem cerca de ao extrair dados de sites protegidos. Preços de provedores em 2026: Smartproxy a partir de US$ 7/GB, Bright Data US$ 8,40/GB e Oxylabs US$ 8/GB.

Adicione um proxy ao Playwright assim:

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

Como lidar com o pop-up de consentimento de cookies

Usuários relatam com frequência que o pop-up "Aceito os termos" bloqueia o acesso: "primeiro o Google vai mostrar o pop-up 'I agree to terms and conditions'." A forma mais limpa de resolver isso é pré-configurar o cookie SOCS (mostrado no Passo 3). Se não funcionar, clique no botão manualmente:

1try:
2    accept_btn = page.locator('button:has-text("Accept all")')
3    if await accept_btn.is_visible(timeout=3000):
4        await accept_btn.click()
5        await page.wait_for_timeout(1000)
6except Exception:
7    pass  # Não há pop-up

Observação: o texto do botão muda conforme o idioma — "Alle akzeptieren" em alemão, "Tout accepter" em francês.

Referência rápida anti-bot

TécnicaDificuldadeEficáciaPrecisa de código?
Atrasos aleatórios (2–7s)BaixaMédia2 linhas
Rotação de user-agentBaixaMédia5 linhas
Bypass de detecção headlessMédiaAltaArgumentos de inicialização do Playwright
Plugin playwright-stealthMédia60–80% em sites básicospip install
Rotação de proxy (datacenter)MédiaMédiaConfiguração
Rotação de proxy (residencial)Média85–95% de sucessoConfiguração
Pré-configuração de consentimento (SOCS)BaixaObrigatório1 linha

Como referência de segurança, mantenha intervalos de 10 a 20 segundos entre requisições com rotação de IP. Os limites do Google ficam em torno de 100 requisições por minuto por IP antes de surgir um erro 429, e volumes sustentados acima de 1.000 requisições por dia por IP podem gerar bloqueios temporários.

Por que seus seletores do Google Flights continuam quebrando (e como corrigir)

Esse é, de longe, o maior ponto de dor. Threads em fóruns estão cheias de variações como "tudo o que recebo são 14 listas vazias". Todo tutorial entrega seletores. Nenhum explica por que eles quebram.

Por que os seletores do Google Flights mudam

Tudo se resume a três fatores:

  1. Ofuscação com Closure Compiler. O Google usa para gerar nomes de classe como BVAVmf e YMlIz por meio de goog.setCssNameMapping(). Eles mudam a cada compilação — às vezes semanalmente.

  2. Testes A/B. Usuários diferentes veem estruturas HTML diferentes ao mesmo tempo. Seu scraper pode funcionar na sua máquina e falhar para alguém em outra região.

  3. Diferenças de localidade. Usuários da UE veem termos, layouts e até campos de dados diferentes dos usuários dos EUA.

Escreva seletores mais resilientes

Prefira seletores ligados ao significado, não à aparência:

1# Frágil — quebra em toda compilação
2price_el = await card.query_selector("div.BVAVmf > div.YMlIz")
3# Mais resistente — ligado a rótulos de acessibilidade
4dep_el = await card.query_selector('span[aria-label*="Departure time"]')
5# Também resistente — correspondência por texto
6more_btn = page.locator('button:has-text("Show more flights")')

Hierarquia de estabilidade dos seletores (do mais estável ao menos estável):

  1. Atributos aria-label — ligados à acessibilidade, raramente mudam
  2. Atributos data-* — adicionados explicitamente para funcionalidade
  3. Atributos role — papéis ARIA são semânticos
  4. Seletores baseados em texto — correspondem ao conteúdo visível
  5. Correspondência parcial de classe — por exemplo, [class*="price"]
  6. Nomes completos de classes ofuscadas — evite sempre que possível

Adicione uma função de validação

Não deixe seletores quebrados produzirem dados vazios em silêncio. Detecte isso cedo:

1import logging
2logger = logging.getLogger(__name__)
3def validate_flight(data: FlightData) -> bool:
4    required = ["airline", "price", "departure_time",
5                "arrival_time", "duration"]
6    valid = True
7    for field_name in required:
8        if not getattr(data, field_name, ""):
9            logger.warning(
10                f"Campo ausente '{field_name}' — os seletores podem precisar de atualização"
11            )
12            valid = False
13    return valid

Execute isso em cada voo extraído. Se começarem a aparecer avisos, é hora de inspecionar a página e atualizar seus seletores.

Estratégia de manutenção dos seletores

  • Verifique os seletores mensalmente, ou imediatamente se a qualidade da saída cair
  • Mantenha os seletores em um dicionário de configuração separado para facilitar ajustes
  • Os seletores deste artigo foram verificados pela última vez em abril de 2026
  • Considere a biblioteca como alternativa — ela usa decodificação Protobuf em vez de seletores CSS, evitando esse problema quase por completo (embora também tenha sua própria fragilidade quando o Google altera os formatos internos de dados)

De uma extração pontual a um rastreador automático de preços do Google Flights

A maioria dos tutoriais termina em "salvar em JSON". O título deste artigo fala em "alertas de preço". Hora de entregar isso.

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

Agende seu extrator para rodar automaticamente

Opção 1: biblioteca schedule do Python (a mais simples e compatível com várias plataformas):

1import schedule
2import time
3def run_scraper():
4    asyncio.run(main())
5schedule.every().day.at("06:00").do(run_scraper)
6schedule.every().day.at("18:00").do(run_scraper)
7while True:
8    schedule.run_pending()
9    time.sleep(60)

Opção 2: cron job (Linux/Mac):

1# Executa às 6h e às 18h todos os dias
20 6,18 * * * cd /path/to/scraper && python scraper.py

Opção 3: Agendador de Tarefas do Windows — crie uma tarefa básica para executar python scraper.py no horário desejado.

A contrapartida: todas essas opções exigem uma máquina sempre ligada. Se você rodar isso em um notebook que entra em suspensão, vai perder execuções.

Armazene o histórico de preços

Em vez de sobrescrever um arquivo JSON, passe a adicionar os dados em um banco SQLite:

1import sqlite3
2from datetime import datetime
3def init_db():
4    conn = sqlite3.connect("flights.db")
5    conn.execute("""
6        CREATE TABLE IF NOT EXISTS flights (
7            id INTEGER PRIMARY KEY AUTOINCREMENT,
8            scrape_date TEXT NOT NULL,
9            route TEXT NOT NULL,
10            airline TEXT,
11            departure_time TEXT,
12            arrival_time TEXT,
13            duration TEXT,
14            stops TEXT,
15            price_usd REAL,
16            co2_emissions TEXT
17        )
18    """)
19    conn.execute(
20        "CREATE INDEX IF NOT EXISTS idx_route_date "
21        "ON flights(route, scrape_date)"
22    )
23    conn.commit()
24    return conn
25def save_flight(conn, route: str, flight: FlightData):
26    price_num = float(
27        flight.price.replace("$", "").replace(",", "")
28    ) if flight.price else None
29    conn.execute(
30        "INSERT INTO flights "
31        "(scrape_date, route, airline, departure_time, "
32        "arrival_time, duration, stops, price_usd, co2_emissions) "
33        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
34        (datetime.now().isoformat(), route, flight.airline,
35         flight.departure_time, flight.arrival_time,
36         flight.duration, flight.stops, price_num,
37         flight.co2_emissions)
38    )
39    conn.commit()

Depois de uma semana de coletas duas vezes por dia, você já terá dados suficientes para começar a identificar tendências.

Analise tendências de preço e configure alertas

Encontre a opção mais barata no histórico:

1import pandas as pd
2import sqlite3
3conn = sqlite3.connect("flights.db")
4df = pd.read_sql_query(
5    "SELECT * FROM flights WHERE route = 'SFO-JFK'", conn
6)
7summary = df.groupby("scrape_date")["price_usd"].agg(
8    ["min", "max", "mean"]
9)
10cheapest = df.loc[df["price_usd"].idxmin()]
11print(
12    f"Mais barato: US${cheapest['price_usd']:.0f} em "
13    f"{cheapest['scrape_date']} ({cheapest['airline']})"
14)

Dispare um alerta por e-mail quando o preço cair abaixo do seu limite:

1import smtplib
2from email.mime.text import MIMEText
3def send_price_alert(route, price, threshold, recipient):
4    msg = MIMEText(
5        f"Alerta de queda de preço! {route}: US${price:.0f} "
6        f"(abaixo do seu limite de US${threshold:.0f})"
7    )
8    msg["Subject"] = f"Oferta de voo: {route} por US${price:.0f}"
9    msg["From"] = "alerts@example.com"
10    msg["To"] = recipient
11    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
12        server.login("alerts@example.com", "your_app_password")
13        server.send_message(msg)
14# Após cada coleta, verifique se há promoções
15min_price = df["price_usd"].min()
16threshold = 250
17if min_price < threshold:
18    send_price_alert("SFO-JFK", min_price, threshold,
19                     "you@email.com")

Frequência recomendada de coleta: duas vezes por dia é suficiente para acompanhar preços pessoais e reduz o risco de detecção. A cada 4 a 6 horas, se você estiver monitorando para um negócio. De hora em hora só durante períodos curtos de promoção, e temporariamente.

O caminho mais fácil: o Scheduled Scraper do Thunderbit

Se gerenciar cron jobs, servidor ligado e configuração de proxy parece infraestrutura demais para manter, o resolve o mesmo caso de uso sem esse peso. Você descreve o intervalo de coleta em linguagem simples, informa as URLs do Google Flights e o extrator roda automaticamente na infraestrutura em nuvem do Thunderbit — com tratamento anti-bot embutido e exportação direta para . Não substitui totalmente a abordagem em Python (você perde personalização), mas para o caso específico de "quero uma planilha com rastreamento de preços", é o caminho mais rápido. Você pode testar no .

Quando Python é exagero: formas sem código de extrair dados do Google Flights

Depois de montar tudo isso, vou ser sincero: são muitas peças móveis. Nem todo mundo precisa desse nível de controle. Seletores quebram, proxies precisam de rotação, cron jobs exigem monitoramento. Se o seu objetivo é simplesmente "colocar preços de voos em uma planilha com regularidade", existem opções mais rápidas.

Comparação: Python feito por conta própria vs. serviços de API vs. Thunderbit

AbordagemTempo de configuraçãoCódigo necessárioLida com anti-botAgendamentoCusto
Playwright DIY (este tutorial)1–2 horasPython (intermediário)Configuração manualManual (cron)Grátis + custos de proxy
Endpoint Google Flights do SerpApi15 minApenas chamadas de APIJá tratadoVia APICerca de US$ 50+/mês
Extensão Chrome do Thunderbit2 minNenhumExtração em nuvemAgendamento integradoPlano grátis disponível

Um ponto sobre o SerpApi: o Google , alegando que o volume de requisições aumentou 25.000% em dois anos. Esse nível de incerteza jurídica vale ser considerado se você estiver avaliando provedores de API.

Como o Thunderbit extrai dados do Google Flights

Abra os resultados da sua busca no Google Flights no Chrome, clique no botão "AI Suggest Fields" do Thunderbit — a IA lê a página e sugere colunas como companhia aérea, preço, horário de partida e escalas — revise os campos sugeridos e clique em "Scrape". Os resultados aparecem em uma tabela que você pode exportar para Excel, Google Sheets, Airtable ou Notion — tudo no .

Para o caso específico de rastreamento de preços, o Scheduled Scraper do Thunderbit e o (capaz de processar 50 páginas ao mesmo tempo) substituem toda a infraestrutura de cron + proxy + servidor.

Python oferece controle total e personalização ilimitada. Thunderbit oferece velocidade e zero manutenção. Escolha de acordo com seu objetivo real. Se quiser aprender mais sobre abordagens sem código, confira nosso guia sobre .

flight-data-options-comparison.webp

Usuários de fóruns perguntam isso com frequência: "extrair dados do Google Flights diretamente viola os termos de serviço do Google". É uma preocupação justa — principalmente porque a API foi descontinuada e não existe uma alternativa oficial sancionada.

Os Termos de Serviço do Google (atualizados em 22 de maio de 2024) dizem que o usuário não deve "acessar ou usar os Serviços ou qualquer conteúdo por meio de métodos automatizados (como robôs, spiders ou scrapers)". Violar os termos é uma quebra contratual (matéria civil) — não é a mesma coisa que cometer um crime.

O precedente jurídico mais importante: hiQ v. LinkedIn (Nono Circuito, 2022) estabeleceu que extrair dados publicamente disponíveis não viola o Computer Fraud and Abuse Act (CFAA). No entanto, o caso terminou em acordo, e o processo do Google contra o SerpApi em dezembro de 2025 usa outra teoria jurídica — a Seção 1201 da DMCA (circunvenção de medidas de proteção tecnológicas) — que pode ser mais séria.

Boas práticas para extração responsável

  • Limite a taxa de requisições — intervalos de 10 a 20 segundos com rotação de IP
  • Não colete dados pessoais — preços de voos são dados agregados exibidos publicamente
  • Não contorne CAPTCHAs programaticamente (essa é a área de risco da DMCA)
  • Use os dados para pesquisa pessoal, não para criar um produto comercial concorrente sem licenciamento adequado
  • Considere APIs oficiais quando houver disponibilidade

Fontes alternativas de dados

Se a extração parecer arriscada demais para o seu caso, há opções legítimas de API:

ProvedorCustoPlano grátisObservações
SerpApiUS$ 75–US$ 3.750+/mês250 buscas/mêsJSON direto do Google Flights (sob escrutínio jurídico)
Kiwi TequilaGrátis (modelo de afiliado)IlimitadoMelhor para startups e testes
AmadeusPagamento conforme uso2.000 requisições/mêsMais de 400 companhias aéreas, com capacidade de reserva
SkyscannerPersonalizadoRequer aprovação52 mercados, 30 idiomas

Escrevemos um detalhamento mais completo sobre se você quiser uma visão mais ampla.

Conclusão e principais aprendizados

Foi bastante coisa. O que importa é o seguinte:

  • Python + Playwright é a abordagem mais flexível para extrair dados do Google Flights, mas exige manutenção contínua
  • As medidas anti-bot (atrasos, rotação de user-agent, proxies residenciais) não são opcionais — são essenciais para a confiabilidade, principalmente depois do SearchGuard
  • Os seletores quebram com frequência — use aria-label e seletores baseados em texto sempre que possível, valide a saída e mantenha uma rotina de revisão
  • Automatize com schedule ou cron para transformar uma extração pontual em um rastreador de preços real, com histórico e alertas por e-mail
  • oferece uma alternativa sem código, com agendamento integrado, extração em nuvem e tratamento anti-bot — ideal se seu objetivo é uma planilha de rastreamento de preços, e não um projeto de programação
  • Respeite os limites legais — limite a taxa, colete apenas dados públicos e considere alternativas de API para uso comercial

Pegue o código deste tutorial ou instale a para seguir pelo caminho mais rápido. De qualquer forma, você vai passar a monitorar preços de voos em vez de atualizar o Google Flights manualmente.

Para mais técnicas de scraping em Python, confira nossos guias sobre e .

Perguntas frequentes

1. Posso extrair dados do Google Flights sem Python?

Sim. Serviços de API como SerpApi e Kiwi Tequila fornecem dados estruturados de voos por chamadas de API (sem necessidade de automação de navegador). Para uma abordagem totalmente sem código, a pode extrair resultados do Google Flights diretamente do navegador, com campos sugeridos por IA e exportação com um clique.

2. O Google bloqueia a extração de dados de voos?

O Google usa detecção de bots (SearchGuard), CAPTCHAs e limitação de taxa. Com medidas anti-bot adequadas — atrasos aleatórios, rotação de user-agent, proxies residenciais e configurações stealth no navegador — você consegue manter a extração confiável em volumes moderados. Veja a seção anti-bot acima para técnicas e limites específicos.

3. Com que frequência devo extrair o Google Flights para rastreamento de preços?

Duas vezes por dia, em horários aleatórios, é suficiente para acompanhar preços pessoais e mantém baixo o risco de detecção. Para monitoramento de negócios, a cada 4 a 6 horas com rotação de proxy. Evite coletas de hora em hora, exceto durante promoções de curta duração — isso aumenta bastante a chance de bloqueio.

4. Existe uma API gratuita do Google Flights?

A API oficial Google QPX Express foi . Não existe um substituto oficial gratuito. A opção gratuita mais próxima é a (modelo de afiliado, buscas ilimitadas). O SerpApi oferece 250 buscas grátis por mês. Para a maioria dos usuários, a extração direta ou uma ferramenta sem código como o Thunderbit é o caminho mais prático.

5. Por que meus seletores CSS do Google Flights continuam retornando dados vazios?

O Google usa o Closure Compiler para gerar nomes de classe ofuscados que mudam a cada compilação. Testes A/B e diferenças de localidade também fazem a estrutura HTML variar entre usuários. A solução: use atributos aria-label e seletores baseados em texto em vez de nomes de classe, adicione uma função de validação para detectar falhas cedo e revise seus seletores mensalmente. Veja a seção de manutenção de seletores para uma estratégia detalhada.

Saiba mais

Ke
Ke
CTO @ Thunderbit. Ke is the person everyone pings when data gets messy. He's spent his career turning tedious, repetitive work into quiet little automations that just run. If you've ever wished a spreadsheet could fill itself in, Ke has probably already built the thing that does it.
Sumário

Experimente a Thunderbit

Extraia leads e outros dados em apenas 2 cliques. Com tecnologia de IA.

Obter Thunderbit É grátis
Extraia dados usando IA
Transfira dados facilmente para Google Sheets, Airtable ou Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week