Use Cases

Monitoramento de preços de comércio eletrônico com tratamento CAPTCHA

Seu concorrente baixou o preço ontem à noite e o seu monitoramento só percebeu dois dias depois? Na maioria dos casos o culpado não é um bug no scraper — é um CAPTCHA que passou a barrar o bot bem na página do produto. Amazon, Walmart, eBay e boa parte das lojas Shopify reagem a tráfego automatizado com reCAPTCHA, Cloudflare Turnstile ou Cloudflare Challenge assim que o padrão de acesso foge do de um visitante comum. Este guia mostra onde esses desafios aparecem, como plugar a API da CaptchaAI no pipeline de coleta e quanto isso custa para diferentes volumes de catálogo.

Onde o CAPTCHA interrompe a coleta de preços

Um scraper que consulta a mesma página de hora em hora, ou a cada poucos minutos, tem um padrão de acesso bem diferente do de um comprador navegando. As principais plataformas usam isso — junto com volume de requisições, reputação de IP e sinais do navegador — para decidir quando exigir um desafio:

Plataforma Tipo de CAPTCHA Gatilho
Amazon CAPTCHA de imagem, reCAPTCHA Alto volume de requisições
Walmart Cloudflare Turnstile Detecção de bots
eBay reCAPTCHA v2 Padrões suspeitos
Best Buy Cloudflare Challenge Todo o tráfego automatizado
Lojas Shopify reCAPTCHA v3 Varia de acordo com a configuração da loja

Sem tratamento de CAPTCHA, o pipeline não quebra com um erro visível — ele simplesmente para de atualizar aquele produto específico, e a lacuna só aparece dias depois, quando alguém percebe que o preço registrado está desatualizado.

Quanto custa resolver CAPTCHA no seu volume

A CaptchaAI cobra por thread simultânea, não por CAPTCHA resolvido — cada plano inclui solves ilimitados dentro das threads contratadas. Por isso, o que define o plano certo é o volume mensal de desafios, não um preço por unidade:

Volume de monitoramento CAPTCHAs/mês (pior caso) Plano recomendado
50 produtos, a cada 30 min ~72.000 BASIC (US$ 15/mês, 5 threads)
200 produtos, a cada 15 min ~576.000 ADVANCE (US$ 90/mês, 50 threads)
1.000 produtos, checagem por hora ~720.000 ADVANCE (US$ 90/mês, 50 threads)

Esses números assumem que toda checagem aciona um CAPTCHA — o pior caso. Na prática, nem toda página carregada esbarra em um desafio, e o uso real de threads costuma ficar bem abaixo do teto do plano, com espaço de sobra para o catálogo crescer antes de precisar migrar de tier.

Arquitetura do pipeline

Na prática, o fluxo é um scheduler disparando workers em paralelo, cada um decidindo na hora se a página pede um CAPTCHA antes de extrair o preço:

Scheduler (every 30 min)
    → URL Queue
        → Scraper Workers (5-10 concurrent)
            → Fetch page
            → CAPTCHA detected?
                → Yes → CaptchaAI → Solve → Retry page
                → No → Parse prices
            → Store in database
        → Alert on price changes

Latência e LGPD para times no Brasil

Se o catálogo monitorado atende principalmente o mercado brasileiro, hospedar os workers de coleta em uma região próxima — sa-east-1 da AWS, por exemplo — reduz o RTT entre a requisição à página, o envio à CaptchaAI e o retorno do token. O ganho vem da rede da sua aplicação: o tempo de resolução do desafio em si (até 60 s para reCAPTCHA v2, normalmente bem menos) não muda com a região dos workers. Vale também revisar as obrigações da LGPD antes de ir para produção — grave apenas dados de produto (nome, preço, URL, timestamp) e evite capturar qualquer dado pessoal que apareça incidentalmente na página coletada.

Implementação

O núcleo é sempre o mesmo em qualquer stack: identificar o tipo de CAPTCHA na página, enviar a tarefa para in.php, consultar res.php até o token voltar, reenviar o formulário com o token e só então extrair o preço.

Monitor de preços em Python

A versão abaixo cobre os dois cenários mais comuns — reCAPTCHA e Turnstile — dentro de uma única função fetch_with_captcha, e grava o resultado de cada produto em prices.json, incluindo os casos de erro:

import requests
import time
import re
import json
import os
from datetime import datetime

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
BASE_URL = "https://ocr.captchaai.com"


def solve_captcha(method, params):
    params["key"] = API_KEY
    params["method"] = method

    resp = requests.get(f"{BASE_URL}/in.php", params=params)
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit failed: {resp.text}")

    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{BASE_URL}/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id,
        })
        if result.text == "CAPCHA_NOT_READY":
            continue
        if result.text.startswith("OK|"):
            return result.text.split("|", 1)[1]
        raise Exception(f"Solve failed: {result.text}")

    raise TimeoutError("CAPTCHA solve timed out")


def fetch_with_captcha(url, session):
    """Fetch a page, solving CAPTCHAs if encountered."""
    resp = session.get(url)

    # Check for reCAPTCHA
    match = re.search(r'data-sitekey=["\']([A-Za-z0-9_-]+)["\']', resp.text)
    if match:
        site_key = match.group(1)
        token = solve_captcha("userrecaptcha", {
            "googlekey": site_key,
            "pageurl": url,
        })
        resp = session.post(url, data={"g-recaptcha-response": token})

    # Check for Turnstile
    match = re.search(
        r'class="cf-turnstile"[^>]*data-sitekey=["\']([^"\']+)', resp.text
    )
    if match:
        site_key = match.group(1)
        token = solve_captcha("turnstile", {
            "sitekey": site_key,
            "pageurl": url,
        })
        resp = session.post(url, data={"cf-turnstile-response": token})

    return resp


def extract_price(html, selectors):
    """Extract price from HTML using regex patterns."""
    for pattern in selectors:
        match = re.search(pattern, html)
        if match:
            price_str = match.group(1).replace(",", "")
            return float(price_str)
    return None


def monitor_prices(products):
    """Monitor prices for a list of products."""
    session = requests.Session()
    session.headers["User-Agent"] = (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/120.0.0.0"
    )

    results = []
    for product in products:
        try:
            resp = fetch_with_captcha(product["url"], session)
            price = extract_price(resp.text, product["selectors"])

            results.append({
                "name": product["name"],
                "url": product["url"],
                "price": price,
                "timestamp": datetime.utcnow().isoformat(),
                "status": "ok",
            })
            print(f"  {product['name']}: ${price}")

        except Exception as e:
            results.append({
                "name": product["name"],
                "url": product["url"],
                "price": None,
                "timestamp": datetime.utcnow().isoformat(),
                "status": f"error: {e}",
            })
            print(f"  {product['name']}: ERROR - {e}")

    return results


# Define products to monitor
products = [
    {
        "name": "Wireless Headphones",
        "url": "https://example.com/product/headphones",
        "selectors": [
            r'class="price"[^>]*>\$?([\d,]+\.?\d*)',
            r'itemprop="price" content="([\d.]+)"',
        ],
    },
    {
        "name": "Bluetooth Speaker",
        "url": "https://example.com/product/speaker",
        "selectors": [
            r'class="price"[^>]*>\$?([\d,]+\.?\d*)',
        ],
    },
]

print("Starting price check...")
results = monitor_prices(products)

# Save results
with open("prices.json", "w") as f:
    json.dump(results, f, indent=2)

Versão em Node.js

A mesma lógica em Node.js, usando axios para as requisições HTTP e cheerio para extrair o preço do HTML retornado:

const axios = require("axios");
const cheerio = require("cheerio");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

async function solveCaptcha(method, params) {
  params.key = API_KEY;
  params.method = method;

  const submit = await axios.get("https://ocr.captchaai.com/in.php", {
    params,
  });
  const taskId = String(submit.data).split("|")[1];

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const poll = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: taskId },
    });
    const text = String(poll.data);
    if (text === "CAPCHA_NOT_READY") continue;
    if (text.startsWith("OK|")) return text.split("|").slice(1).join("|");
    throw new Error(text);
  }
  throw new Error("Timeout");
}

async function monitorPrice(url) {
  const resp = await axios.get(url);
  const $ = cheerio.load(resp.data);

  // Check for reCAPTCHA
  const siteKey = $(".g-recaptcha").attr("data-sitekey");
  if (siteKey) {
    const token = await solveCaptcha("userrecaptcha", {
      googlekey: siteKey,
      pageurl: url,
    });
    // Re-fetch with token
    const formResp = await axios.post(url, { "g-recaptcha-response": token });
    return cheerio.load(formResp.data);
  }

  const price = $('[itemprop="price"]').attr("content") || $(".price").text();
  return parseFloat(price.replace(/[^0-9.]/g, ""));
}

Como agendar as verificações

Duas formas cobrem a maioria dos casos: cron, quando o processo já roda em um servidor Linux, ou a biblioteca schedule do Python, quando o monitoramento vive dentro de um processo Python de longa duração. Comece pelo cron:

# crontab -e
*/30 * * * * cd /opt/monitor && python price_monitor.py >> /var/log/prices.log 2>&1

Ou, sem depender do cron do sistema, use a biblioteca schedule do Python direto no processo:

import schedule

schedule.every(30).minutes.do(lambda: monitor_prices(products))

while True:
    schedule.run_pending()
    time.sleep(60)

Perguntas frequentes

Quantas threads da CaptchaAI preciso para monitorar 500 produtos a cada 30 minutos?

Considerando o pior caso — todo ciclo esbarra em um CAPTCHA —, 500 produtos a cada 30 minutos chegam a cerca de 720 mil desafios por mês, faixa que cai na recomendação ADVANCE (US$ 90/mês, 50 threads). Na prática, como nem toda checagem aciona um desafio, o uso real fica bem abaixo disso — normalmente entre 5 e 20 threads simultâneas.

Como evito alertas falsos quando o preço muda por causa do frete ou de um cupom, e não do produto?

Separe o preço do produto do valor final exibido com frete ou desconto antes de comparar com o histórico, e defina um limiar mínimo de variação — por exemplo, só alertar acima de 5% — para não disparar notificação por flutuações de centavos.

Rodar os workers em uma região do Brasil reduz o tempo de resposta?

Sim, na parte sob seu controle: o RTT entre o worker, a página monitorada e a chamada à API da CaptchaAI. Hospedar os workers em sa-east-1, por exemplo, encurta esse trajeto, mas o tempo de resolução do desafio em si (até 60 s para reCAPTCHA v2, normalmente bem menos) não muda com a região.

Resolver o CAPTCHA é suficiente para não ser bloqueado?

Não sozinho. Sites de e-commerce combinam CAPTCHA com outros sinais — frequência de acesso, reputação do IP, cabeçalhos da requisição. Distribua as requisições ao longo do tempo em vez de fazer rajadas, faça rotação de proxies e use User-Agents realistas para reduzir o risco de bloqueio.

Preciso me preocupar com a LGPD para monitorar preços de concorrentes?

Coletar preços públicos de páginas de produto normalmente não envolve dados pessoais, mas isso não é aconselhamento jurídico. Grave apenas dados de produto (nome, preço, URL, timestamp), evite capturar qualquer informação de cliente que apareça incidentalmente na página e revise as obrigações da LGPD antes de rodar o pipeline em produção.

Guias relacionados

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