Решатель капчи на Go: рабочий пример с net/http (2026)
Решателю капчи на Go не нужны сторонние пакеты. Стандартных net/http и encoding/json хватает на весь поток: один POST в API Scarnox, один JSON-ответ и одна отправка формы на сайт. Этот гайд строит такой поток для PopularCaptcha, которую Scarnox сегодня решает в варианте proxyless.
Начните с пробного баланса, карта не нужна и запустите программу на странице, которой вы управляете, прежде чем использовать её где-либо ещё.
Требования
- Go 1.21 или новее.
- Ключ API Scarnox, который читается из переменной окружения.
- Sitekey виджета PopularCaptcha на странице. Если нужна помощь с поиском, см. как найти sitekey капчи.
Полная программа
Этот один файл решает капчу, повторяет временные сбои и отправляет токен в форму. Сохраните его как 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 получает то же значение. Большинство сайтов читают одно из них.
Используйте cookie jar
Многие страницы регистрации и поиска привязывают капчу к сессии. cookiejar у клиента сайта сохраняет эти cookie между GET, который показывает форму, и POST, который её отправляет. Если его пропустить, сайт может отклонить токен, который в остальном был валидным.
Тайминг
Токен истекает примерно через две минуты, поэтому решайте непосредственно перед отправкой. Не решайте в начале длинной задачи и не ждите. Истечение токена капчи объясняет, как рассчитать окно.
Невидимые виджеты
Если виджет на странице невидимый, передайте аргумент invisible равным true. Тогда запрос включает is_invisible: true. Некоторым страницам также нужна функция-колбэк, которой передают токен. Проверьте скрипт страницы, чтобы её найти. Шаблоны перечислены в гайде невидимая капча.
Конкурентность
В Go легко решать много капч одновременно с помощью горутин. Держите число параллельных решений в пределах того, что выдержат ваш аккаунт и целевой сайт. Один домен не должен получать всплеск запросов, даже если ваш решатель за ними успевает. Соблюдайте robots.txt и условия сайта и ограничивайте парсинг публичными данными, которые вам разрешено собирать.
Тарифы
Scarnox берёт плату за каждое успешное решение. Неудачные решения не списываются. Расценки — на странице цен. Чтобы попробовать API, начните с пробного баланса, карта не нужна.