Scarnox · Blog

Captcha solver ใน Go: ตัวอย่างการทำงานด้วย net/http (2026)

captcha solver ใน Go ไม่ต้องใช้แพ็กเกจของบุคคลที่สาม net/http และ encoding/json ของ standard library ครอบคลุมทั้งโฟลว์: POST หนึ่งครั้งไปยัง Scarnox API การตอบกลับ JSON หนึ่งครั้ง และการ submit ฟอร์มหนึ่งครั้งไปยังเว็บ คู่มือนี้สร้างโฟลว์นั้นสำหรับ PopularCaptcha ซึ่ง Scarnox แก้ได้ในวันนี้แบบ proxyless

เริ่มด้วยเครดิตทดลองใช้ฟรี ไม่ต้องใช้บัตร และรันโปรแกรมกับหน้าที่คุณควบคุมก่อนนำไปใช้ที่อื่น

สิ่งที่ต้องมี

โปรแกรมฉบับเต็ม

ไฟล์เดียวนี้แก้ captcha ลองใหม่เมื่อเกิดความล้มเหลวชั่วคราว และส่ง token ไปยังฟอร์ม บันทึกเป็น 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"

// การเรียก solve จะรอจนกว่า token จะพร้อม จึงตั้ง timeout ไว้นาน
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 ลองใหม่เมื่อล้มเหลวชั่วคราว การ solve ที่ล้มเหลวจะไม่ถูกเรียกเก็บเงิน
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 จะเก็บ session ที่เว็บผูก 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)
}

รันโปรแกรม:

export SCARNOX_API_KEY=scarnox_YOURAPIKEY
go run main.go

โครงสร้างของแต่ละส่วน

คำขอ solveCaptcha แปลง struct เป็น JSON body ที่ API คาดหวังและตั้งค่า header Authorization คำนำหน้า Bearer จำเป็นต้องมี

timeout API บล็อกจนกว่า token จะพร้อม ดังนั้น timeout ของ client คือ 185 วินาที อย่าตั้งให้ต่ำกว่านี้ มิฉะนั้นคุณจะยกเลิกการแก้ที่กำลังจะเสร็จ

การตรวจ error API ส่ง errorId เป็น 0 เมื่อสำเร็จ ค่าอื่นใดจะมาพร้อม errorCode และ errorDescription ฟังก์ชันคืนค่าเหล่านี้เป็น error token อยู่ที่ solution.token

การ submit token ถูกใส่ในฟิลด์ h-captcha-response และ g-recaptcha-response ได้ค่าเดียวกัน เว็บส่วนใหญ่อ่านอย่างใดอย่างหนึ่ง

หน้าสมัครสมาชิกและค้นหาหลายแห่งผูก captcha กับ session cookie jar ของ site client เก็บ cookie ระหว่าง GET ที่แสดงฟอร์มกับ POST ที่ submit หากข้ามขั้นนี้ เว็บอาจปฏิเสธ token ที่ถูกต้องอยู่แล้ว

การจับเวลา

token หมดอายุภายในประมาณสองนาที ดังนั้นให้ solve ทันทีก่อน submit อย่า solve ตอนเริ่มงานยาว ๆ แล้วรอ อายุของ captcha token อธิบายวิธีกำหนดช่วงเวลา

widget แบบ invisible

หาก widget ของหน้าเป็นแบบ invisible ให้ตั้งอาร์กิวเมนต์ invisible เป็น true คำขอจะมี is_invisible: true บางหน้าต้องมีฟังก์ชัน callback ที่ถูกเรียกพร้อม token ให้ตรวจสอบ script ของหน้าเพื่อหาฟังก์ชันนั้น คู่มือ invisible captcha แสดงรูปแบบต่าง ๆ

ความพร้อมกัน

Go ทำให้การแก้ captcha จำนวนมากพร้อมกันด้วย goroutine ได้ง่าย ควรให้จำนวนการแก้พร้อมกันอยู่ในขอบเขตที่บัญชีของคุณและเว็บเป้าหมายรับไหว โดเมนเดียวไม่ควรได้รับคำขอชุดใหญ่ แม้ solver ของคุณจะตามทันก็ตาม เคารพ robots.txt และข้อกำหนดของเว็บ และจำกัดการ scrape ให้เป็นข้อมูลสาธารณะที่คุณได้รับอนุญาตให้เก็บ

ราคา

Scarnox คิดเงินต่อการแก้ที่สำเร็จ การแก้ที่ล้มเหลวไม่มีค่าใช้จ่าย ดูราคาปัจจุบัน สำหรับอัตรา และเพื่อลอง API ให้เริ่มด้วยเครดิตทดลองใช้ฟรี ไม่ต้องใช้บัตร

ขั้นตอนถัดไป