Scarnox · Blog

Солвер капчі на Go: робочий приклад з net/http (2026)

Солверу капчі на Go не потрібні сторонні пакети. Стандартні net/http і encoding/json покривають увесь потік: один POST до API Scarnox, одна JSON-відповідь і одна відправка форми на сайт. Цей посібник будує такий потік для PopularCaptcha, яку Scarnox сьогодні розв'язує в режимі proxyless.

Почніть з безкоштовного пробного кредиту, картка не потрібна і запустіть програму на сторінці, якою володієте, перш ніж використовувати її деінде.

Вимоги

Повна програма

Цей один файл розв'язує капчу, повторює тимчасові збої і відправляє токен у форму. Збережіть його як main.go:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"time"
)

const apiURL = "https://api.scarnox.com/api/tasks/create"

// Виклик розв'язання блокується, доки токен не буде готовий, тому таймаут довгий.
var apiClient = &http.Client{Timeout: 185 * time.Second}

type createRequest struct {
	CaptchaType string `json:"captcha_type"`
	SiteKey     string `json:"site_key"`
	SiteURL     string `json:"site_url"`
	IsInvisible bool   `json:"is_invisible,omitempty"`
}

type createResponse struct {
	ErrorID          int    `json:"errorId"`
	ErrorCode        string `json:"errorCode"`
	ErrorDescription string `json:"errorDescription"`
	Solution         struct {
		Token string `json:"token"`
	} `json:"solution"`
}

func solveCaptcha(apiKey, sitekey, pageURL string, invisible bool) (string, error) {
	body, err := json.Marshal(createRequest{
		CaptchaType: "PopularCaptchaTaskProxyless",
		SiteKey:     sitekey,
		SiteURL:     pageURL,
		IsInvisible: invisible,
	})
	if err != nil {
		return "", err
	}

	req, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewReader(body))
	if err != nil {
		return "", err
	}
	req.Header.Set("Authorization", "Bearer "+apiKey)
	req.Header.Set("Content-Type", "application/json")

	resp, err := apiClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	raw, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	var out createResponse
	if err := json.Unmarshal(raw, &out); err != nil {
		return "", fmt.Errorf("HTTP %d, response was not JSON: %s", resp.StatusCode, raw)
	}
	if out.ErrorID != 0 {
		return "", fmt.Errorf("%s: %s", out.ErrorCode, out.ErrorDescription)
	}
	return out.Solution.Token, nil
}

// solveWithRetry повторює тимчасові збої. Невдалі розв'язання не тарифікуються.
func solveWithRetry(apiKey, sitekey, pageURL string, attempts int) (string, error) {
	var lastErr error
	for i := 1; i <= attempts; i++ {
		token, err := solveCaptcha(apiKey, sitekey, pageURL, false)
		if err == nil {
			return token, nil
		}
		lastErr = err
		log.Printf("attempt %d failed: %v", i, err)
		time.Sleep(time.Duration(i) * time.Second)
	}
	return "", lastErr
}

func main() {
	apiKey := os.Getenv("SCARNOX_API_KEY")
	if apiKey == "" {
		log.Fatal("set SCARNOX_API_KEY")
	}

	const pageURL = "https://example.com/signup"
	const sitekey = "YOUR-SITEKEY-UUID"

	token, err := solveWithRetry(apiKey, sitekey, pageURL, 3)
	if err != nil {
		log.Fatal(err)
	}

	// Cookie jar зберігає сесію, до якої сайт прив'язує капчу.
	jar, err := cookiejar.New(nil)
	if err != nil {
		log.Fatal(err)
	}
	site := &http.Client{Jar: jar, Timeout: 60 * time.Second}

	form := url.Values{}
	form.Set("email", "you@example.com")
	form.Set("h-captcha-response", token)
	form.Set("g-recaptcha-response", token)

	resp, err := site.PostForm(pageURL, form)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	fmt.Println("Status:", resp.Status)
}

Запуск:

export SCARNOX_API_KEY=scarnox_YOURAPIKEY
go run main.go

Як частини складаються разом

Запит. solveCaptcha перетворює структуру на JSON-тіло, яке очікує API, і встановлює заголовок Authorization. Префікс Bearer обов'язковий.

Таймаут. API блокується, доки токен не буде готовий, тому таймаут клієнта — 185 секунд. Не встановлюйте менше, інакше скасуєте розв'язання, які мали от-от завершитися.

Перевірка помилки. API повертає errorId зі значенням 0 при успіху. Будь-яке інше значення супроводжується errorCode і errorDescription, і функція повертає їх як помилку. Токен лежить у solution.token.

Відправка. Токен іде в поле h-captcha-response, а g-recaptcha-response отримує те саме значення. Більшість сайтів читають одне з них.

Багато сторінок реєстрації та пошуку прив'язують капчу до сесії. cookiejar на клієнті сайту зберігає ці cookie між GET, який показує форму, і POST, який її відправляє. Якщо його пропустити, сайт може відхилити токен, який інакше був би дійсним.

Час

Токен спливає приблизно за дві хвилини, тож розв'язуйте безпосередньо перед відправкою. Не розв'язуйте на початку довгого завдання і не чекайте. Посібник про термін дії токена капчі пояснює, як розрахувати вікно.

Невидимі віджети

Якщо віджет на сторінці невидимий, встановіть аргумент invisible зі значенням true. Тоді запит містить is_invisible: true. Деякі сторінки також потребують колбеку, який викликається з токеном. Перевірте скрипт сторінки на його наявність. Посібник про невидиму капчу перелічує патерни.

Паралельність

У Go легко розв'язувати багато капч одночасно через горутини. Тримайте кількість одночасних розв'язань у межах того, що витримують ваш обліковий запис і цільовий сайт. Один домен не повинен отримувати сплеск запитів, навіть якщо ваш солвер встигає. Дотримуйтесь robots.txt і умов сайту та обмежуйтесь публічними даними, які вам дозволено збирати.

Ціни

Scarnox тарифікує за кожне успішне розв'язання. Невдалі розв'язання безкоштовні. Тарифи дивіться на сторінці цін. Щоб спробувати API, почніть з безкоштовного пробного кредиту, картка не потрібна.

Наступні кроки