Scarnox · Blog

Bộ giải captcha bằng Go: ví dụ hoạt động với net/http (2026)

Một bộ giải captcha bằng Go không cần gói bên thứ ba nào. net/http và encoding/json của thư viện chuẩn bao quát toàn bộ luồng: một lệnh POST đến API của Scarnox, một phản hồi JSON, và một lần submit form đến website. Hướng dẫn này xây dựng luồng đó cho PopularCaptcha, loại mà Scarnox giải hôm nay theo kiểu proxyless.

Bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ và chạy chương trình trên một trang bạn kiểm soát trước khi dùng nó ở nơi khác.

Yêu cầu

Chương trình đầy đủ

Tệp đơn này giải captcha, thử lại các lỗi tạm thời và submit token vào form. Lưu nó thành 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"

// Lời gọi giải bị chặn cho đến khi token sẵn sàng, nên timeout dài.
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 thử lại các lỗi tạm thời. Các lần giải thất bại không bị tính phí.
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 giữ phiên mà website gắn captcha với.
	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)
}

Chạy nó:

export SCARNOX_API_KEY=scarnox_YOURAPIKEY
go run main.go

Các phần hoạt động cùng nhau thế nào

Yêu cầu. solveCaptcha marshal một struct thành thân JSON mà API mong đợi và đặt header Authorization. Tiền tố Bearer là bắt buộc.

Timeout. API bị chặn cho đến khi token sẵn sàng, nên timeout phía client là 185 giây. Đừng đặt thấp hơn, nếu không bạn sẽ hủy những lần giải lẽ ra đã gần xong.

Kiểm tra lỗi. API trả về errorId là 0 khi thành công. Mọi giá trị khác đi kèm errorCode và errorDescription, và hàm trả về chúng dưới dạng lỗi. Token nằm tại solution.token.

Submit. Token được đưa vào trường h-captcha-response, và g-recaptcha-response nhận cùng giá trị. Hầu hết website đọc một trong hai.

Nhiều trang đăng ký và tìm kiếm gắn captcha với phiên. cookiejar trên site client giữ các cookie đó giữa GET hiển thị form và POST submit nó. Nếu bỏ qua nó, website có thể từ chối một token vốn hợp lệ.

Thời gian

Token hết hạn trong khoảng hai phút, nên hãy giải ngay trước khi submit. Đừng giải ở đầu một công việc dài rồi chờ. Hết hạn token captcha giải thích cách xác định khoảng thời gian.

Widget ẩn

Nếu widget trên trang là ẩn, hãy đặt đối số invisible thành true. Khi đó yêu cầu có is_invisible: true. Một số trang cũng cần một hàm callback được gọi với token. Kiểm tra script của trang để tìm nó. Hướng dẫn captcha ẩn liệt kê các mẫu thường gặp.

Đồng thời

Go giúp dễ dàng giải nhiều captcha cùng lúc bằng goroutine. Giữ số lần giải đồng thời trong khả năng chịu đựng của tài khoản và website mục tiêu. Một tên miền không nên nhận một đợt yêu cầu bùng nổ, ngay cả khi bộ giải của bạn có thể theo kịp. Tôn trọng robots.txt và điều khoản của website, và giới hạn việc scraping ở dữ liệu công khai mà bạn được phép thu thập.

Giá

Scarnox tính phí theo mỗi lần giải thành công. Các lần giải thất bại không bị tính phí. Xem bảng giá hiện tại để biết mức phí. Để thử API, bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ.

Các bước tiếp theo