🎯 Um navegador em nuvem personalizável e anti-detecção alimentado por Chromium desenvolvido internamente, projetado para rastreadores web e agentes de IA. 👉Experimente agora
De volta ao blog

Construa um Rastreador de Preços de Voos com a API de Scraping Scrapeless

Sophia Martinez
Sophia Martinez

Specialist in Anti-Bot Strategies

14-Jul-2026

TL;DR:

  • Um monitor de voos é uma rota e uma data, não uma URL. As tarifas aéreas não têm uma única página canônica como um SKU de varejo — a mesma viagem é uma combinação de departure_id, arrival_id, outbound_date e (para uma viagem de ida e volta) return_date. O pipeline abaixo consulta essa combinação diretamente através do ator scraper.google.flights da Scrapeless Scraping API, em vez de renderizar e rastrear a própria interface do Google Flights.
  • O tipo de viagem é um parâmetro de primeira classe, não uma variante de URL. data_type alterna a mesma solicitação entre viagem de ida, ida e volta e multi-cidade, e multi-cidade leva seu próprio array de pernas multi_city_json. Um monitor de preços de varejo nunca precisa dessa dimensão; um monitor de voos precisa desde o primeiro dia.
  • Google Flights fornece seu próprio sinal de volatilidade. O objeto price_insights da resposta carrega lowest_price e uma classificação de price_level (baixo, típico, alto) juntamente com as ofertas individuais em best_flights e other_flights. Isso permite que o pipeline alerte sobre "essa é uma boa tarifa agora", não apenas "isso é mais barato do que da última vez."
  • O histórico é indexado por rota e data, anexado uma vez por verificação. Um log JSONL com um registro por combinação (departure_id, arrival_id, outbound_date, return_date) é suficiente para calcular um mínimo em execução e detectar uma queda — nenhum banco de dados é necessário até que a lista de monitoramento cresça além de um punhado de rotas.
  • Uma queda, ou uma mudança de price_level para baixo, dispara um webhook. A decisão de alerta combina a comparação numérica com o próprio sinal qualitativo do Google, e publica uma vez no endpoint que o recebe.
  • Um programador executa o loop; nada sobre ele precisa permanecer em execução. Cron, Agendador de Tarefas ou um timer serverless executa a verificação em um ritmo e sai — o pipeline não carrega nenhum processo de longa duração.
  • Gratuito para começar. Novas contas Scrapeless incluem créditos gratuitos da Scraping API — inscreva-se em app.scrapeless.com.

Introdução: uma tarifa é um alvo em movimento com duas dimensões, não uma

Um preço de varejo é um único número associado a uma única página. Uma tarifa de voo é uma função de uma rota e uma data, e a mesma rota pode ter preços diferentes para cada data de partida dentro da mesma busca. Essa é a razão pela qual a própria funcionalidade de rastreamento de preços de voos do Google existe: as companhias aéreas reprecificam continuamente o inventário de assentos contra a demanda, e um comprador que verifica uma vez e reserva está adivinhando o momento. O rastreador do Google envia um resumo quando a rota rastreada muda; ele não fornece ao leitor dados brutos, um webhook ou uma maneira de combinar esse sinal com qualquer outra coisa que o leitor já esteja utilizando.

Este post constrói um pequeno pipeline que faz esse trabalho programaticamente, em cima de uma rota específica e janela de data em vez da interface do Google Flights. A Scraping API da Scrapeless fornece um ator construído especificamente para essa superfície — scraper.google.flights — que aceita uma combinação de rota e data e retorna dados de tarifa estruturados: ofertas individuais, uma companhia aérea e duração por oferta, e a própria leitura do nível de preço do Google sobre a tarifa. O pipeline consulta esse ator, extrai os sinais de tarifa, os anexa a um log de histórico indexado por rota e data, decide se a leitura vale um alerta e dispara um webhook quando vale. Um programador executa a verificação em um ritmo.

O ator retornou uma resposta disabled actor ao vivo para a conta usada para pesquisar este post — um gap de nível de plano, não um pipeline quebrado — e esse gap está documentado no ponto em que importa, em vez de ser ignorado. Cada outro estágio abaixo é exercido contra Python real, executado.

O Que Você Pode Fazer Com Isso

  • Monitoramento de tarifas pessoais. Rastreie uma rota específica e uma janela de datas e receba notificações somente quando o preço realmente se mover em uma direção útil.
  • Compras em datas flexíveis. Execute a mesma rota em uma variedade de datas candidatas e compare price_insights.price_level ao longo do tempo para encontrar a janela mais barata, não apenas a data única mais barata.
  • Monitoramento de viagens corporativas. Um escritório de viagens pode monitorar rotas recorrentes de deslocamento ou de clientes e sinalizar quando a rota de um itinerário reservado caiu, levando a uma nova reserva.
  • Monitoramento de itinerários multi-cidade. multi_city_json permite que a mesma solicitação represente uma viagem com várias pernas, assim, um itinerário com três ou quatro segmentos é uma chamada ao invés de três ou quatro monitores de ida separados costurados à mão.
  • Conjuntos de dados de histórico de tarifas. O log apenas para anexar também serve como uma série temporal por rota, útil para análises de tendência posteriores ou para alimentar uma heurística "reserve agora ou espere."

Por que Scrapeless Scraping API para isso

A Scrapeless Scraping API expõe atores específicos de sites — scraper.<site> — que retornam JSON estruturado para um alvo específico em vez de uma página renderizada que um chamador precisa analisar. Para dados de voos especificamente, isso significa:

  • Um pedido de rota e data, não uma sessão de navegador. A chamada é uma POST autenticada com um corpo JSON; não há página para renderizar, nenhum seletor para ancorar e nenhuma sessão para manter ativa.
  • Tipo de viagem incorporado à estrutura do pedido. data_type abrange ida única (2), ida e volta (1) e múltiplas cidades (3, com multi_city_json) como parâmetros documentados, não como lógica separada de raspagem por caso.
  • Uma classificação de preços que o próprio site alvo calcula. price_insights.price_level é o próprio julgamento do Google sobre se a tarifa é baixa, típica ou alta para a rota — o pipeline pode se apoiar nisso em vez de adivinhar um limite do zero.
  • Um pedido em vez de uma pilha de distribuição. Dados de tarifas aéreas normalmente alcançam um chamador através de um mosaico de GDS e feeds diretos de companhias aéreas — o tipo de distribuição fragmentada o padrão de Nova Capacidade de Distribuição da IATA existe para padronizar do lado da indústria aérea. Uma única chamada de ator estruturada contorna completamente essa pilha para um monitoramento de preços somente leitura.

Obtenha uma chave de API de plano gratuito em app.scrapeless.com. A página do produto Scraping API cobre a família de atores que este pipeline chama, e a forma do pedido para este ator específico é detalhada em o guia de pedidos da API Google Flights.

Pré-requisitos

  • Python 3.10 ou mais recente, além do pacote requests (pip install requests).
  • Uma conta Scrapeless e chave API, lidas da variável de ambiente SCRAPELESS_API_KEY. Confirme que o ator scraper.google.flights está habilitado para o plano da conta antes de direcionar o tráfego de produção para ele — veja a nota abaixo em Fetch.
  • Um endpoint de webhook para receber alertas (Slack, Discord ou qualquer relé que aceite um POST JSON).
  • Um host compatível com cron, Windows Task Scheduler ou um agendador serverless para executar a verificação em uma cadência.

Pipeline em um Relance

O pipeline é composto por seis etapas ligadas em linha reta: buscar → descobrir → extrair → transformar → armazenar → decidir → alertar, com um agendador controlando tudo isso de forma cadenciada.

  1. Buscar — POST uma rota, uma data (ou janela de data) e um tipo de viagem para scraper.google.flights.
  2. Descobrir — ler o envelope do tipo de viagem de volta: qual dos best_flights, other_flights e price_insights a resposta realmente populou.
  3. Extrair — puxar o preço da oferta mais barata e a classificação price_level do Google desse envelope.
  4. Transformar — normalizar a leitura em um registro canônico único, indexado por rota e data.
  5. Armazenar — adicionar o registro a um log histórico.
  6. Decidir e alertar — comparar com o histórico e disparar um webhook quando a leitura vale a pena ser exibida.

Buscar: Consultar uma Rota e uma Janela de Data

O pedido é um único POST autenticado. O nome do ator vai em actor; os parâmetros de rota e data vão em input. data_type é 1 para ida e volta e 2 para ida única; uma ida e volta também inclui return_date.

python Copy
import os
import requests

API_URL = "https://api.scrapeless.com/api/v1/scraper/request"


def fetch_route(departure_id: str, arrival_id: str, outbound_date: str,
                 return_date: str | None = None, gl: str = "us", hl: str = "en") -> dict:
    payload = {
        "actor": "scraper.google.flights",
        "input": {
            "departure_id": departure_id,
            "arrival_id": arrival_id,
            "data_type": 1 if return_date else 2,   # 1 = ida e volta, 2 = ida única
            "outbound_date": outbound_date,
            "gl": gl,
            "hl": hl,
        },
    }
    if return_date:
        payload["input"]["return_date"] = return_date

    headers = {"Content-Type": "application/json", "x-api-token": os.environ["SCRAPELESS_API_KEY"]}
    response = requests.post(API_URL, headers=headers, json=payload, timeout=60)
    response.raise_for_status()
    return response.json()

outbound_date e return_date seguem o formato simples de data de calendário YYYY-MM-DD definido pela RFC 3339 para troca de data e hora na internet — sem componente de hora, uma vez que uma busca de tarifa está ligada a um dia de partida, não a um instante de partida. Um itinerário de múltiplas cidades substitui os campos de perna única com data_type: 3 e um array multi_city_json, um objeto por perna, cada um carregando seu próprio departure_id, arrival_id e data.

Gap de pré-requisitos: executar a chamada acima contra uma rota real retornou um HTTP 400 ao vivo para a conta usada para verificar este pipeline: {"code": 14002, "message": "ator desabilitado: scraper.google.flights"}. Essa mensagem é distinta do erro que um nome de ator não reconhecido retorna ("ator inválido: <nome>" — confirmado ao solicitar deliberadamente algumas variantes inventadas), o que significa que o ator em si é real e catalogado; ele está bloqueado do plano gratuito usado aqui, em vez de estar ausente. Os mecanismos de requisição subjacentes da conta não estão em questão — o ator irmão scraper.google.search retornou um HTTP 200 real com resultados orgânicos na mesma chave e no mesmo endpoint durante esta pesquisa. Confirme que o ator está habilitado para o plano alvo (GET /api/v1/me relata plano e saldo de créditos) antes de gerar tráfego de produção sobre ele, e trate tudo a partir do Discover como se estivesse executando contra a forma de resposta documentada do ator, em vez de uma página capturada ao vivo nesta sessão.

Discover: Lendo o Envelope do Tipo de Viagem

Uma busca de ida e volta ou de ida retorna dois arrays de ofertas — best_flights e other_flights — além de um objeto price_insights. Uma busca em múltiplas cidades retorna a estrutura equivalente por itinerário em vez de por perna única. Antes de extrair qualquer coisa, o pipeline apenas precisa saber quais dessas chaves a resposta realmente populou, uma vez que uma rota estreita ou uma data incomum podem retornar com other_flights vazio e apenas best_flights com ofertas.

python Copy
def envelope_shape(payload: dict) -> dict:
    return {
        "has_best": bool(payload.get("best_flights")),
        "has_other": bool(payload.get("other_flights")),
        "has_insights": bool(payload.get("price_insights")),
    }


if __name__ == "__main__":
    sample = {"best_flights": [{"price": 312}], "other_flights": [], "price_insights": {}}
    print(envelope_shape(sample))

Executar contra uma resposta estreita que apenas populou best_flights, envelope_shape retorna {'has_best': True, 'has_other': False, 'has_insights': False} — exatamente o caso que a etapa de extração abaixo precisa tolerar.

Esta é uma verificação de sanidade de uma linha, mas é importante: extrair uma "oferta mais barata" de um envelope que nunca populou best_flights produz um None em vez de um número errado, desde que a etapa de extração verifique ambos os arrays em vez de assumir que um deles está sempre presente.

Extrair: Puxar os Sinais de Tarifas da Resposta

A etapa de extração puxa a oferta mais barata em ambos os arrays e os campos de price_insights ao lado. O fixture abaixo espelha a forma da resposta documentada para essa família de atores — best_flights e other_flights como arrays de objetos {flights: [...], price, layovers}, e price_insights contendo lowest_price, price_level, e um typical_price_range — uma vez que a chamada ao vivo é a lacuna mencionada acima, em vez de uma página capturada nesta sessão.

python Copy
# Fixture ilustrativa correspondente à forma de resposta documentada (veja a
# nota da lacuna de pré-requisitos em Fetch) — não uma captura ao vivo.
FIXTURE_RESPONSE = {
    "best_flights": [
        {
            "flights": [{
                "departure_airport": {"id": "JFK", "time": "2026-08-10 08:00"},
                "arrival_airport": {"id": "LAX", "time": "2026-08-10 11:24"},
                "airline": "Example Air",
                "flight_number": "EX 101",
                "duration": 384,
                "travel_class": "Economy",
            }],
            "price": 312,
            "layovers": 0,
        }
    ],
    "other_flights": [
        {"flights": [{"airline": "Sample Airways", "flight_number": "SA 220"}], "price": 349, "layovers": 1}
    ],
    "price_insights": {
        "lowest_price": 289,
        "price_level": "baixo",
        "typical_price_range": [295, 430],
    },
}


def extract_fares(payload: dict) -> dict:
    best = payload.get("best_flights", [])
    other = payload.get("other_flights", [])
    insights = payload.get("price_insights", {})

    cheapest_offer = min([*best, *other], key=lambda offer: offer["price"], default=None)

    return {
        "cheapest_price": cheapest_offer["price"] if cheapest_offer else None,
        "cheapest_layovers": cheapest_offer["layovers"] if cheapest_offer else None,
        "offer_count": len(best) + len(other),
        "price_insights_lowest": insights.get("lowest_price"),
        "price_level": insights.get("price_level"),
        "typical_price_range": insights.get("typical_price_range"),
    }


if __name__ == "__main__":
    print(extract_fares(FIXTURE_RESPONSE))

Executar contra o fixture, extract_fares retorna:

text Copy
{'cheapest_price': 312, 'cheapest_layovers': 0, 'offer_count': 2,
 'price_insights_lowest': 289, 'price_level': 'baixo', 'typical_price_range': [295, 430]}

Observe a diferença entre cheapest_price (a oferta única mais baixa realmente retornada) e price_insights_lowest (o próprio piso do Google para a rota). Os dois raramente coincidem exatamente — price_insights reflete uma visão mais ampla sobre a rota do que as ofertas específicas nesta única resposta — portanto, mantenha ambos os campos em vez de colapsá-los em um único número.

Transformar: Normalizar em um Registro de Rota e Data

A identidade de um monitor de voos é a tupla de (departure_id, arrival_id, outbound_date, return_date), não uma URL. A fase de transformação constrói essa chave junto a uma forma de registro canônica para que o armazenamento e a comparação nunca tenham que re-derivá-la.

python Copy
from datetime import datetime, timezone


def to_record(route: dict, fares: dict) -> dict:
    return {
        "departure_id": route["departure_id"],
        "arrival_id": route["arrival_id"],
        "outbound_date": route["outbound_date"],
        "return_date": route.get("return_date"),
        "trip_type": "round_trip" if route.get("return_date") else "one_way",
        "price": fares["cheapest_price"],
        "price_level": fares["price_level"],
        "currency": "USD",
        "checked_at": datetime.now(timezone.utc).strftime("%d-%b-%Y %H:%M UTC"),
    }


def route_key(record: dict) -> str:
    return f"{record['departure_id']}-{record['arrival_id']}:{record['outbound_date']}:{record.get('return_date')}"


if __name__ == "__main__":
    sample_route = {"departure_id": "JFK", "arrival_id": "LAX",
                     "outbound_date": "2026-08-10", "return_date": "2026-08-17"}
    sample_fares = {"cheapest_price": 312, "price_level": "low"}
    record = to_record(sample_route, sample_fares)
    print(record)
    print(route_key(record))

Executando contra a rota de exemplo (JFK para LAX, 10 de agosto na saída, 17 de agosto no retorno) e as tarifas extraídas acima, to_record retorna um registro com price: 312, price_level: 'baixo', trip_type: 'round_trip', e route_key retorna JFK-LAX:2026-08-10:2026-08-17. Um monitor de ida em uma rota e data de saída iguais produz uma chave distinta no momento em que return_date está ausente, que é o ponto: dois tipos de viagem na mesma rota são dois monitores diferentes, não um.

Armazenar: Um Registro Somente de Anexos Chaveado por Rota e Data

O armazenamento é o mesmo padrão JSONL de anexos somente que um monitor de preço de varejo usa, com uma diferença: os filtros de pesquisa estão na chave de rota-data em vez de uma URL.

python Copy
import json

HISTORY_FILE = "flight_history.jsonl"


def append_history(record: dict) -> dict:
    with open(HISTORY_FILE, "a", encoding="utf-8") as f:
        f.write(json.dumps(record) + "\n")
    return record


def load_history(key: str) -> list[dict]:
    rows = []
    try:
        with open(HISTORY_FILE, encoding="utf-8") as f:
            for line in f:
                row = json.loads(line)
                if route_key(row) == key:
                    rows.append(row)
    except FileNotFoundError:
        pass
    return rows


if __name__ == "__main__":
    open(HISTORY_FILE, "w").close()  # iniciar esta demonstração a partir de um log vazio
    sample_record = {"departure_id": "JFK", "arrival_id": "LAX", "outbound_date": "2026-08-10",
                      "return_date": "2026-08-17", "trip_type": "round_trip",
                      "price": 312, "price_level": "low", "currency": "USD",
                      "checked_at": "13-Jul-2026 14:28 UTC"}
    append_history(sample_record)
    append_history(sample_record)
    with open(HISTORY_FILE, encoding="utf-8") as f:
        print(f"{len(f.readlines())} linha(s) em {HISTORY_FILE} após duas verificações")

Anexando o mesmo registro duas vezes e recarregando pela chave retorna ambas as linhas na ordem de inserção, confirmando que o log acumula em vez de sobrescrever — a mesma garantia que um arquivo somente de anexos dá a um monitor chaveado por URL, apenas filtrado em uma chave diferente. Passando por um punhado de rotas, troque o arquivo por uma pequena tabela SQLite com (departure_id, arrival_id, outbound_date, return_date) como uma chave composta; a forma de leitura e escrita permanece a mesma.

Decidir: Comparar Contra o Histórico e o Próprio Sinal de Preço do Google

Um monitor de preços de varejo tem uma regra de decisão: o novo preço é inferior ao mais baixo já visto até agora. Um monitor de tarifas tem um segundo sinal disponível — price_level — então a fase de decisão verifica ambos: uma verdadeira queda numérica abaixo do mais baixo em vigor, ou uma primeira leitura que já cai em price_level: "baixo".

python Copy
def is_price_drop(history: list[dict], current_price: float, current_level: str) -> dict:
    prior_prices = [r["price"] for r in history if r.get("price") is not None]
    previous_low = min(prior_prices) if prior_prices else None

    numeric_drop = (
        current_price is not None
        and previous_low is not None
        and current_price < previous_low
    )
    level_signal = current_level == "low"

    return {
        "dropped": numeric_drop,
        "worth_alerting": numeric_drop or (level_signal and previous_low is None),
        "current": current_price,
        "previous_low": previous_low,
json Copy
"preço_nível": nível_atual,
    }


se __nome__ == "__main__":
    histórico = [{"preço": 349}, {"preço": 340}]
    print(é_queda_de_preço(histórico, preço_atual=312, nível_atual="baixo"))

Contra duas leituras anteriores de 349 e 340, uma nova leitura de 312 com preço_nível: "baixo" retorna {'caiu': True, 'vale_alerta': True, 'atual': 312, 'anterior_baixo': 340, 'preço_nível': 'baixo'}. O campo vale_alerta é deliberadamente mais amplo do que caiu: ele também é acionado na primeira leitura de uma rota se a própria classificação do Google já a considera baixa, já que uma comparação de baixa corrida não tem nada para comparar, mas o sinal qualitativo tem. Essa distinção reflete algo que pesquisa sobre precificação dinâmica offline em mercados de passagens aéreas trata como uma característica estrutural da categoria: o inventário de assentos de companhias aéreas reprecifica contra a demanda e a capacidade remanescente, não contra um calendário de vendas fixo, então uma única captura instantânea pode ser informativa sem um histórico de preços para comparar.

Alerta: Acionar um Webhook em uma Queda

A mecânica de alerta é a mesma única requests.post que um monitor de varejo usa — uma chamada HTTP, sem fila, sem corretor — com a mensagem construída a partir da identidade da rota e data em vez de um nome de produto.

python Copy
def enviar_alerta(rótulo_rota: str, decisão: dict, webhook_url: str) -> int:
    mensagem = (
        f"Monitor de tarifas: {rótulo_rota}\n"
        f"Agora ${decisão['atual']} (era ${decisão['anterior_baixo']}), "
        f"Google preço_nível={decisão['preço_nível']}"
    )
    resposta = requests.post(webhook_url, json={"texto": mensagem}, timeout=15)
    resposta.raise_for_status()
    return resposta.status_code

raise_for_status() exibe um endpoint de webhook quebrado imediatamente em vez de deixar um relay mal configurado consumir silenciosamente cada alerta. O corpo {"texto": mensagem} corresponde à forma comum de webhook de entrada do Slack/Discord; ajuste o JSON para o que o endpoint receptor espera.

Agendar: Consultar em uma Cadência Limitada pela Viagem

Conectar busca através de alerta em uma única função dá a chamada check_route() que um agendador pode executar:

python Copy
def verificar_rota(id_partida, id_chegada, data_ida, data_volta=None, webhook_url=None):
    carga = buscar_rota(id_partida, id_chegada, data_ida, data_volta)
    tarifas = extrair_tarifas(carga)
    rota = {"id_partida": id_partida, "id_chegada": id_chegada,
             "data_ida": data_ida, "data_volta": data_volta}
    registro = adicionar_histórico(to_record(rota, tarifas))
    decisão = é_queda_de_preço(carregar_histórico(chave_rota(registro)), registro["preço"], registro["preço_nível"])
    se decisão["vale_alerta"] e webhook_url:
        enviar_alerta(f"{id_partida}-{id_chegada} {data_ida}", decisão, webhook_url)
    retornar registro, decisão

Uma execução diária é suficiente para a maioria dos monitores pessoais; um escritório de viagens corporativas que acompanha várias rotas recorrentes pode executar com mais frequência. Diferente de um SKU de varejo, um monitor de voo também tem um fim natural: uma vez que a data de ida passa, a rota deixa de ser uma busca ativa e o histórico para ela está concluído. Uma entrada de crontab é o driver mais simples no Linux ou macOS, usando a mesma cinco-campos especificação de tempo de crontab POSIX que qualquer trabalho agendado na plataforma usa:

bash Copy
# crontab -e — verificar duas vezes ao dia, 07:00 e 19:00
0 7,19 * * * cd /opt/monitor-tarifas && /usr/bin/python3 check.py >> monitor.log 2>&1

O Agendador de Tarefas do Windows executa o mesmo comando em um gatilho equivalente, e um temporizador sem servidor também funciona — o script não mantém estado além do arquivo de histórico, então uma invocação sem estado é adequada. Para uma lista de monitoramento de várias rotas, execute verificar_rota() sobre a lista e mantenha a concorrência modesta para que os limites de taxa da chave da API permaneçam confortáveis.

O Que Você Recebe de Volta

Cada verificação anexa um registro ao histórico, indexado por rota e data em vez de por URL:

json Copy
// O esquema reflete exatamente o que to_record()/adicionar_histórico() escreve.
// Os valores dos campos são ilustrativos, correspondendo ao fixture usado acima — não uma leitura ao vivo.
{
  "id_partida": "JFK",
  "id_chegada": "LAX",
  "data_ida": "2026-08-10",
  "data_volta": "2026-08-17",
  "tipo_viagem": "ida_e_volta",
  "preço": 312,
  "preço_nível": "baixo",
  "moeda": "USD",
  "verificado_em": "13-Jul-2026 15:01 UTC"
}

Alguns pontos que vale a pena saber antes de executar isso em escala:

  • preço e preço_insights_mais_baixo podem divergir. O primeiro é a oferta mais barata que esta resposta específica retornou; o último é a leitura mais ampla do Google sobre a rota. Armazene ambos se o caso de uso downstream se preocupá com a diferença.

  • O valor armazenado deve ser o preço total, não uma tarifa base. As transportadoras dos EUA que publicitam uma tarifa são obrigadas pela regra federal de publicidade de tarifas completas a citar o preço total que um passageiro paga, e não uma tarifa base reduzida — trate qualquer campo que o pipeline armazena como preço da mesma forma, para que uma comparação entre verificações nunca seja uma tarifa base em um dia e um total completo em outro.

  • Multi-cidade muda a forma, não o pipeline. data_type: 3 e multi_city_json produzem uma resposta a nível de itinerário em vez de um único trecho; estenda to_record para incorporar o preço de cada trecho em um total em vez de tratar o itinerário como uma única tarifa fixa.

  • gl e hl afetam a moeda e a língua, não apenas a redação. Fixe ambos por observação da mesma forma que uma observação de varejo fixa proxy_country, para que uma comparação entre verificações permaneça no mesmo mercado.

  • Um preço nulável não é zero. Trate um cheapest_price ausente como None, exatamente como a fase de extração já faz — uma resposta ocasional estranha nunca deve injetar um falso zero no mínimo atual.

Conclusão: uma rota, uma data, uma decisão

O pipeline se reduz à mesma forma que qualquer outro monitor de preços — buscar, extrair, armazenar, decidir, alertar, agendar — com a identidade de um "monitor" redefinida para a categoria: um par de rota e data em vez de uma URL, um parâmetro de tipo de viagem em vez de uma única forma de solicitação, e um segundo sinal, fornecido pelo alvo (price_level), disponível ao lado da comparação numérica simples. Ampliar a lista de monitoramento para mais rotas, mais datas ou um itinerário multi-cidade reutiliza todas as etapas sem alterações; apenas os parâmetros passados para Fetch mudam.

Pronto para Construir Seu Pipeline de Dados Através de IA?

Junte-se à comunidade para trocar notas com desenvolvedores que estão construindo pipelines de dados na Scrapeless: Discord · Telegram.

Inscreva-se em app.scrapeless.com para créditos gratuitos da API de Scraping, confirme que o ator scraper.google.flights está ativado no plano da conta e adapte as etapas acima para as rotas que a lista de monitoramento precisa. Veja preços para detalhes do plano, e a documentação da API de Scraping para a referência atual do ator.

FAQ

P: O scraper.google.flights é um ator real, documentado?
Sim. Um nome de ator deliberadamente inventado retorna "invalid actor: <name>"; solicitar scraper.google.flights retorna "disabled actor: scraper.google.flights" — um erro distinto que se aplica apenas a um ator reconhecido e catalogado. Está restrito a alguns planos, em vez de ser inexistente; confirme que está habilitado para o plano da conta alvo antes de centralizar o tráfego de produção nele.

P: Por que chavear o histórico por rota e data em vez de uma URL, como faz um monitor de varejo?
Porque uma tarifa de voo não tem uma única página canônica da mesma forma que um SKU de varejo. Os preços da mesma rota variam conforme a data de partida, a data de retorno e o tipo de viagem, então a identidade de um "monitor" precisa ser essa combinação, não uma única URL.

P: O que o price_insights.price_level adiciona em comparação com uma simples comparação "mais barato do que da última vez"?
É a própria classificação do Google sobre se a tarifa atual é baixa, típica ou alta para a rota, calculada a partir de dados além da única resposta que o pipeline acabou de receber. Combiná-lo com a comparação de mínimo atual permite que a primeira leitura de uma rota seja acionável, não apenas as segundas e posteriores.

P: Isso pode lidar com um itinerário multi-cidade?
Sim, ao nível de solicitação — data_type: 3 com um array multi_city_json de trechos é uma forma de parâmetro documentada para esta família de atores. Estender to_record para incorporar um itinerário de vários trechos em um total comparável é a única peça de lógica extra que um monitor multi-cidade precisa além do que este post constrói.

P: Com que frequência a verificação deve ser realizada?
Uma execução diária ou duas vezes ao dia atende a maioria dos monitores de tarifas pessoais. Um monitor de voos também tem uma parada natural: uma vez que a data de partida passa, a rota não é mais uma busca ativa, ao contrário de um SKU de varejo que pode ser monitorado indefinidamente.

P: Isso pode funcionar sem um agente de IA?
Sim. O Python em Fetch através de Schedule roda de ponta a ponta por conta própria — chamar, extrair, armazenar, decidir, alertar e deixar um programador conduzir a cadência. Um agente é uma maneira conveniente de direcionar a seleção de rota e data em linguagem natural, mas o pipeline em si não precisa de nada além de Python e um programador.

Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.

Artigos mais populares

Catálogo