Integração do Katana com o CapSolver: Resolução Automática de CAPTCHA para Web Crawling

Adélia Cruz
Neural Network Developer
12-Jan-2026
Por que resolver CAPTCHA com Katana usando CapSolver
A raspagem de web é uma técnica essencial para pesquisadores de segurança, testadores de penetração e analistas de dados. No entanto, sites modernos empregam cada vez mais CAPTCHAs para proteger contra acesso automatizado. Este guia demonstra como integrar o Katana, o poderoso framework de raspagem da ProjectDiscovery, com o CapSolver, um serviço líder de resolução de CAPTCHA, para criar uma solução de raspagem robusta que resolva automaticamente os desafios de CAPTCHA.
O que você aprenderá
- Configurar o Katana no modo navegador sem cabeça
- Integrar a API do Capsolver para resolução automática de CAPTCHA
- Lidar com reCAPTCHA v2 e Cloudflare Turnstile
- Exemplos completos de código para cada tipo de CAPTCHA
- Boas práticas para raspagem eficiente e responsável
O que é Katana?
Katana é um framework de raspagem web de próxima geração desenvolvido pela ProjectDiscovery. Foi projetado para velocidade e flexibilidade, sendo ideal para reconhecimento de segurança e pipelines de automação.
Principais Funcionalidades
- Dois Modos de Raspagem: Raspagem baseada em HTTP e automação de navegador sem cabeça
- Suporte a JavaScript: Analisar e raspar conteúdo renderizado por JavaScript
- Configuração Flexível: Cabeçalhos personalizados, cookies, preenchimento de formulários e controle de escopo
- Vários Formatos de Saída: Texto simples, JSON ou JSONL
Instalação
bash
# Requer Go 1.24+
CGO_ENABLED=1 go install github.com/projectdiscovery/katana/cmd/katana@latest
Uso Básico
bash
katana -u https://example.com -headless
O que é Capsolver?
CapSolver é um serviço de resolução de CAPTCHA baseado em inteligência artificial que fornece soluções rápidas e confiáveis para vários tipos de CAPTCHA.
Tipos de CAPTCHA Suportados
- reCAPTCHA: v2 e versões Enterprise
- Cloudflare: Turnstile e Challenge
- AWS WAF: Bypass da proteção WAF
- E Mais
Fluxo de Trabalho da API
O CapSolver usa um modelo de API baseado em tarefas:
- Criar Tarefa: Enviar parâmetros de CAPTCHA (tipo, siteKey, URL)
- Obter ID da Tarefa: Receber um identificador único da tarefa
- Verificar Resultado: Verificar o status da tarefa até que a solução esteja pronta
- Receber Token: Obter o token de CAPTCHA resolvido
Pré-requisitos
Antes de começar, certifique-se de ter:
- Go 1.24+ instalado
- Chave de API do Capsolver - Registre-se aqui
- Navegador Chrome (para modo sem cabeça)
Defina sua chave de API como uma variável de ambiente:
bash
export CAPSOLVER_API_KEY="SUA_CHAVE_DE_API"
Arquitetura de Integração
┌─────────────────────────┐
│ Aplicação Go │
│ (navegador go-rod) │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Website Alvo │
│ (com CAPTCHA) │
└───────────┬─────────────┘
│
CAPTCHA Detectado
│
▼
┌─────────────────────────┐
│ Extrair Parâmetros │
│ (siteKey, URL, tipo) │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ API do Capsolver │
│ createTask() │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Verificar Resultado │
│ getTaskResult() │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Injetar Token │
│ na Página │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Continuar Raspagem │
└─────────────────────────┘
Resolvendo reCAPTCHA v2 com o CapSolver
O reCAPTCHA v2 é o tipo de CAPTCHA mais comum, mostrando um checkbox "I'm not a robot" ou desafios de imagem. Aqui está um script completo e executável para resolver o reCAPTCHA v2:
go
// Solucionador de reCAPTCHA v2 - Exemplo Completo
// Uso: go run main.go
// Requer: variável de ambiente CAPSOLVER_API_KEY
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
)
// Configuração
var (
CAPSOLVER_API_KEY = os.Getenv("CAPSOLVER_API_KEY")
CAPSOLVER_API = "https://api.capsolver.com"
)
// Estruturas de resposta da API
type CreateTaskResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
TaskID string `json:"taskId"`
}
type GetTaskResultResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
Status string `json:"status"`
Solution struct {
GRecaptchaResponse string `json:"gRecaptchaResponse"`
} `json:"solution"`
}
type BalanceResponse struct {
ErrorID int `json:"errorId"`
Balance float64 `json:"balance"`
}
// CapsolverClient lida com a comunicação da API
type CapsolverClient struct {
APIKey string
Client *http.Client
}
// NewCapsolverClient cria um novo cliente do Capsolver
func NewCapsolverClient(apiKey string) *CapsolverClient {
return &CapsolverClient{
APIKey: apiKey,
Client: &http.Client{Timeout: 120 * time.Second},
}
}
// GetBalance recupera o saldo da conta
func (c *CapsolverClient) GetBalance() (float64, error) {
payload := map[string]string{"clientKey": c.APIKey}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getBalance", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result BalanceResponse
json.Unmarshal(body, &result)
if result.ErrorID != 0 {
return 0, fmt.Errorf("verificação de saldo falhou")
}
return result.Balance, nil
}
// SolveRecaptchaV2 soluciona um desafio de reCAPTCHA v2
func (c *CapsolverClient) SolveRecaptchaV2(websiteURL, siteKey string) (string, error) {
log.Printf("Criando tarefa de reCAPTCHA v2 para %s", websiteURL)
// Criar tarefa
task := map[string]interface{}{
"type": "ReCaptchaV2TaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("falha ao criar tarefa: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("erro da API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Tarefa criada: %s", createResult.TaskID)
// Verificar resultado
for i := 0; i < 120; i++ {
result, err := c.getTaskResult(createResult.TaskID)
if err != nil {
return "", err
}
if result.Status == "ready" {
log.Printf("CAPTCHA resolvido com sucesso!")
return result.Solution.GRecaptchaResponse, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("tarefa falhou: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Aguardando solução... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("tempo esgotado esperando solução")
}
func (c *CapsolverClient) getTaskResult(taskID string) (*GetTaskResultResponse, error) {
payload := map[string]string{
"clientKey": c.APIKey,
"taskId": taskID,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result GetTaskResultResponse
json.Unmarshal(body, &result)
return &result, nil
}
// extractSiteKey extrai a chave do site do reCAPTCHA da HTML da página
func extractSiteKey(html string) string {
// Procurar atributo data-sitekey
patterns := []string{
`data-sitekey="`,
`data-sitekey='`,
`"sitekey":"`,
`'sitekey':'`,
}
for _, pattern := range patterns {
if idx := strings.Index(html, pattern); idx != -1 {
start := idx + len(pattern)
end := start
for end < len(html) && html[end] != '"' && html[end] != '\'' {
end++
}
if end > start {
return html[start:end]
}
}
}
return ""
}
// injectRecaptchaToken injeta o token resolvido na página
func injectRecaptchaToken(page *rod.Page, token string) error {
js := fmt.Sprintf(`
(function() {
// Definir o campo de resposta
var responseField = document.getElementById('g-recaptcha-response');
if (responseField) {
responseField.style.display = 'block';
responseField.value = '%s';
}
// Também definir textareas ocultas
var textareas = document.querySelectorAll('textarea[name="g-recaptcha-response"]');
for (var i = 0; i < textareas.length; i++) {
textareas[i].value = '%s';
}
// Disparar callback se existir
if (typeof ___grecaptcha_cfg !== 'undefined') {
var clients = ___grecaptcha_cfg.clients;
for (var key in clients) {
var client = clients[key];
if (client) {
// Tentar encontrar e chamar o callback
try {
var callback = client.callback ||
(client.Q && client.Q.callback) ||
(client.S && client.S.callback);
if (typeof callback === 'function') {
callback('%s');
}
} catch(e) {}
}
}
}
return true;
})();
`, token, token, token)
_, err := page.Eval(js)
return err
}
func main() {
// Verificar chave de API
if CAPSOLVER_API_KEY == "" {
log.Fatal("A variável de ambiente CAPSOLVER_API_KEY é necessária")
}
// URL alvo - página de demonstração do reCAPTCHA da Google
targetURL := "https://www.google.com/recaptcha/api2/demo"
log.Println("==============================================")
log.Println("Katana + Capsolver - Demonstração de reCAPTCHA v2")
log.Println("==============================================")
// Inicializar cliente do Capsolver
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Verificar saldo
balance, err := client.GetBalance()
if err != nil {
log.Printf("Aviso: Não foi possível verificar o saldo: %v", err)
} else {
log.Printf("Saldo do Capsolver: $%.2f", balance)
}
// Iniciar navegador
log.Println("Iniciando navegador...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(true).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Navegar até o alvo
log.Printf("Navegando até: %s", targetURL)
page := browser.MustPage(targetURL)
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Obter HTML da página e extrair a chave do site
html := page.MustHTML()
// Verificar reCAPTCHA
if !strings.Contains(html, "g-recaptcha") && !strings.Contains(html, "grecaptcha") {
log.Fatal("Nenhum reCAPTCHA encontrado na página")
}
log.Println("reCAPTCHA detectado!")
// Extrair chave do site
siteKey := extractSiteKey(html)
if siteKey == "" {
log.Fatal("Não foi possível extrair a chave do site")
}
log.Printf("Chave do site: %s", siteKey)
// Resolver CAPTCHA
log.Println("Resolvendo CAPTCHA com o Capsolver...")
token, err := client.SolveRecaptchaV2(targetURL, siteKey)
if err != nil {
log.Fatalf("Falha ao resolver CAPTCHA: %v", err)
}
log.Printf("Token recebido: %s...", token[:50])
// Injetar token
log.Println("Injetando token na página...")
err = injectRecaptchaToken(page, token)
if err != nil {
log.Fatalf("Falha ao injetar token: %v", err)
}
// Submeter formulário
log.Println("Submetendo formulário...")
submitBtn := page.MustElement("#recaptcha-demo-submit")
submitBtn.MustClick()
// Aguardar resultado
time.Sleep(3 * time.Second)
// Verificar resultado
newHTML := page.MustHTML()
if strings.Contains(newHTML, "Verificação bem-sucedida") || strings.Contains(newHTML, "success") {
log.Println("==============================================")
log.Println("SUCESSO! reCAPTCHA resolvido e verificado!")
log.Println("==============================================")
} else {
log.Println("Formulário submetido - verifique a página para o resultado")
}
// Obter título da página
title := page.MustEval(`document.title`).String()
log.Printf("Título da página final: %s", title)
}
Configuração e Execução
bash
# Criar projeto
mkdir katana-recaptcha-v2
cd katana-recaptcha-v2
go mod init katana-recaptcha-v2
# Instalar dependências
go get github.com/go-rod/rod@latest
# Definir chave de API
export CAPSOLVER_API_KEY="SUA_CHAVE_DE_API"
# Executar
go run main.go
Resolvendo Cloudflare Turnstile com o CapSolver
O Cloudflare Turnstile é uma alternativa de CAPTCHA focada na privacidade. Aqui está um script completo:
go
// Solucionador de Cloudflare Turnstile - Exemplo Completo
// Uso: go run main.go
// Requer: variável de ambiente CAPSOLVER_API_KEY
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"regexp"
"strings"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
)
// Configuração
var (
CAPSOLVER_API_KEY = os.Getenv("CAPSOLVER_API_KEY")
CAPSOLVER_API = "https://api.capsolver.com"
)
// Estruturas de resposta da API
type CreateTaskResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
TaskID string `json:"taskId"`
}
type GetTaskResultResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
Status string `json:"status"`
Solution struct {
Token string `json:"token"`
} `json:"solution"`
}
type BalanceResponse struct {
ErrorID int `json:"errorId"`
Balance float64 `json:"balance"`
}
// CapsolverClient lida com a comunicação da API
type CapsolverClient struct {
APIKey string
Client *http.Client
}
// NewCapsolverClient cria um novo cliente do Capsolver
func NewCapsolverClient(apiKey string) *CapsolverClient {
return &CapsolverClient{
APIKey: apiKey,
Client: &http.Client{Timeout: 120 * time.Second},
}
}
// GetBalance recupera o saldo da conta
func (c *CapsolverClient) GetBalance() (float64, error) {
payload := map[string]string{"clientKey": c.APIKey}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getBalance", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result BalanceResponse
json.Unmarshal(body, &result)
return result.Balance, nil
}
// SolveTurnstile soluciona um desafio de Cloudflare Turnstile
func (c *CapsolverClient) SolveTurnstile(websiteURL, siteKey string) (string, error) {
log.Printf("Criando tarefa de Turnstile para %s", websiteURL)
// Criar tarefa
task := map[string]interface{}{
"type": "AntiTurnstileTaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("falha ao criar tarefa: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("erro da API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Tarefa criada: %s", createResult.TaskID)
// Verificar resultado
for i := 0; i < 120; i++ {
result, err := c.getTaskResult(createResult.TaskID)
if err != nil {
return "", err
}
if result.Status == "ready" {
log.Printf("CAPTCHA resolvido com sucesso!")
return result.Solution.Token, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("tarefa falhou: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Aguardando solução... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("tempo esgotado esperando solução")
}
func (c *CapsolverClient) getTaskResult(taskID string) (*GetTaskResultResponse, error) {
payload := map[string]string{
"clientKey": c.APIKey,
"taskId": taskID,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result GetTaskResultResponse
json.Unmarshal(body, &result)
return &result, nil
}
// extractSiteKey extrai a chave do site do Cloudflare Turnstile da HTML da página
func extractSiteKey(html string) string {
// Procurar por atributo data-sitekey
patterns := []string{
`data-sitekey="`,
`data-sitekey='`,
`"sitekey":"`,
`'sitekey':'`,
}
for _, pattern := range patterns {
if idx := strings.Index(html, pattern); idx != -1 {
start := idx + len(pattern)
end := start
for end < len(html) && html[end] != '"' && html[end] != '\'' {
end++
}
if end > start {
return html[start:end]
}
}
}
return ""
}
// injectTurnstileToken injeta o token resolvido na página
func injectTurnstileToken(page *rod.Page, token string) error {
js := fmt.Sprintf(`
(function() {
// Definir o token
var tokenField = document.querySelector('input[name="cf-turnstile-response"]');
if (tokenField) {
tokenField.value = '%s';
}
// Ativar o campo de token
var tokenInput = document.querySelector('input[name="cf-turnstile-response"]');
if (tokenInput) {
tokenInput.setAttribute('data-solution', '%s');
tokenInput.dispatchEvent(new Event('input', { bubbles: true }));
}
// Verificar se o token foi injetado
if (document.querySelector('input[name="cf-turnstile-response"]')) {
return true;
} else {
return false;
}
})();
`, token, token)
_, err := page.Eval(js)
return err
}
func main() {
// Verificar chave de API
if CAPSOLVER_API_KEY == "" {
log.Fatal("A variável de ambiente CAPSOLVER_API_KEY é necessária")
}
// URL alvo - página de demonstração do Cloudflare Turnstile
targetURL := "https://cf-turnstile-demo.glitch.me/"
log.Println("==============================================")
log.Println("Katana + Capsolver - Demonstração de Cloudflare Turnstile")
log.Println("==============================================")
// Inicializar cliente do Capsolver
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Verificar saldo
balance, err := client.GetBalance()
if err != nil {
log.Printf("Aviso: Não foi possível verificar o saldo: %v", err)
} else {
log.Printf("Saldo do Capsolver: $%.2f", balance)
}
// Iniciar navegador
log.Println("Iniciando navegador...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(true).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Navegar até o alvo
log.Printf("Navegando até: %s", targetURL)
page := browser.MustPage(targetURL)
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Obter HTML da página e extrair a chave do site
html := page.MustHTML()
// Verificar Cloudflare Turnstile
if !strings.Contains(html, "cf-turnstile") {
log.Fatal("Nenhum Cloudflare Turnstile encontrado na página")
}
log.Println("Cloudflare Turnstile detectado!")
// Extrair chave do site
siteKey := extractSiteKey(html)
if siteKey == "" {
log.Fatal("Não foi possível extrair a chave do site")
}
log.Printf("Chave do site: %s", siteKey)
// Resolver CAPTCHA
log.Println("Resolvendo CAPTCHA com o Capsolver...")
token, err := client.SolveTurnstile(targetURL, siteKey)
if err != nil {
log.Fatalf("Falha ao resolver CAPTCHA: %v", err)
}
log.Printf("Token recebido: %s...", token[:50])
// Injetar token
log.Println("Injetando token na página...")
err = injectTurnstileToken(page, token)
if err != nil {
log.Fatalf("Falha ao injetar token: %v", err)
}
// Submeter formulário
log.Println("Submetendo formulário...")
submitBtn := page.MustElement("button[type='submit']")
submitBtn.MustClick()
// Aguardar resultado
time.Sleep(3 * time.Second)
// Verificar resultado
newHTML := page.MustHTML()
if strings.Contains(newHTML, "success") || strings.Contains(newHTML, "Verification Success") {
log.Println("==============================================")
log.Println("SUCESSO! Cloudflare Turnstile resolvido e verificado!")
log.Println("==============================================")
} else {
log.Println("Formulário submetido - verifique a página para o resultado")
}
// Obter título da página
title := page.MustEval(`document.title`).String()
log.Printf("Título da página final: %s", title)
}
"websiteKey": siteKey,
}
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("falha ao criar tarefa: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("erro da API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Tarefa criada: %s", createResult.TaskID)
// Verificar resultado
for i := 0; i < 120; i++ {
result, err := c.getTaskResult(createResult.TaskID)
if err != nil {
return "", err
}
if result.Status == "ready" {
log.Printf("Turnstile resolvido com sucesso!")
return result.Solution.Token, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("tarefa falhou: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Aguardando solução... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("tempo esgotado ao aguardar solução")
}
func (c *CapsolverClient) getTaskResult(taskID string) (*GetTaskResultResponse, error) {
payload := map[string]string{
"clientKey": c.APIKey,
"taskId": taskID,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result GetTaskResultResponse
json.Unmarshal(body, &result)
return &result, nil
}
// extractTurnstileSiteKey extrai a chave do site Turnstile do HTML da página
func extractTurnstileSiteKey(html string) string {
// Padrão 1: atributo data-sitekey em div cf-turnstile
patterns := []string{
cf-turnstile[^>]*data-sitekey=['"]([^'"]+)['"],
data-sitekey=['"]([^'"]+)['"][^>]*class=['"][^'"]*cf-turnstile,
turnstile\.render\s*\([^,]+,\s*\{[^}]*sitekey['":\s]+['"]([^'"]+)['"],
sitekey['":\s]+['"]([0-9a-zA-Z_-]+)['"],
}
for _, pattern := range patterns {
re := regexp.MustCompile(pattern)
matches := re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
}
return ""
}
// injectTurnstileToken injeta o token resolvido na página
func injectTurnstileToken(page *rod.Page, token string) error {
js := fmt.Sprintf(`
(function() {
// Definir o campo cf-turnstile-response
var responseField = document.querySelector('[name="cf-turnstile-response"]');
if (responseField) {
responseField.value = '%s';
}
// Também tentar encontrar por ID
var byId = document.getElementById('cf-turnstile-response');
if (byId) {
byId.value = '%s';
}
// Criar campo oculto se necessário
if (!responseField && !byId) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'cf-turnstile-response';
input.value = '%s';
var form = document.querySelector('form');
if (form) {
form.appendChild(input);
}
}
// Tentar disparar callback
if (window.turnstile && window.turnstileCallback) {
window.turnstileCallback('%s');
}
return true;
})();
`, token, token, token, token)
_, err := page.Eval(js)
return err
}
func main() {
// Verificar chave de API
if CAPSOLVER_API_KEY == "" {
log.Fatal("variável de ambiente CAPSOLVER_API_KEY é obrigatória")
}
// URL alvo - Substituir por um site que use Cloudflare Turnstile
targetURL := "https://example.com"
log.Println("==============================================")
log.Println("Katana + Capsolver - Demonstração de Turnstile")
log.Println("==============================================")
// Inicializar cliente Capsolver
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Verificar saldo
balance, err := client.GetBalance()
if err != nil {
log.Printf("Aviso: Não foi possível verificar o saldo: %v", err)
} else {
log.Printf("Saldo Capsolver: $%.2f", balance)
}
// Iniciar navegador
log.Println("Iniciando navegador...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(true).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Navegar para o alvo
log.Printf("Navegando para: %s", targetURL)
page := browser.MustPage(targetURL)
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Obter HTML da página
html := page.MustHTML()
// Verificar se há Turnstile
if !strings.Contains(html, "cf-turnstile") && !strings.Contains(html, "turnstile") {
log.Println("Nenhum Turnstile encontrado na página")
log.Println("Dica: Substitua targetURL por um site que use Cloudflare Turnstile")
return
}
log.Println("Turnstile da Cloudflare detectado!")
// Extrair chave do site
siteKey := extractTurnstileSiteKey(html)
if siteKey == "" {
log.Fatal("Não foi possível extrair a chave do site")
}
log.Printf("Chave do site: %s", siteKey)
// Resolver Turnstile
log.Println("Resolvendo Turnstile com Capsolver...")
token, err := client.SolveTurnstile(targetURL, siteKey)
if err != nil {
log.Fatalf("Falha ao resolver Turnstile: %v", err)
}
log.Printf("Token recebido: %s...", token[:min(50, len(token))])
// Injetar token
log.Println("Injetando token na página...")
err = injectTurnstileToken(page, token)
if err != nil {
log.Fatalf("Falha ao injetar token: %v", err)
}
log.Println("==============================================")
log.Println("SUCESSO! Token do Turnstile injetado!")
log.Println("==============================================")
// Obter título da página
title := page.MustEval(`document.title`).String()
log.Printf("Título da página: %s", title)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
### Pontos Chave do Turnstile
1. **Tipo de Tarefa**: Use `AntiTurnstileTaskProxyLess`
2. **Campo de Resposta**: O Turnstile usa `cf-turnstile-response` em vez de `g-recaptcha-response`
3. **Resolução Mais Rápida**: O Turnstile geralmente é resolvido mais rápido que o reCAPTCHA (1-10 segundos)
4. **Campo do Token**: A solução está em `solution.token` em vez de `solution.gRecaptchaResponse`
---
## Crawler Universal de CAPTCHA
Aqui está um crawler completo e modular que lida automaticamente com todos os tipos de CAPTCHA:
```go
// Crawler de CAPTCHA Universal - Exemplo Completo
// Detecta e resolve automaticamente reCAPTCHA v2 e Turnstile
// Uso: go run main.go -url "https://example.com"
// Requer: variável de ambiente CAPSOLVER_API_KEY
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"regexp"
"strings"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
)
// ============================================
// Configuração
// ============================================
var (
CAPSOLVER_API_KEY = os.Getenv("CAPSOLVER_API_KEY")
CAPSOLVER_API = "https://api.capsolver.com"
)
// CaptchaType representa diferentes tipos de CAPTCHA
type CaptchaType string
const (
RecaptchaV2 CaptchaType = "recaptcha_v2"
Turnstile CaptchaType = "turnstile"
Unknown CaptchaType = "unknown"
)
// CaptchaInfo contém os parâmetros extraídos do CAPTCHA
type CaptchaInfo struct {
Type CaptchaType
SiteKey string
}
// ============================================
// Tipos de API
// ============================================
type CreateTaskResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
TaskID string `json:"taskId"`
}
type GetTaskResultResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
Status string `json:"status"`
Solution struct {
GRecaptchaResponse string `json:"gRecaptchaResponse"`
Token string `json:"token"`
} `json:"solution"`
}
type BalanceResponse struct {
ErrorID int `json:"errorId"`
Balance float64 `json:"balance"`
}
// ============================================
// Cliente Capsolver
// ============================================
type CapsolverClient struct {
APIKey string
Client *http.Client
}
func NewCapsolverClient(apiKey string) *CapsolverClient {
return &CapsolverClient{
APIKey: apiKey,
Client: &http.Client{Timeout: 120 * time.Second},
}
}
func (c *CapsolverClient) GetBalance() (float64, error) {
payload := map[string]string{"clientKey": c.APIKey}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getBalance", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result BalanceResponse
json.Unmarshal(body, &result)
return result.Balance, nil
}
func (c *CapsolverClient) Solve(info *CaptchaInfo, websiteURL string) (string, error) {
switch info.Type {
case RecaptchaV2:
return c.solveRecaptchaV2(websiteURL, info.SiteKey)
case Turnstile:
return c.solveTurnstile(websiteURL, info.SiteKey)
default:
return "", fmt.Errorf("tipo de CAPTCHA não suportado: %s", info.Type)
}
}
func (c *CapsolverClient) solveRecaptchaV2(websiteURL, siteKey string) (string, error) {
task := map[string]interface{}{
"type": "ReCaptchaV2TaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
return c.solveTask(task, "recaptcha")
}
func (c *CapsolverClient) solveTurnstile(websiteURL, siteKey string) (string, error) {
task := map[string]interface{}{
"type": "AntiTurnstileTaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
return c.solveTask(task, "turnstile")
}
func (c *CapsolverClient) solveTask(task map[string]interface{}, tokenType string) (string, error) {
// Criar tarefa
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("falha ao criar tarefa: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("erro da API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Tarefa criada: %s", createResult.TaskID)
// Verificar resultado
for i := 0; i < 120; i++ {
getPayload := map[string]string{
"clientKey": c.APIKey,
"taskId": createResult.TaskID,
}
jsonData, _ := json.Marshal(getPayload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", err
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var result GetTaskResultResponse
json.Unmarshal(body, &result)
if result.Status == "ready" {
if tokenType == "turnstile" {
return result.Solution.Token, nil
}
return result.Solution.GRecaptchaResponse, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("tarefa falhou: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Aguardando solução... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("tempo esgotado ao aguardar solução")
}
// ============================================
// Detecção de CAPTCHA
// ============================================
func DetectCaptcha(html string) *CaptchaInfo {
// Verificar reCAPTCHA v2 (checkbox)
if strings.Contains(html, "g-recaptcha") {
siteKey := extractDataSiteKey(html, "g-recaptcha")
if siteKey != "" {
return &CaptchaInfo{
Type: RecaptchaV2,
SiteKey: siteKey,
}
}
}
// Verificar Cloudflare Turnstile
if strings.Contains(html, "cf-turnstile") || strings.Contains(html, "challenges.cloudflare.com/turnstile") {
siteKey := extractDataSiteKey(html, "cf-turnstile")
if siteKey != "" {
return &CaptchaInfo{
Type: Turnstile,
SiteKey: siteKey,
}
}
}
return nil
}
func extractDataSiteKey(html, className string) string {
pattern := fmt.Sprintf(`class=['"][^'"]*%s[^'"]*['"][^>]*data-sitekey=['"]([^'"]+)['"]`, className)
re := regexp.MustCompile(pattern)
matches := re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
// Padrão alternativo
pattern = fmt.Sprintf(`data-sitekey=['"]([^'"]+)['"][^>]*class=['"][^'"]*%s`, className)
re = regexp.MustCompile(pattern)
matches = re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
// Padrão genérico para sitekey
re = regexp.MustCompile(`data-sitekey=['"]([^'"]+)['"]`)
matches = re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
return ""
}
// ============================================
// Injeção de Token
// ============================================
func InjectToken(page *rod.Page, token string, captchaType CaptchaType) error {
var js string
switch captchaType {
case RecaptchaV2:
js = fmt.Sprintf(`
(function() {
var responseField = document.getElementById('g-recaptcha-response');
if (responseField) {
responseField.style.display = 'block';
responseField.value = '%s';
}
var textareas = document.querySelectorAll('textarea[name="g-recaptcha-response"]');
for (var i = 0; i < textareas.length; i++) {
textareas[i].value = '%s';
}
if (typeof ___grecaptcha_cfg !== 'undefined') {
var clients = ___grecaptcha_cfg.clients;
for (var key in clients) {
var client = clients[key];
if (client) {
try {
var callback = client.callback ||
(client.Q && client.Q.callback) ||
(client.S && client.S.callback);
if (typeof callback === 'function') {
callback('%s');
}
} catch(e) {}
}
}
}
return true;
})();
`, token, token, token)
case Turnstile:
js = fmt.Sprintf(`
(function() {
var responseField = document.querySelector('[name="cf-turnstile-response"]');
if (responseField) {
responseField.value = '%s';
}
if (!responseField) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'cf-turnstile-response';
input.value = '%s';
var form = document.querySelector('form');
if (form) form.appendChild(input);
}
if (window.turnstile && window.turnstileCallback) {
window.turnstileCallback('%s');
}
return true;
})();
`, token, token, token)
default:
return fmt.Errorf("tipo de CAPTCHA não suportado: %s", captchaType)
}
_, err := page.Eval(js)
return err
}
// ============================================
// Crawler
// ============================================
type CrawlResult struct {
URL string
Title string
Success bool
CaptchaFound bool
CaptchaType CaptchaType
CaptchaSolved bool
Error string
}
func Crawl(browser *rod.Browser, client *CapsolverClient, targetURL string) *CrawlResult {
result := &CrawlResult{
URL: targetURL,
Sucesso: false,
}
// Navegar para o destino
log.Printf("Navegando para: %s", targetURL)
page := browser.MustPage(targetURL)
defer page.MustClose()
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Obter HTML da página
html := page.MustHTML()
// Detectar CAPTCHA
captchaInfo := DetectCaptcha(html)
if captchaInfo != nil && captchaInfo.Type != Unknown {
result.CaptchaFound = true
result.CaptchaType = captchaInfo.Type
log.Printf("CAPTCHA detectado: %s (siteKey: %s)", captchaInfo.Type, captchaInfo.SiteKey)
// Resolver CAPTCHA
log.Println("Resolvendo CAPTCHA com Capsolver...")
token, err := client.Solve(captchaInfo, targetURL)
if err != nil {
result.Error = fmt.Sprintf("falha ao resolver CAPTCHA: %v", err)
log.Printf("Erro: %s", result.Error)
return result
}
log.Printf("Token recebido: %s...", token[:min(50, len(token))])
// Injetar token
log.Println("Injetando token...")
err = InjectToken(page, token, captchaInfo.Type)
if err != nil {
result.Error = fmt.Sprintf("falha ao injetar token: %v", err)
log.Printf("Erro: %s", result.Error)
return result
}
result.CaptchaSolved = true
log.Println("Token injetado com sucesso!")
// Tentar enviar formulário
submitForm(page)
time.Sleep(3 * time.Second)
} else {
log.Println("Nenhum CAPTCHA detectado na página")
}
// Obter informações da página final
result.Title = page.MustEval(`document.title`).String()
result.Success = true
return result
}
func submitForm(page *rod.Page) {
selectors := []string{
"button[type='submit']",
"input[type='submit']",
"#recaptcha-demo-submit",
".submit-button",
}
for _, selector := range selectors {
js := fmt.Sprintf(`
(function() {
var btn = document.querySelector('%s');
if (btn && btn.offsetParent !== null) {
btn.click();
return true;
}
return false;
})();
`, selector)
result := page.MustEval(js)
if result.Bool() {
log.Printf("Clicado no botão de envio: %s", selector)
return
}
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
// ============================================
// Principal
// ============================================
func main() {
// Analisar flags
targetURL := flag.String("url", "https://www.google.com/recaptcha/api2/demo", "URL alvo para navegação")
headless := flag.Bool("headless", true, "Executar o navegador em modo sem cabeça")
checkBalance := flag.Bool("balance", false, "Verificar apenas o saldo da conta")
flag.Parse()
// Verificar chave de API
if CAPSOLVER_API_KEY == "" {
log.Fatal("A variável de ambiente CAPSOLVER_API_KEY é obrigatória")
}
log.Println("==============================================")
log.Println("Katana + Capsolver - Crawler Universal de CAPTCHA")
log.Println("==============================================")
// Inicializar cliente
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Verificar saldo
balance, err := client.GetBalance()
if err != nil {
log.Printf("Aviso: Não foi possível verificar o saldo: %v", err)
} else {
log.Printf("Saldo do Capsolver: $%.2f", balance)
}
if *checkBalance {
return
}
// Iniciar navegador
log.Println("Iniciando navegador...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(*headless).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Navegar
result := Crawl(browser, client, *targetURL)
// Exibir resultados
log.Println("==============================================")
log.Println("RESULTADOS DA NAVEGAÇÃO")
log.Println("==============================================")
log.Printf("URL: %s", result.URL)
log.Printf("Título: %s", result.Title)
log.Printf("Sucesso: %v", result.Success)
log.Printf("CAPTCHA Detectado: %v", result.CaptchaFound)
if result.CaptchaFound {
log.Printf("Tipo de CAPTCHA: %s", result.CaptchaType)
log.Printf("CAPTCHA Resolvido: %v", result.CaptchaSolved)
}
if result.Error != "" {
log.Printf("Erro: %s", result.Error)
}
log.Println("==============================================")
}
Uso
bash
# Criar projeto
mkdir katana-universal-crawler
cd katana-universal-crawler
go mod init katana-universal-crawler
# Instalar dependências
go get github.com/go-rod/rod@latest
# Definir chave de API
export CAPSOLVER_API_KEY="SUA_CHAVE_DE_API"
# Executar com padrão (demonstração reCAPTCHA v2)
go run main.go
# Executar com URL personalizada
go run main.go -url "https://exemplo.com"
# Verificar saldo apenas
go run main.go -balance
# Executar com navegador visível
go run main.go -headless=false
Boas Práticas
1. Otimização de Desempenho
- Usar tipos de tarefa sem proxy:
ReCaptchaV2TaskProxyLessusa proxies internos do Capsolver para resolver mais rapidamente - Processamento paralelo: Comece a resolver CAPTCHA enquanto outros elementos da página carregam
- Cache de tokens: Tokens de reCAPTCHA são válidos por ~2 minutos; cache quando possível
2. Gestão de Custos
- Detectar antes de resolver: Chame o Capsolver apenas quando um CAPTCHA realmente estiver presente
- Validar chaves de site: Certifique-se de que as chaves extraídas sejam válidas antes das chamadas à API
- Monitorar uso: Rastreie chamadas à API para gerenciar custos efetivamente
3. Tratamento de Erros
go
func SolveWithRetry(client *CapsolverClient, info *CaptchaInfo, url string, maxRetries int) (string, error) {
var lastErr error
for i := 0; i < maxRetries; i++ {
token, err := client.Solve(info, url)
if err == nil {
return token, nil
}
lastErr = err
log.Printf("Tentativa %d falhou: %v", i+1, err)
// Backoff exponencial
time.Sleep(time.Duration(i+1) * time.Second)
}
return "", fmt.Errorf("falha após %d tentativas: %w", maxRetries, lastErr)
}
4. Limitação de Taxa
Implemente atrasos apropriados entre solicitações para evitar detecção:
go
type RateLimiter struct {
requests int
interval time.Duration
lastRequest time.Time
mu sync.Mutex
}
func (r *RateLimiter) Wait() {
r.mu.Lock()
defer r.mu.Unlock()
elapsed := time.Since(r.lastRequest)
if elapsed < r.interval {
time.Sleep(r.interval - elapsed)
}
r.lastRequest = time.Now()
}
Solução de Problemas
Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
ERROR_ZERO_BALANCE |
Créditos insuficientes | Recarregar conta do Capsolver |
ERROR_CAPTCHA_UNSOLVABLE |
Chave de site inválida | Verificar lógica de extração |
ERROR_INVALID_TASK_DATA |
Parâmetros ausentes | Verificar estrutura da tarefa |
context deadline exceeded |
Tempo limite | Aumentar tempo limite ou verificar rede |
Dicas de Depuração
- Ativar navegador visível: Definir
Headless(false)para ver o que está acontecendo - Logar tráfego de rede: Monitorar solicitações para identificar problemas
- Salvar capturas de tela: Capturar estado da página para depuração
- Validar tokens: Registrar formato do token antes da injeção
Perguntas Frequentes
Q: Posso usar o Katana sem modo sem cabeça para páginas de CAPTCHA?
A: Não, páginas de CAPTCHA exigem renderização de JavaScript, que só funciona no modo sem cabeça.
Q: Por quanto tempo os tokens de CAPTCHA são válidos?
A: Tokens de reCAPTCHA: ~2 minutos. Turnstile: varia conforme configuração.
Q: Qual é o tempo médio de resolução?
A: reCAPTCHA v2: 5-15s, Turnstile: 1-10s.
Q: Posso usar meu próprio proxy?
A: Sim, use tipos de tarefa sem o sufixo "ProxyLess" e forneça a configuração do proxy.
Conclusão
Integrar o Capsolver com o Katana permite lidar com CAPTCHA de forma robusta para suas necessidades de raspagem de web. Os scripts completos acima podem ser copiados diretamente e usados com seus projetos Go.
Pronto para começar? Registre-se no Capsolver e potencialize seus raspadores!
💡 Bônus Exclusivo para Usuários de Integração Katana:
Para comemorar esta integração, oferecemos um código de bônus exclusivo de 6% — Katana para todos os usuários do CapSolver que se registrarem por meio deste tutorial.
Basta inserir o código durante o recarregamento no Dashboard para receber créditos extras de 6% instantaneamente.
12. Documentações
- 12.1. Repositório GitHub do Katana
- 12.2. Documentação do Katana
- 12.3. Documentação do Capsolver
- 12.4. Automação de Navegador Go Rod
Declaração de Conformidade: As informações fornecidas neste blog são apenas para fins informativos. A CapSolver está comprometida em cumprir todas as leis e regulamentos aplicáveis. O uso da rede CapSolver para atividades ilegais, fraudulentas ou abusivas é estritamente proibido e será investigado. Nossas soluções de resolução de captcha melhoram a experiência do usuário enquanto garantem 100% de conformidade ao ajudar a resolver dificuldades de captcha durante a coleta de dados públicos. Incentivamos o uso responsável de nossos serviços. Para mais informações, visite nossos Termos de Serviço e Política de Privacidade.
Mais

Selenium vs Puppeteer para Resolução de CAPTCHA: Comparação de Desempenho e Caso de Uso
Compare o Selenium vs Puppeteer para resolver CAPTCHA. Descubra benchmarks de desempenho, notas de estabilidade e como integrar o CapSolver para o máximo de sucesso.

Ethan Collins
08-Apr-2026

Dados como Serviço (DaaS): O que é e por que importa em 2026
Entenda Dados como Serviço (DaaS) em 2026. Descubra seus benefícios, casos de uso e como transforma os negócios com visões em tempo real e escalabilidade.

Ethan Collins
12-Feb-2026

Como corrigir erros comuns de raspagem da web em 2026
Dominar a correção de diversos erros de raspagem de web, como 400, 401, 402, 403, 429, 5xx e 1001 do Cloudflare em 2026. Aprenda estratégias avançadas para rotação de IPs, cabeçalhos e limitação de taxa adaptativa com o CapSolver.

Rajinder Singh
05-Feb-2026

Como resolver Captcha no RoxyBrowser com integração do CapSolver
Integre o CapSolver com o RoxyBrowser para automatizar tarefas do navegador e contornar o reCAPTCHA, o Turnstile e outros CAPTCHAS.

Adélia Cruz
04-Feb-2026

Como resolver Captcha no EasySpider com integração do CapSolver
EasySpider é uma ferramenta de raspagem de web e automação do navegador visual e sem código, e quando combinado com o CapSolver, pode resolver de forma confiável CAPTCHAs como reCAPTCHA v2 e Cloudflare Turnstile, permitindo a extração de dados automatizada sem interrupções em sites.

Adélia Cruz
04-Feb-2026

Como resolver reCAPTCHA v2 no Relevance AI com integração da CapSolver
Construa uma ferramenta da Relevance AI para resolver reCAPTCHA v2 usando o CapSolver. Automatize os envios de formulários via API sem automação de navegador.

Adélia Cruz
03-Feb-2026


