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

Última atualização em July 9, 2026
Como Extrair Dados do Google Flights com Python: do Código aos Alertas de Preço

O Google fechou a API do Flights lá atrás, em 2018, mas os preços das passagens não param de balançar — chegam a mudar 17 vezes em 48 horas numa única rota doméstica. Se você quer chegar nesses dados de forma programática, raspar a web é praticamente a única porta de entrada.

Passei um tempão testando jeitos diferentes de coletar dados de voo no Google, e o terreno mudou bastante — sobretudo depois que o Google soltou o SearchGuard em janeiro de 2025. Neste guia, vou mostrar como montar um extrator funcional para o Google Flights em Python usando Playwright, explicar como furar as barreiras anti-bot que travam quase todo mundo e, em seguida, transformar isso num rastreador automático de preços com alertas. E, se você quiser pular a parte de código, também tem uma alternativa sem programação com a Thunderbit que entrega o mesmo resultado em uns dois minutos.

Por que usar Python para extrair dados do Google Flights?

O Google Flights manda nas buscas por voo. A visibilidade no mobile, nos EUA, subiu para 47,6%, passando à frente de todas as principais OTAs. O mercado de metabusca de viagens por trás disso foi avaliado em US$ 8,33 bilhões em 2024, com crescimento anual composto de 30,2%. Mesmo assim, desde que a API QPX Express foi desligada de vez em 10 de abril de 2018, não há jeito oficial de acessar esses dados programaticamente.

Ao mesmo tempo, os preços dos voos oscilam até 50% 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 trabalham com 77 faixas tarifárias na precificação dinâmica. A passagem média de ida e volta nos EUA, no começo de 2026, ficou em US$ 408, com as tarifas 14,9% acima do ano anterior.

Plataforma dominante, sem API e com preços nervosos. É por isso que raspar o Google Flights com Python virou um dos projetos mais populares no GitHub e nos fóruns de viagem.

Veja quem ganha com isso 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
Times 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 companhiasPesquisa acadêmica e de mercado

Usuários de fórum são bem diretos sobre o porquê de terem partido para a raspagem: "O Google Flights API foi descontinuado e eu deveria usar web scraping" é um sentimento que aparece o tempo todo. E o retorno compensa — a Hopper diz ter 95% de precisão nas previsões ao processar 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 por volta de US$ 25 em voos domésticos.

Que dados dá para extrair do Google Flights?

A página de resultados do Google Flights guarda um conjunto de dados surpreendentemente rico. Em geral, dá para pegar:

  • 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 (na moeda escolhida)
  • Emissões de CO2 (kg CO2e, com diferença percentual em relação aos voos típicos)
  • Classe da viagem, número do voo, modelo da aeronave
  • Especificação de espaço para as pernas
  • Recursos a bordo (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:

{
  "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"
    }
  ]
}

Preparando 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 inteiro
  • O que você vai precisar: Python 3.7+, noções básicas de Python e um navegador baseado em Chrome

Instale as bibliotecas necessárias

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

pip install playwright playwright-stealth pandas
playwright install chromium
  • Playwright — automação de navegador headless, dá conta da renderização em JavaScript e tem mecanismos nativos de espera
  • playwright-stealth — reduz os sinais mais comuns de detecção de bot
  • pandas — para analisar os dados e exportar para CSV mais tarde

Por que Playwright em vez de Selenium ou requests

O Google Flights não roda só com requests + BeautifulSoup — o conteúdo da página é renderizado inteiramente 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 óbvia.

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

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

google-flights-scraping-workflow.webp

Passo 1: Defina suas classes de dados

Comece estruturando os parâmetros de busca e os dados do voo com dataclasses do Python. Isso deixa o código organizado e facilita expandir depois.

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

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

Cada campo corresponde direto ao que vamos extrair da página. Ter essa estrutura desde o início evita lidar com dicionários bagunçados lá na frente.

Passo 2: Entenda a estrutura da URL do Google Flights

O Google Flights codifica os parâmetros de busca em Protobuf no formato Base64, dentro do 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:

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

Se quiser mais controle, dá para gerar as URLs programaticamente:

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"

A outra opção — reverter a codificação Protobuf — dá mais precisão, mas quebra quando o Google muda o formato interno. Bibliotecas como a fast-flights no GitHub usam decodificação Protobuf para fugir totalmente da análise de HTML, mas essa é uma abordagem mais avançada.

Passo 3: Inicie o navegador e abra o Google Flights

Aqui está a configuração com Playwright. Vamos usar o playwright-stealth para reduzir o risco de detecção logo de cara.

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",
        )
        # Pré-define o cookie de consentimento para pular o pop-up
        await context.add_cookies([{
            "name": "SOCS",
            "value": "CAESHwgBEhJnd3NfMjAyNTAyMjctMF9SQzIaBXpoLUNOIAEaBgiAy6O-Bg",
            "domain": ".google.com",
            "path": "/"
        }])
        page = await context.new_page()

Estamos rodando em modo headless para produção (mude para headless=False na hora de depurar), definindo uma viewport e um user agent realistas e já 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 montada e espere os resultados dos voos aparecerem:

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

        # Aguarde o carregamento dos resultados
        await page.wait_for_selector(
            "li.pIav2d", timeout=15000
        )

Se der timeout aqui, normalmente é porque o pop-up de consentimento travou a página (veja a correção com cookie no Passo 3) ou porque o Google está mostrando um CAPTCHA. Vamos tratar dos dois cenários na seção anti-bot.

Passo 5: Carregue todos os resultados de voo

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

        # Clique em "Mostrar mais voos" até carregar todos os resultados
        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

Esse loop clica no botão, espera 2 segundos para os novos resultados renderizarem e para quando o botão não aparece mais. 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 de voo de fato na página já carregada. Aqui estão os seletores (verificados em abril de 2026 — veja a seção de manutenção mais abaixo para entender por que essa data importa):

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

        for card in cards:
            flight = FlightData()

            # Nome da companhia aérea
            airline_el = await card.query_selector(
                "div.sSHqwe span:not([class])"
            )
            if airline_el:
                flight.airline = (await airline_el.inner_text()).strip()

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

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

            # Duração
            dur_el = await card.query_selector("div.gvkrdb")
            if dur_el:
                flight.duration = (await dur_el.inner_text()).strip()

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

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

            # Emissões de CO2
            co2_el = await card.query_selector("div.O7CXue")
            if co2_el:
                flight.co2_emissions = (
                    await co2_el.get_attribute("aria-label") or ""
                ).strip()

            flights.append(flight)

        await browser.close()
        return flights

Atenção a um detalhe: classes como pIav2d, sSHqwe e FpEdX são geradas pelo Closure Compiler do Google e podem mudar a cada nova compilação. Já os seletores baseados em aria-label costumam ser mais estáveis. Vou detalhar uma estratégia completa de manutenção mais 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):

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)

    # Também salva como 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())

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

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

A maioria dos tutoriais para por aqui. A maioria dos extratores também quebra por aqui. O Google lançou o SearchGuard em janeiro de 2025, e isso derrubou quase todos os scrapers de SERP de um dia para o outro. O próprio Google descreve a solução como "o resultado de dezenas de milhares de horas de trabalho e milhões de dólares de investimento". O Google Flights tira nota 4/5 de dificuldade para extração.

Nenhum artigo concorrente entra fundo nesse ponto, mas é justamente essa a principal razão pela qual os scrapers param de funcionar. Veja o que você tem pela frente e como lidar.

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:

import time
import random

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

Coloque isso entre as navegações de página. Intervalos fixos (tipo exatos 5 segundos toda vez) entregam o jogo — prefira valores aleatórios.

Rotação de User-Agent

Mandar a mesma string de user-agent em todas as requisições é um sinal fácil de captar. Vá alternando a partir de uma lista:

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)

Bypass de detecção em modo headless

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

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

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 dar conta.

Rotação de proxy: datacenter vs. residencial

Para qualquer coisa além de algumas buscas, você vai precisar de proxies. E a diferença pesa:

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 180x mais baratos por requisição bem-sucedida na hora de raspar 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:

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

Como lidar com o pop-up de consentimento de cookies

Usuários reclamam com frequência que o pop-up "Aceito os termos" trava o acesso: "primeiro o Google vai mostrar o pop-up 'I agree to terms and conditions'." O jeito mais limpo de resolver é pré-configurar o cookie SOCS (mostrado no Passo 3). Se não pegar, clique no botão na mão:

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  # 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 por volta de 100 requisições por minuto por IP antes de aparecer 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 vivem quebrando (e como resolver)

Esse é, disparado, o maior ponto de dor. Threads de fórum estão lotadas de variações de "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

No fim das contas, são três fatores:

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

  2. Testes A/B. Usuários diferentes veem estruturas HTML diferentes ao mesmo tempo. Seu scraper pode rodar liso na sua máquina e falhar para outra pessoa 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 resistentes

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

# Frágil — quebra a cada compilação
price_el = await card.query_selector("div.BVAVmf > div.YMlIz")

# Mais resistente — ligado a rótulos de acessibilidade
dep_el = await card.query_selector('span[aria-label*="Departure time"]')

# Também resistente — correspondência por texto
more_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 de propósito para funcionalidade
  3. Atributos role — papéis ARIA são semânticos
  4. Seletores baseados em texto — combinam com o conteúdo visível
  5. Correspondência parcial de classe — por exemplo, [class*="price"]
  6. Nomes completos de classes ofuscadas — evite sempre que der

Adicione uma função de validação

Não deixe seletores quebrados gerarem dados vazios sem ninguém perceber. Pegue isso cedo:

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"Campo ausente '{field_name}' — os seletores podem precisar de atualização"
            )
            valid = False
    return valid

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

Estratégia de manutenção dos seletores

  • Confira os seletores todo mês, ou na hora, se a qualidade da saída cair
  • Mantenha os seletores num dicionário de configuração à parte, para ajustar com facilidade
  • Os seletores deste artigo foram verificados pela última vez em abril de 2026
  • Considere a biblioteca fast-flights como alternativa — ela usa decodificação Protobuf no lugar de seletores CSS, fugindo quase que totalmente desse problema (embora também tenha a própria fragilidade quando o Google muda 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 sozinho

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

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)

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

# Executa às 6h e às 18h todos os dias
0 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 que você quiser.

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

Guarde o histórico de preços

Em vez de sobrescrever um arquivo JSON, passe a anexar os dados num banco SQLite:

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

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

Analise as tendências de preço e configure alertas

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

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"Mais barato: US${cheapest['price_usd']:.0f} em "
    f"{cheapest['scrape_date']} ({cheapest['airline']})"
)

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

import smtplib
from email.mime.text import MIMEText

def send_price_alert(route, price, threshold, recipient):
    msg = MIMEText(
        f"Alerta de queda de preço! {route}: US${price:.0f} "
        f"(abaixo do seu limite de US${threshold:.0f})"
    )
    msg["Subject"] = f"Oferta de voo: {route} por US${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)

# Após cada coleta, verifique se há promoções
min_price = df["price_usd"].min()
threshold = 250
if min_price < threshold:
    send_price_alert("SFO-JFK", min_price, threshold,
                     "you@email.com")

Frequência de coleta recomendada: duas vezes por dia já basta 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 janelas curtas de promoção, e de forma temporária.

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

Se gerenciar cron jobs, servidor sempre ligado e configuração de proxy parece infraestrutura demais para cuidar, o Scheduled Scraper da Thunderbit 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 sozinho na infraestrutura em nuvem da Thunderbit — com tratamento anti-bot já embutido e exportação direta para Google Sheets, Excel ou Airtable. Não substitui de todo a abordagem em Python (você abre mão de personalização), mas, para o caso específico de "quero uma planilha com rastreamento de preços", é o caminho mais rápido. Dá para testar no plano grátis.

Quando Python é exagero: jeitos 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 pedem monitoramento. Se o seu objetivo é simplesmente "ter preços de voo numa planilha com regularidade", existem opções mais rápidas.

Comparação: Python na unha vs. serviços de API vs. Thunderbit

AbordagemTempo de configuraçãoCódigo necessárioLida com anti-botAgendamentoCusto
Playwright na unha (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 da Thunderbit2 minNenhumExtração em nuvemAgendamento integradoPlano grátis disponível

Um detalhe sobre o SerpApi: o Google entrou com uma ação de DMCA contra o SerpApi em dezembro de 2025, alegando que o volume de requisições subiu 25.000% em dois anos. Esse nível de incerteza jurídica vale pesar se você estiver avaliando provedores de API.

Como a Thunderbit extrai dados do Google Flights

Abra os resultados da sua busca no Google Flights no Chrome, clique no botão "AI Suggest Fields" da 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 numa tabela que você exporta para Excel, Google Sheets, Airtable ou Notion — tudo no plano grátis.

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

Python oferece controle total e personalização sem limite. A Thunderbit oferece velocidade e zero manutenção. Escolha conforme o seu objetivo real. Se quiser saber mais sobre abordagens sem código, veja nosso guia sobre os melhores web scrapers sem código.

flight-data-options-comparison.webp

É legal extrair dados do Google Flights? O que você precisa saber

Usuários de fórum perguntam isso direto: "extrair dados do Google Flights diretamente viola os termos de serviço do Google". É uma preocupação justa — ainda mais porque a API foi descontinuada e não há uma alternativa oficial chancelada.

Violação dos termos vs. responsabilidade legal

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 meios automatizados (como robôs, spiders ou scrapers)". Violar os termos é uma quebra contratual (matéria civil) — não é a mesma coisa que cometer crime.

O precedente jurídico mais importante: hiQ v. LinkedIn (Nono Circuito, 2022) firmou que extrair dados de acesso público não viola o Computer Fraud and Abuse Act (CFAA). Ainda assim, o caso terminou em acordo, e o processo do Google contra o SerpApi, de dezembro de 2025, usa outra teoria jurídica — a Seção 1201 da DMCA (circunvenção de medidas técnicas de proteção) — que pode ser mais séria.

Boas práticas para uma 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 voo são dados agregados exibidos publicamente
  • Não contorne CAPTCHAs de forma programática (essa é a zona 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

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 as implicações legais do web scraping se você quiser uma visão mais ampla.

Conclusão e principais aprendizados

Foi muita coisa. O que importa é o seguinte:

  • Python + Playwright é a abordagem mais flexível para extrair dados do Google Flights, mas pede 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, ainda mais depois do SearchGuard
  • Os seletores quebram com frequência — use aria-label e seletores baseados em texto sempre que der, valide a saída e mantenha uma rotina de revisão
  • Automatize com schedule ou cron para transformar uma extração pontual num rastreador de preços de verdade, com histórico e alertas por e-mail
  • A Thunderbit oferece uma alternativa sem código, com agendamento integrado, extração em nuvem e tratamento anti-bot — ideal se o 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 extensão Chrome da Thunderbit para ir pelo caminho mais rápido. De um jeito ou de outro, você vai passar a monitorar preços de voo em vez de ficar atualizando o Google Flights na mão.

Para mais técnicas de scraping em Python, veja nossos guias sobre como fazer web scraping com Python e as melhores ferramentas de web scraping em Python.

Perguntas frequentes

1. Posso extrair dados do Google Flights sem Python?

Pode. Serviços de API como SerpApi e Kiwi Tequila fornecem dados estruturados de voo por chamadas de API (sem precisar de automação de navegador). Para uma abordagem totalmente sem código, a extensão Chrome da Thunderbit pode extrair resultados do Google Flights direto do navegador, com campos sugeridos por IA e exportação com um clique.

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

O Google usa detecção de bot (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 rastrear preços?

Duas vezes por dia, em horários aleatórios, já basta para acompanhar preços pessoais e mantém o risco de detecção baixo. Para monitoramento de negócio, a cada 4 a 6 horas com rotação de proxy. Evite coletas de hora em hora, exceto em 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 descontinuada em abril de 2018. Não há um substituto oficial gratuito. A opção gratuita mais próxima é a Kiwi Tequila API (modelo de afiliado, buscas ilimitadas). O SerpApi oferece 250 buscas grátis por mês. Para a maioria das pessoas, a extração direta ou uma ferramenta sem código como a Thunderbit é o caminho mais prático.

5. Por que meus seletores CSS do Google Flights vivem 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 no lugar de nomes de classe, adicione uma função de validação para pegar falhas cedo e revise seus seletores todo mês. Veja a seção de manutenção de seletores para uma estratégia detalhada.

Saiba mais

Ke
Ke
CTO na Thunderbit | Cientista de Dados Sênior e Especialista em ML Com quase uma década de experiência em machine learning e data science, Ke Shen é ex-aluno da Columbia University e foi Cientista de Dados Sênior na Walmart Labs. Com profunda experiência, reconhecida pelos pares, em Python, R, Java e Estatística, ele compartilha insights testados em batalha sobre como levar algoritmos complexos de IA da teoria à arquitetura pronta para produção.
Índice

Extraia uma página só perguntando

Diga o que você precisa em inglês simples. Ou melhor: nem precisa dizer nada.

Experimente 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