Use Cases

Monitoramento de tarifas aéreas com tratamento CAPTCHA

Consultar tarifas em muitas rotas ao mesmo tempo é o tipo de tráfego que sites de viagem tentam conter com reCAPTCHA v2, Cloudflare Turnstile e limitação de requisições — normalmente ativados no instante em que o preço muda. A correção não está em reescrever o coletor de dados: é encaixar a resolução do CAPTCHA como mais uma etapa do pipeline, entre a requisição da página e a extração do preço, para o monitor seguir coletando dados sem intervenção manual.

Este guia mostra como montar o workflow em Python, do agendamento à captura do preço, com um exemplo fora dos EUA: workers na região sa-east-1 da AWS (São Paulo) reduzem a latência ao consultar sites de companhias aéreas.


Perguntas frequentes antes de montar o pipeline

Antes de escrever a primeira linha do monitor, vale responder ao que costuma decidir se o projeto compensa o esforço: quanto custa manter rodando, com que frequência checar cada rota, onde hospedar os workers e o que fazer com os dados de tarifa coletados ao longo do tempo.

Quanto custa manter um monitor de tarifas rodando com a CaptchaAI?

Os planos são por thread, com resoluções ilimitadas — o custo depende de quantas verificações simultâneas você roda, não do número de CAPTCHAs. Um BASIC (US$ 15/mês, 5 threads) cobre um monitor pequeno; para dezenas de rotas em paralelo, considere ADVANCE (US$ 90/mês, 50 threads).

Com que frequência vale a pena verificar as tarifas?

De 4 a 8 horas costuma ser o ponto de equilíbrio: checagens mais frequentes detectam mudanças de preço mais rápido, mas também aumentam CAPTCHAs e consumo de threads.

Faz diferença rodar o monitor perto da América do Sul?

Sim, na latência. Workers numa região próxima — sa-east-1 da AWS, em São Paulo — reduzem o tempo de ida e volta até os sites de companhias aéreas.

O LGPD afeta como eu armazeno os dados de tarifa coletados?

Só se o payload guardar dado pessoal além do preço, como e-mail ou CPF do passageiro. Guardando apenas rota, data e valor, o tema não se aplica.

Preciso de proxy para não ser bloqueado durante o monitoramento?

Na prática, sim: sites de viagem bloqueiam IPs de datacenter com muitas requisições seguidas. Combine egress de rede autorizado, rotação de proxy e intervalos realistas.


Como funciona o pipeline de monitoramento

Cada rota agenda, solicita a página e resolve o CAPTCHA antes de repetir a requisição, quando aparece.

Schedule check → Request fare page → CAPTCHA detected?
                                         ↓ Yes
                                    Solve via CaptchaAI → Inject token → Retry request
                                         ↓ No
                                    Parse fare data → Store → Alert on price change

O que você precisa

  • Chave de API CaptchaAI — obtenha a sua em captchaai.com.
  • Python 3.8+ com a biblioteca requests.
  • Proxy com egress de rede autorizado para os sites de viagem que você for monitorar.
pip install requests

Função auxiliar para resolver CAPTCHA com a CaptchaAI

As funções abaixo resolvem reCAPTCHA v2 e Turnstile via CaptchaAI e devolvem o token pronto para injetar.

import requests
import time

API_KEY = "YOUR_API_KEY"


def solve_recaptcha_v2(sitekey, pageurl):
    """Solve reCAPTCHA v2 and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "userrecaptcha",
        "googlekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(20)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")


def solve_turnstile(sitekey, pageurl):
    """Solve Cloudflare Turnstile and return the token."""
    submit = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY, "method": "turnstile",
        "sitekey": sitekey, "pageurl": pageurl, "json": 1
    }).json()

    if submit.get("status") != 1:
        raise RuntimeError(f"Submit error: {submit.get('request')}")

    task_id = submit["request"]
    time.sleep(10)

    for _ in range(30):
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": 1
        }).json()
        if result.get("status") == 1:
            return result["request"]
        if result.get("request") != "CAPCHA_NOT_READY":
            raise RuntimeError(f"Solve error: {result['request']}")
        time.sleep(5)
    raise TimeoutError("Solve timed out")

Classe FareMonitor: verificação de tarifas com CAPTCHA

A classe reaproveita as funções acima: detecta o desafio, resolve, injeta o token certo e refaz a requisição. Ajuste _parse_fare por companhia aérea.

import json
from datetime import datetime


class FareMonitor:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {
                "http": f"http://{proxy}",
                "https": f"http://{proxy}"
            }
        self.fare_history = {}

    def check_fare(self, route):
        """Check fare for a route, solving CAPTCHAs if needed."""
        url = route["url"]

        response = self.session.get(url)

        # Detect CAPTCHA in response
        if self._has_recaptcha(response.text):
            sitekey = self._extract_sitekey(response.text)
            token = solve_recaptcha_v2(sitekey, url)
            response = self.session.post(url, data={
                "g-recaptcha-response": token,
                **route.get("params", {})
            })

        elif self._has_turnstile(response.text):
            sitekey = self._extract_turnstile_key(response.text)
            token = solve_turnstile(sitekey, url)
            response = self.session.post(url, data={
                "cf-turnstile-response": token,
                **route.get("params", {})
            })

        return self._parse_fare(response.text, route)

    def _has_recaptcha(self, html):
        return "g-recaptcha" in html or "recaptcha/api" in html

    def _has_turnstile(self, html):
        return "cf-turnstile" in html or "turnstile" in html

    def _extract_sitekey(self, html):
        # Extract data-sitekey from reCAPTCHA div
        if 'data-sitekey="' in html:
            start = html.index('data-sitekey="') + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _extract_turnstile_key(self, html):
        if 'data-sitekey="' in html:
            idx = html.index("cf-turnstile")
            start = html.index('data-sitekey="', idx) + 14
            end = html.index('"', start)
            return html[start:end]
        return None

    def _parse_fare(self, html, route):
        """Parse fare data from the response. Customize per target site."""
        # Placeholder — implement per site
        return {
            "route": route["name"],
            "timestamp": datetime.now().isoformat(),
            "raw_length": len(html)
        }

    def monitor_routes(self, routes):
        """Check all routes and report price changes."""
        results = []
        for route in routes:
            try:
                fare = self.check_fare(route)
                results.append(fare)
                print(f"[OK] {route['name']}: checked")
            except Exception as e:
                print(f"[ERROR] {route['name']}: {e}")
        return results


# Usage
routes = [
    {
        "name": "NYC-LAX",
        "url": "https://example-airline.com/search?from=JFK&to=LAX&date=2025-08-15",
        "params": {"adults": 1}
    },
    {
        "name": "SFO-ORD",
        "url": "https://example-airline.com/search?from=SFO&to=ORD&date=2025-08-20",
        "params": {"adults": 1}
    }
]

monitor = FareMonitor(proxy="user:pass@proxy.example.com:8080")
results = monitor.monitor_routes(routes)

for r in results:
    print(json.dumps(r, indent=2))

Como agendar as verificações automaticamente

Rode o monitor em um agendador — cron no Linux, Agendador de Tarefas no Windows, ou um cron job gerenciado se os workers estiverem na nuvem:

# Check fares every 6 hours
0 */6 * * * cd /path/to/project && python fare_monitor.py >> /var/log/fares.log 2>&1

Problemas comuns e como resolver

Problema Causa Correção
CAPTCHAs frequentes Muitas requisições do mesmo IP Aumente o intervalo entre verificações e use rotação de proxy autorizada
Preços desatualizados Páginas em cache Adicione cabeçalhos anti-cache ou randomize os parâmetros da requisição
IP bloqueado Rate limiting Aumente os atrasos entre verificações e alterne os proxies
Resolução do CAPTCHA falha Sitekey extraída errada Confirme se a sitekey corresponde ao CAPTCHA exibido na página

Checklist para captura confiável de tarifas

  • Grave tarifa base, taxas, moeda e classe tarifária no mesmo payload, para comparações históricas continuarem válidas se o layout mudar.
  • Mantenha dois seletores alternativos por campo e salve um snapshot do HTML sempre que o parser falhar.
  • Registre rota, data de saída, horário da coleta e tempo de resolução do CAPTCHA — o histórico mostra se o site está enrijecendo a detecção.
  • Se o payload guardar e-mail ou CPF de passageiro, considere a LGPD; para só acompanhar preço, restrinja o payload a rota, data e valor.
  • Valide reCAPTCHA e Turnstile em staging antes de produção: veja o guia de Turnstile em staging.

Comece a monitorar tarifas com a CaptchaAI

Crie sua conta e obtenha a chave de API em captchaai.com. Plugue a chave no monitor acima e deixe o pipeline resolver os CAPTCHAs sozinho.


Guias relacionados

Os comentários estão desativados para este artigo.