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-Agentde 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
- Como fazer scraping de sites protegidos sem travar no CAPTCHA
- Boas práticas de rotação de proxy
- Problemas de CAPTCHA em navegador headless