Solver captcha w Go: działający przykład z net/http (2026)
Solver captcha w Go nie potrzebuje pakietów zewnętrznych. Standardowe net/http i encoding/json obejmują cały przepływ: jedno żądanie POST do API Scarnox, jedną odpowiedź JSON i jedno wysłanie formularza na stronę. Ten przewodnik buduje ten przepływ dla PopularCaptcha, które Scarnox dziś rozwiązuje bez proxy.
Zacznij od darmowego kredytu próbnego, bez karty i uruchom program na stronie, którą kontrolujesz, zanim użyjesz go gdziekolwiek indziej.
Wymagania
- Go 1.21 lub nowszy.
- Klucz API Scarnox odczytywany ze zmiennej środowiskowej.
- Sitekey widżetu PopularCaptcha na stronie. Jeśli potrzebujesz pomocy w jego znalezieniu, zobacz jak znaleźć sitekey captcha.
Kompletny program
Ten pojedynczy plik rozwiązuje captcha, ponawia przejściowe błędy i wysyła token do formularza. Zapisz go jako 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"
// Wywołanie blokuje się, aż token będzie gotowy, dlatego timeout jest długi.
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 ponawia przejściowe błędy. Nieudane rozwiązania nie są naliczane.
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 zachowuje sesję, z którą serwis wiąże captcha.
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)
}
Uruchom:
export SCARNOX_API_KEY=scarnox_YOURAPIKEY
go run main.go
Jak elementy się łączą
Żądanie. solveCaptcha serializuje strukturę do treści JSON, której oczekuje API, i ustawia nagłówek Authorization. Prefiks Bearer jest wymagany.
Timeout. API blokuje się, aż token będzie gotowy, więc timeout klienta wynosi 185 sekund. Nie ustawiaj go niżej, bo anulujesz rozwiązania, które właśnie miały się zakończyć.
Sprawdzenie błędu. API zwraca errorId równe 0 przy sukcesie. Każda inna wartość przychodzi z errorCode i errorDescription, a funkcja zwraca je jako błąd. Token znajduje się w solution.token.
Wysłanie. Token trafia do pola h-captcha-response, a g-recaptcha-response dostaje tę samą wartość. Większość serwisów czyta jedno z nich.
Użyj cookie jar
Wiele stron rejestracji i wyszukiwania wiąże captcha z sesją. cookiejar na kliencie strony zachowuje te ciasteczka między GET, które pokazuje formularz, a POST, który go wysyła. Jeśli to pominiesz, serwis może odrzucić token, który w innym wypadku byłby poprawny.
Czas
Token wygasa po około dwóch minutach, więc rozwiązuj tuż przed wysłaniem. Nie rozwiązuj na początku długiego zadania, a potem nie czekaj. Wygasanie tokenu captcha wyjaśnia, jak dobrać okno czasowe.
Niewidoczne widżety
Jeśli widżet na stronie jest niewidoczny, ustaw argument invisible na true. Żądanie zawiera wtedy is_invisible: true. Niektóre strony wymagają też funkcji zwrotnej wywoływanej z tokenem. Sprawdź skrypt strony, czy ją zawiera. Wzorce opisuje przewodnik o niewidocznej captcha.
Równoległość
Go ułatwia rozwiązywanie wielu captcha naraz za pomocą goroutine. Utrzymuj liczbę równoległych rozwiązań w granicach tego, co obsłuży Twoje konto i docelowy serwis. Jedna domena nie powinna otrzymywać serii żądań, nawet jeśli solver nadąża. Szanuj robots.txt i regulamin serwisu oraz ogranicz scrapowanie do danych publicznych, które wolno Ci zbierać.
Cennik
Scarnox nalicza opłatę za każde udane rozwiązanie. Nieudane rozwiązania są bezpłatne. Stawki znajdziesz na stronie cennika. Aby wypróbować API, zacznij od darmowego kredytu próbnego, bez karty.