Use Cases

Tratamento de CAPTCHA para monitoramento de sites de leilões

Monitorar leilões online esbarra quase sempre no mesmo obstáculo: assim que as buscas ficam rápidas ou repetitivas demais para parecer humanas, o reCAPTCHA v2 aparece e interrompe a coleta. A resposta não é desacelerar o monitoramento até ele deixar de ser útil — é resolver o desafio automaticamente e manter o fluxo de dados rodando. Este guia mostra onde o CAPTCHA costuma aparecer em plataformas de leilão, como decidir a frequência de verificação de cada listagem e como automatizar a resolução em Python e JavaScript sem perder um lance importante.

Onde o CAPTCHA aparece ao monitorar leilões online

CAPTCHAs em sites de leilão não aparecem de forma aleatória — eles seguem o padrão de uso que mais se parece com automação: buscas em sequência, muitas requisições vindas do mesmo IP, ou navegação rápida demais entre categorias.

Ação Tipo de CAPTCHA Padrão que aciona o desafio
Buscar ou navegar em listagens reCAPTCHA v2 Buscas sequenciais em alta velocidade
Abrir detalhes de uma listagem reCAPTCHA v2 Volume alto vindo do mesmo IP
Consultar histórico de lances reCAPTCHA v2 Recarregamentos repetidos da página de detalhes
Navegar por categorias Cloudflare Turnstile Velocidade de navegação típica de bot
Páginas de alerta de preço reCAPTCHA v2 Atualizações frequentes

Com que frequência verificar cada leilão

A frequência de verificação certa depende do que você está acompanhando — um lance de última hora exige checagens quase em tempo real, enquanto uma lista de observação tolera intervalos bem maiores.

Frequência de verificação Caso de uso Taxa esperada de CAPTCHA
A cada 30 segundos Lance nos minutos finais do leilão Alta — mantenha sessão autenticada e proxies prontos
A cada 5 minutos Acompanhamento de leilão em andamento Moderada
A cada 15 minutos Lista de observação (watchlist) Baixa
A cada hora Pesquisa de preços de longo prazo Mínima

Se o worker roda fora do Brasil, a latência de rede já consome parte da sua janela de 30 segundos antes mesmo do CAPTCHA aparecer. Hospedar o monitor em uma região como AWS sa-east-1 (São Paulo) reduz o tempo de ida e volta até a maioria dos sites de leilão voltados ao público brasileiro e deixa mais margem para a resolução em si — o reCAPTCHA v2 normalmente resolve em menos de 60 segundos, com alta taxa de sucesso nos tipos suportados.

Como reduzir a frequência de CAPTCHA no monitoramento

Antes de otimizar a resolução em si, vale reduzir quantas vezes o CAPTCHA chega a aparecer:

Técnica Impacto
Reaproveitar cookies de sessão Mantém o estado autenticado entre requisições
Fazer rotação de proxies Distribui as requisições entre IPs diferentes
Randomizar os intervalos entre requisições Evita padrões de acesso previsíveis
Usar contas autenticadas Reduz a frequência com que o CAPTCHA é acionado

Ao armazenar históricos de lances e nomes de usuário coletados durante o monitoramento, vale considerar as obrigações da LGPD sobre retenção e finalidade dos dados — mesmo quando a informação já é pública na listagem, guardar esse histórico por tempo indefinido pode extrapolar o propósito original da coleta.

Monitorando leilões em Python: resolução automática de reCAPTCHA v2

A classe abaixo cobre o fluxo completo: busca paginada, checagem de uma listagem específica e acompanhamento contínuo de lances. O solver só é acionado quando _has_captcha() encontra o desafio na resposta — nenhuma tarefa é enviada à toa.

import requests
import time
import re
from datetime import datetime

class AuctionMonitor:
    def __init__(self, api_key):
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })

    def search_listings(self, auction_url, query, category=None):
        """Search auction listings, solving CAPTCHAs when triggered."""
        params = {"q": query}
        if category:
            params["category"] = category

        response = self.session.get(
            f"{auction_url}/search", params=params
        )

        if self._has_captcha(response.text):
            site_key = self._extract_site_key(response.text)
            token = self._solve_recaptcha(site_key, f"{auction_url}/search")
            response = self.session.post(
                f"{auction_url}/search",
                data={**params, "g-recaptcha-response": token}
            )

        return self._parse_listings(response.text)

    def monitor_listing(self, auction_url, listing_id):
        """Get current bid and listing details."""
        url = f"{auction_url}/item/{listing_id}"
        response = self.session.get(url)

        if self._has_captcha(response.text):
            site_key = self._extract_site_key(response.text)
            token = self._solve_recaptcha(site_key, url)
            response = self.session.post(url, data={
                "g-recaptcha-response": token
            })

        return self._parse_listing_detail(response.text)

    def track_bids(self, auction_url, listing_ids, interval=60):
        """Track bid changes across multiple listings."""
        history = {lid: [] for lid in listing_ids}

        while True:
            for listing_id in listing_ids:
                try:
                    detail = self.monitor_listing(auction_url, listing_id)
                    previous = history[listing_id]

                    if previous and detail["current_bid"] != previous[-1]["current_bid"]:
                        print(f"Bid change on {listing_id}: "
                              f"${previous[-1]['current_bid']} → ${detail['current_bid']}")

                    history[listing_id].append(detail)
                except Exception as e:
                    print(f"Error checking {listing_id}: {e}")

            time.sleep(interval)

    def _has_captcha(self, html):
        return "g-recaptcha" in html or "recaptcha" in html.lower()

    def _extract_site_key(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            return match.group(1)
        match = re.search(r"sitekey['\"]?\s*[:=]\s*['\"]([^'\"]+)", html)
        if match:
            return match.group(1)
        raise ValueError("Could not find reCAPTCHA site key")

    def _solve_recaptcha(self, site_key, page_url):
        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "json": 1
        })
        task_id = resp.json()["request"]

        for _ in range(60):
            time.sleep(3)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1
            })
            data = result.json()
            if data["status"] == 1:
                return data["request"]

        raise TimeoutError("reCAPTCHA solve timed out")

    def _parse_listings(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        def attr_or_none(node, attr):
            return node.get(attr) if node else None

        listings = []
        for item in soup.select(".listing-item, .auction-item"):
            listings.append({
                "title": text_or_none(item.select_one(".title")),
                "current_bid": text_or_none(item.select_one(".price, .bid")),
                "time_left": text_or_none(item.select_one(".time-left")),
                "url": attr_or_none(item.select_one("a"), "href")
            })
        return listings

    def _parse_listing_detail(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        return {
            "title": text_or_none(soup.select_one("h1, .item-title")),
            "current_bid": text_or_none(soup.select_one(".current-bid, .price")),
            "bid_count": text_or_none(soup.select_one(".bid-count")),
            "time_left": text_or_none(soup.select_one(".time-remaining")),
            "checked_at": datetime.now().isoformat()
        }

# Usage
monitor = AuctionMonitor("YOUR_API_KEY")
listings = monitor.search_listings(
    "https://auctions.example.com",
    "vintage electronics",
    category="collectibles"
)

Alertas de preço em JavaScript: acompanhando lances automaticamente

Para quem prefere Node.js ou roda o monitor direto no navegador, esta versão em JavaScript acompanha uma lista de leilões e dispara um alerta quando o lance atual se aproxima do teto que você definiu.

class AuctionTracker {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.watchList = new Map();
  }

  addWatch(listingId, url, maxPrice) {
    this.watchList.set(listingId, { url, maxPrice, history: [] });
  }

  async checkAll() {
    const alerts = [];

    for (const [id, watch] of this.watchList) {
      try {
        const detail = await this.fetchListing(watch.url);
        watch.history.push(detail);

        const price = parseFloat(detail.currentBid.replace(/[^0-9.]/g, ''));
        if (price >= watch.maxPrice * 0.9) {
          alerts.push({
            listing: id,
            price,
            threshold: watch.maxPrice,
            message: `Price approaching limit: $${price} / $${watch.maxPrice}`
          });
        }
      } catch (error) {
        alerts.push({ listing: id, error: error.message });
      }
    }

    return alerts;
  }

  async fetchListing(url) {
    const response = await fetch(url);
    const html = await response.text();

    if (html.includes('g-recaptcha')) {
      return this.solveAndFetch(url, html);
    }

    return this.parseDetail(html);
  }

  async solveAndFetch(url, html) {
    const siteKeyMatch = html.match(/data-sitekey="([^"]+)"/);
    if (!siteKeyMatch) throw new Error('No reCAPTCHA site key found');

    const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
      method: 'POST',
      body: new URLSearchParams({
        key: this.apiKey,
        method: 'userrecaptcha',
        googlekey: siteKeyMatch[1],
        pageurl: url,
        json: '1'
      })
    });

    const { request: taskId } = await submitResp.json();

    for (let i = 0; i < 60; i++) {
      await new Promise(r => setTimeout(r, 3000));
      const result = await fetch(
        `https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
      );
      const data = await result.json();
      if (data.status === 1) {
        // Resubmit with token
        const response = await fetch(url, {
          method: 'POST',
          body: new URLSearchParams({ 'g-recaptcha-response': data.request })
        });
        return this.parseDetail(await response.text());
      }
    }

    throw new Error('reCAPTCHA solve timed out');
  }

  parseDetail(html) {
    // Parse auction listing details from HTML
    return {
      currentBid: html.match(/current.?bid[^>]*>([^<]+)/i)?.[1]?.trim(),
      bidCount: html.match(/(\d+)\s*bids?/i)?.[1],
      timeLeft: html.match(/time.?(?:left|remaining)[^>]*>([^<]+)/i)?.[1]?.trim(),
      checkedAt: new Date().toISOString()
    };
  }
}

// Usage
const tracker = new AuctionTracker('YOUR_API_KEY');
tracker.addWatch('item-123', 'https://auctions.example.com/item/123', 500);
tracker.addWatch('item-456', 'https://auctions.example.com/item/456', 200);
const alerts = await tracker.checkAll();

Solução de problemas

Problema Causa Correção
CAPTCHA em toda requisição Sem persistência de sessão Reaproveite requests.Session() entre as chamadas
Token reCAPTCHA rejeitado O token expirou (validade de 2 minutos) Resolva o CAPTCHA imediatamente antes do envio
A página de listagens retorna 0 resultados O CAPTCHA filtrou o resultado silenciosamente Verifique se há elementos de CAPTCHA ocultos na resposta
IP bloqueado após vários CAPTCHAs Limite de requisições excedido Faça rotação de proxies e aumente os intervalos

Perguntas frequentes

Quantos threads preciso para monitorar vários leilões em paralelo?

Cada thread do seu plano CaptchaAI processa um CAPTCHA por vez; quando o solve termina, a thread fica livre para a próxima tarefa. O plano BASIC (US$ 15/mês, 5 threads) já cobre o monitoramento de algumas listagens com verificações espaçadas. Para acompanhar dezenas de leilões em paralelo com checagens a cada poucos segundos, o STANDARD (US$ 30/mês, 15 threads) ou o ADVANCE (US$ 90/mês, 50 threads) dão mais margem sem formar fila.

O monitoramento automatizado de leilões é detectável pelos sites?

A detecção depende do padrão das requisições, não da resolução do CAPTCHA em si. Use intervalos realistas entre as chamadas, alterne user agents e evite concentrar toda a atividade nos horários de menor tráfego, quando o seu volume se destaca no meio do silêncio.

Preciso resolver o CAPTCHA antes de cada requisição ou só quando ele aparece?

Só quando ele aparece. O padrão recomendado é enviar a requisição normalmente e resolver o CAPTCHA apenas se a resposta contiver o desafio — é o que o método _has_captcha() do exemplo em Python faz. Resolver de forma preventiva a cada chamada gasta threads sem necessidade.

O CaptchaAI resolve outros tipos de CAPTCHA além do reCAPTCHA v2 em sites de leilão?

Sim. A navegação por categorias em alguns sites de leilão aciona o Cloudflare Turnstile em vez do reCAPTCHA v2, e a CaptchaAI resolve os dois tipos com a mesma chave de API — só muda o parâmetro method na chamada a in.php.

Artigos relacionados

Próximas etapas

Monitore leilões de forma confiável: crie sua chave de API da CaptchaAI e comece a resolver os desafios de reCAPTCHA v2 automaticamente.

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