Use Cases

Fluxos de trabalho de verificação de anúncios com tratamento CAPTCHA

Antes de aprovar a veiculação, o time de ad ops confirma se o anúncio apareceu no lugar certo, sem ficar perto de conteúdo impróprio — em milhares de páginas de editores. O obstáculo mais comum não é técnico, é o CAPTCHA: editores protegidos por Cloudflare bloqueiam a visita antes que ela comece. Este guia mostra como manter a verificação rodando com a API da CaptchaAI.

Onde o CAPTCHA interrompe a verificação de anúncios

Cada tipo de checagem dispara um sinal diferente de automação para o editor:

Verificação O que é checado Por que aciona CAPTCHA
Posicionamento O anúncio aparece acima da dobra? Visitas automatizadas repetidas
Brand safety Nenhum anúncio perto de conteúdo impróprio Checagem de URLs em massa lembra scraping
Viewability O anúncio esteve realmente visível? Navegador headless sinalizado pela Cloudflare
Segmentação geográfica Anúncio certo na região certa? Tráfego via proxy aciona CAPTCHA
Monitoramento de concorrentes Quais anúncios eles estão veiculando? Alto volume de consultas no mesmo domínio

Quando a checagem trava no CAPTCHA, o efeito não é só um erro de log: o time de ad ops perde visibilidade justamente nas páginas mais sensíveis — as que os anunciantes mais cobram sobre brand safety. Editores de notícias e finanças, que concentram tráfego premium, também são os que mais reforçam Cloudflare, então o gargalo tende a aparecer exatamente onde a verificação é mais crítica.

Como implementar a verificação com a API da CaptchaAI

O fluxo é sempre o mesmo: abrir a página, detectar o CAPTCHA, resolver via API da CaptchaAI e só então procurar as tags de anúncio no HTML. Repetir esse ciclo por milhares de URLs manualmente não escala — a automação existe justamente para transformar uma checagem pontual em um pipeline que roda em lote, com log e retomada em caso de falha. O exemplo abaixo cobre reCAPTCHA v2, grava um relatório por URL em JSON e resume o total de páginas seguras ao final:

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

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


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

    resp = requests.get("https://ocr.captchaai.com/in.php", params=params)
    if not resp.text.startswith("OK|"):
        raise Exception(resp.text)

    task_id = resp.text.split("|")[1]
    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/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(result.text)
    raise TimeoutError()


def verify_ad_placement(url, session):
    """Verify ad placement on a publisher page."""
    resp = session.get(url)

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

    html = resp.text

    # Check for ad elements
    result = {
        "url": url,
        "timestamp": datetime.utcnow().isoformat(),
        "ads_found": [],
        "brand_safety": True,
        "captcha_solved": match is not None,
    }

    # Detect ad tags
    ad_patterns = [
        (r'googletag\.pubads', "Google Ad Manager"),
        (r'doubleclick\.net', "DFP/DoubleClick"),
        (r'ad\.doubleclick', "DoubleClick"),
        (r'amazon-adsystem', "Amazon Ads"),
        (r'criteo\.com/.*\.js', "Criteo"),
    ]

    for pattern, name in ad_patterns:
        if re.search(pattern, html):
            result["ads_found"].append(name)

    # Brand safety check — flag problematic content
    safety_keywords = [
        "violence", "hate speech", "explicit",
        "gambling", "illegal",
    ]
    page_text = re.sub(r'<[^>]+>', '', html).lower()
    for keyword in safety_keywords:
        if keyword in page_text:
            result["brand_safety"] = False
            break

    return result


def run_verification(urls, output_file="verification_report.json"):
    """Run ad verification across multiple publisher URLs."""
    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 i, url in enumerate(urls):
        try:
            result = verify_ad_placement(url, session)
            results.append(result)
            ads = ", ".join(result["ads_found"]) or "None"
            safe = "SAFE" if result["brand_safety"] else "UNSAFE"
            print(f"  [{i+1}/{len(urls)}] {url}: {ads} [{safe}]")
        except Exception as e:
            results.append({
                "url": url,
                "error": str(e),
                "timestamp": datetime.utcnow().isoformat(),
            })
            print(f"  [{i+1}/{len(urls)}] {url}: ERROR - {e}")

        time.sleep(2)

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

    # Summary
    total = len(results)
    safe = sum(1 for r in results if r.get("brand_safety"))
    captchas = sum(1 for r in results if r.get("captcha_solved"))
    errors = sum(1 for r in results if "error" in r)

    print(f"\n  Total: {total} | Safe: {safe} | CAPTCHAs solved: {captchas} | Errors: {errors}")

    return results


# Publisher URLs to verify
publisher_urls = [
    "https://publisher1.com/article/tech-news",
    "https://publisher2.com/sports/latest",
    "https://publisher3.com/finance/markets",
]

run_verification(publisher_urls)

Isso cobre anúncios gráficos e nativos; vídeo exige renderização real com Selenium ou Playwright, porque o player só injeta as tags de rastreamento depois que o script carrega no DOM — checagem estática de HTML não é suficiente para esse formato. Para editores voltados ao Brasil, workers na região sa-east-1 da AWS reduzem o tempo de rede até a CaptchaAI e evitam que a latência transatlântica infle o tempo total de cada verificação. Ao guardar HTML para auditoria de brand safety, revise as obrigações da LGPD/RGPD antes de definir a política de retenção: registre só o necessário para provar o posicionamento do anúncio, evite reter dados pessoais que porventura apareçam na página capturada e documente por quanto tempo o relatório fica armazenado.

Escalando a verificação em editores com Cloudflare Turnstile

Editores premium costumam rodar atrás da Cloudflare, e o sinal muda conforme a agressividade da proteção: o widget do Turnstile aparece embutido no formulário da página, enquanto o Cloudflare Challenge some antes mesmo do HTML carregar, quando a requisição já chega suspeita para o edge da Cloudflare. Distinguir os dois casos importa porque o segundo exige repetir a requisição depois de resolver, e não apenas enviar um token junto do formulário:

def handle_cloudflare(url, session):
    """Handle Cloudflare-protected publisher pages."""
    resp = session.get(url)

    if "cf-turnstile" in resp.text:
        match = re.search(r'data-sitekey=["\']([^"\']+)', resp.text)
        if match:
            token = solve_captcha("turnstile", {
                "sitekey": match.group(1),
                "pageurl": url,
            })
            return session.post(url, data={
                "cf-turnstile-response": token,
            })

    if resp.status_code == 403 and "cf-browser-verification" in resp.text:
        data = solve_captcha("turnstile_staging", {
            "pageurl": url,
            "proxy": "user:pass@proxy:port",
            "proxytype": "HTTP",
        })
        # Parse cookie_qa_validacao and use same proxy
        return data

    return resp

Para checar uma região específica, use um proxy dessa geografia — a API aceita proxy e proxytype. Sem esse ajuste, a segmentação geográfica do anúncio nunca bate com a origem real da requisição, e o editor pode servir o placement errado para a verificação sem que isso apareça como erro.

Boas práticas para escalar sem travar no editor

Resolver o CAPTCHA é só metade do problema — manter o pipeline saudável em milhares de páginas por dia exige alguns cuidados operacionais:

  • Espace as requisições por domínio. Rajadas contra o mesmo editor aumentam a chance de checagens adicionais, mesmo depois do CAPTCHA resolvido; o time.sleep(2) do exemplo acima é o mínimo, não o ideal para editores mais sensíveis.
  • Registre o resultado de cada tentativa. Guardar captcha_solved, tempo de resolução e código de status por URL facilita identificar qual editor mudou de proteção antes que o relatório de brand safety fique incompleto.
  • Retome de onde parou. Em lotes de milhares de URLs, uma falha de rede não deve reiniciar a checagem inteira — persista o progresso e reprocesse só as URLs pendentes.
  • Combine proxy e user-agent coerentes. Um proxy de uma região com um cabeçalho User-Agent de outra é um sinal de inconsistência que os editores mais protegidos também observam.

Perguntas frequentes

Que tipos de CAPTCHA aparecem com mais frequência na verificação de anúncios?

reCAPTCHA v2 em formulários de acesso, Cloudflare Turnstile no carregamento da página e, em editores mais agressivos, o Cloudflare Challenge completo antes mesmo do HTML aparecer. A proporção varia por vertical: editores de notícias e finanças tendem a reforçar mais a proteção do que blogs menores.

O CaptchaAI não resolve hCaptcha nos editores que usam esse widget, certo?

Correto. O hCaptcha não está entre os tipos suportados hoje — se um editor específico depende dele, essa página fica fora da automação até a CaptchaAI anunciar suporte. A cobertura atual inclui reCAPTCHA v2/v3, Cloudflare Turnstile e Cloudflare Challenge, GeeTest v3 e CAPTCHAs de imagem/OCR, que já respondem pela maioria dos editores premium monitorados.

Quantas páginas dá para verificar por hora?

Entre 200 e 500 por worker, dependendo da frequência dos CAPTCHAs e do tempo de resolução de cada tipo. Rodar mais workers em paralelo aumenta o throughput, mas respeite o limite de requisições simultâneas contra o mesmo domínio para não acionar checagens extras.

Resolver o CAPTCHA garante que eu não seja bloqueado no próximo acesso?

Sozinho, não. Distribua as chamadas ao longo do tempo, evite rajadas de requisições no mesmo editor e trate o CAPTCHA resolvido como parte de um fluxo mais amplo de comportamento consistente, não como uma solução isolada.

Guias relacionados

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