Tích hợp Katana với CapSolver: Giải CAPTCHA tự động cho quét web

Anh Tuan
Data Science Expert
12-Jan-2026

Crawling web là một kỹ thuật quan trọng đối với các nhà nghiên cứu an ninh, người kiểm tra xâm nhập và nhà phân tích dữ liệu. Tuy nhiên, các trang web hiện đại ngày càng sử dụng CAPTCHA để bảo vệ chống lại truy cập tự động. Hướng dẫn này sẽ minh họa cách tích hợp Katana, khung crawling web mạnh mẽ của ProjectDiscovery, với CapSolver, dịch vụ giải CAPTCHA hàng đầu, để tạo ra một giải pháp crawling mạnh mẽ có thể tự động xử lý các thách thức CAPTCHA.
Bạn sẽ học được gì
- Cài đặt Katana ở chế độ trình duyệt không đầu
- Tích hợp API của Capsolver để giải CAPTCHA tự động
- Xử lý reCAPTCHA v2 và Cloudflare Turnstile
- Các ví dụ mã chạy được cho từng loại CAPTCHA
- Các phương pháp tốt nhất để dò tìm hiệu quả và có trách nhiệm
Katana là gì?
Katana là một khung crawling web thế hệ mới được phát triển bởi ProjectDiscovery. Nó được thiết kế để nhanh chóng và linh hoạt, phù hợp với việc khám phá an ninh và quy trình tự động hóa.
Tính năng chính
- Hai chế độ crawling: Crawling dựa trên HTTP và tự động hóa trình duyệt không đầu
- Hỗ trợ JavaScript: Phân tích và crawling nội dung được render bằng JavaScript
- Cấu hình linh hoạt: Tiêu đề tùy chỉnh, cookie, điền biểu mẫu và kiểm soát phạm vi
- Nhiều định dạng đầu ra: Văn bản thuần, JSON hoặc JSONL
Cài đặt
bash
# Yêu cầu Go 1.24+
CGO_ENABLED=1 go install github.com/projectdiscovery/katana/cmd/katana@latest
Cách sử dụng cơ bản
bash
katana -u https://example.com -headless
CapSolver là gì?
CapSolver là một dịch vụ giải CAPTCHA dựa trên AI cung cấp các giải pháp nhanh và đáng tin cậy cho nhiều loại CAPTCHA.
Loại CAPTCHA được hỗ trợ
- reCAPTCHA: v2 và phiên bản doanh nghiệp
- Cloudflare: Turnstile và Challenge
- AWS WAF: Bypass bảo vệ WAF
- Và Nhiều hơn
Quy trình API
CapSolver sử dụng mô hình API dựa trên nhiệm vụ:
- Tạo nhiệm vụ: Gửi tham số CAPTCHA (loại, siteKey, URL)
- Nhận ID nhiệm vụ: Nhận mã nhiệm vụ duy nhất
- Kiểm tra kết quả: Kiểm tra trạng thái nhiệm vụ cho đến khi giải pháp sẵn sàng
- Nhận token: Nhận token CAPTCHA đã được giải
Yêu cầu trước khi bắt đầu
Trước khi bắt đầu, hãy đảm bảo bạn có:
- Go 1.24+ được cài đặt
- Khóa API của Capsolver - Đăng ký tại đây
- Trình duyệt Chrome (cho chế độ không đầu)
Thiết lập khóa API dưới dạng biến môi trường:
bash
export CAPSOLVER_API_KEY="YOUR_API_KEY"
Kiến trúc tích hợp
┌─────────────────────────┐
│ Ứng dụng Go │
│ (trình duyệt go-rod) │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Trang web mục tiêu │
│ (với CAPTCHA) │
└───────────┬─────────────┘
│
CAPTCHA được phát hiện
│
▼
┌─────────────────────────┐
│ Trích xuất tham số │
│ (siteKey, URL, loại)│
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ API Capsolver │
│ createTask() │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Kiểm tra kết quả │
│ getTaskResult() │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Chèn token │
│ vào trang web │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ Tiếp tục crawling │
└─────────────────────────┘
Giải CAPTCHA reCAPTCHA v2 với CapSolver
reCAPTCHA v2 là loại CAPTCHA phổ biến nhất, hiển thị hộp kiểm "Tôi không phải là robot" hoặc các thách thức hình ảnh. Dưới đây là đoạn mã hoàn chỉnh để giải CAPTCHA reCAPTCHA v2:
go
// Giải CAPTCHA reCAPTCHA v2 - Ví dụ hoàn chỉnh
// Cách sử dụng: go run main.go
// Yêu cầu: biến môi trường CAPSOLVER_API_KEY
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
)
// Cấu hình
var (
CAPSOLVER_API_KEY = os.Getenv("CAPSOLVER_API_KEY")
CAPSOLVER_API = "https://api.capsolver.com"
)
// Các cấu trúc phản hồi API
type CreateTaskResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
TaskID string `json:"taskId"`
}
type GetTaskResultResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
Status string `json:"status"`
Solution struct {
GRecaptchaResponse string `json:"gRecaptchaResponse"`
} `json:"solution"`
}
type BalanceResponse struct {
ErrorID int `json:"errorId"`
Balance float64 `json:"balance"`
}
// CapsolverClient xử lý giao tiếp API
type CapsolverClient struct {
APIKey string
Client *http.Client
}
// NewCapsolverClient tạo một client Capsolver mới
func NewCapsolverClient(apiKey string) *CapsolverClient {
return &CapsolverClient{
APIKey: apiKey,
Client: &http.Client{Timeout: 120 * time.Second},
}
}
// GetBalance lấy số dư tài khoản
func (c *CapsolverClient) GetBalance() (float64, error) {
payload := map[string]string{"clientKey": c.APIKey}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getBalance", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result BalanceResponse
json.Unmarshal(body, &result)
if result.ErrorID != 0 {
return 0, fmt.Errorf("kiểm tra số dư thất bại")
}
return result.Balance, nil
}
// SolveRecaptchaV2 giải thách thức reCAPTCHA v2
func (c *CapsolverClient) SolveRecaptchaV2(websiteURL, siteKey string) (string, error) {
log.Printf("Tạo nhiệm vụ reCAPTCHA v2 cho %s", websiteURL)
// Tạo nhiệm vụ
task := map[string]interface{}{
"type": "ReCaptchaV2TaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("không thể tạo nhiệm vụ: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("lỗi API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Nhiệm vụ được tạo: %s", createResult.TaskID)
// Kiểm tra kết quả
for i := 0; i < 120; i++ {
result, err := c.getTaskResult(createResult.TaskID)
if err != nil {
return "", err
}
if result.Status == "ready" {
log.Printf("CAPTCHA được giải thành công!")
return result.Solution.GRecaptchaResponse, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("nhiệm vụ thất bại: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Đang chờ giải pháp... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("hết thời gian chờ đợi cho giải pháp")
}
func (c *CapsolverClient) getTaskResult(taskID string) (*GetTaskResultResponse, error) {
payload := map[string]string{
"clientKey": c.APIKey,
"taskId": taskID,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result GetTaskResultResponse
json.Unmarshal(body, &result)
return &result, nil
}
// extractSiteKey trích xuất site key từ HTML trang
func extractSiteKey(html string) string {
// Tìm thuộc tính data-sitekey
patterns := []string{
`data-sitekey="`,
`data-sitekey='`,
`"sitekey":"`,
`'sitekey':'`,
}
for _, pattern := range patterns {
if idx := strings.Index(html, pattern); idx != -1 {
start := idx + len(pattern)
end := start
for end < len(html) && html[end] != '"' && html[end] != '\'' {
end++
}
if end > start {
return html[start:end]
}
}
}
return ""
}
// injectRecaptchaToken chèn token đã giải vào trang
func injectRecaptchaToken(page *rod.Page, token string) error {
js := fmt.Sprintf(`
(function() {
// Thiết lập trường nhập response
var responseField = document.getElementById('g-recaptcha-response');
if (responseField) {
responseField.style.display = 'block';
responseField.value = '%s';
}
// Thiết lập cả các trường textarea ẩn
var textareas = document.querySelectorAll('textarea[name="g-recaptcha-response"]');
for (var i = 0; i < textareas.length; i++) {
textareas[i].value = '%s';
}
// Kích hoạt callback nếu tồn tại
if (typeof ___grecaptcha_cfg !== 'undefined') {
var clients = ___grecaptcha_cfg.clients;
for (var key in clients) {
var client = clients[key];
if (client) {
// Tìm và gọi callback
try {
var callback = client.callback ||
(client.Q && client.Q.callback) ||
(client.S && client.S.callback);
if (typeof callback === 'function') {
callback('%s');
}
} catch(e) {}
}
}
}
return true;
})();
`, token, token, token)
_, err := page.Eval(js)
return err
}
func main() {
// Kiểm tra khóa API
if CAPSOLVER_API_KEY == "" {
log.Fatal("Biến môi trường CAPSOLVER_API_KEY là bắt buộc")
}
// URL mục tiêu - trang demo reCAPTCHA của Google
targetURL := "https://www.google.com/recaptcha/api2/demo"
log.Println("==============================================")
log.Println("Katana + Capsolver - Hướng dẫn giải reCAPTCHA v2")
log.Println("==============================================")
// Khởi tạo client Capsolver
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Kiểm tra số dư
balance, err := client.GetBalance()
if err != nil {
log.Printf("Cảnh báo: Không thể kiểm tra số dư: %v", err)
} else {
log.Printf("Số dư Capsolver: $%.2f", balance)
}
// Khởi động trình duyệt
log.Println("Khởi động trình duyệt...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(true).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Điều hướng đến trang mục tiêu
log.Printf("Điều hướng đến: %s", targetURL)
page := browser.MustPage(targetURL)
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Lấy HTML trang và trích xuất site key
html := page.MustHTML()
// Kiểm tra reCAPTCHA
if !strings.Contains(html, "g-recaptcha") && !strings.Contains(html, "grecaptcha") {
log.Fatal("Không tìm thấy reCAPTCHA trên trang")
}
log.Println("Phát hiện reCAPTCHA!")
// Trích xuất site key
siteKey := extractSiteKey(html)
if siteKey == "" {
log.Fatal("Không thể trích xuất site key")
}
log.Printf("Site key: %s", siteKey)
// Giải CAPTCHA
log.Println("Giải CAPTCHA bằng Capsolver...")
token, err := client.SolveRecaptchaV2(targetURL, siteKey)
if err != nil {
log.Fatalf("Không thể giải CAPTCHA: %v", err)
}
log.Printf("Token nhận được: %s...", token[:50])
// Chèn token
log.Println("Chèn token vào trang...")
err = injectRecaptchaToken(page, token)
if err != nil {
log.Fatalf("Không thể chèn token: %v", err)
}
// Gửi biểu mẫu
log.Println("Gửi biểu mẫu...")
submitBtn := page.MustElement("#recaptcha-demo-submit")
submitBtn.MustClick()
// Chờ kết quả
time.Sleep(3 * time.Second)
// Kiểm tra kết quả
newHTML := page.MustHTML()
if strings.Contains(newHTML, "Verification Success") || strings.Contains(newHTML, "success") {
log.Println("==============================================")
log.Println("THÀNH CÔNG! CAPTCHA đã được giải và xác minh!")
log.Println("==============================================")
} else {
log.Println("Biểu mẫu đã được gửi - kiểm tra trang để xem kết quả")
}
// Lấy tiêu đề trang
title := page.MustEval(`document.title`).String()
log.Printf("Tiêu đề trang cuối cùng: %s", title)
}
Cài đặt và chạy
bash
# Tạo dự án
mkdir katana-recaptcha-v2
cd katana-recaptcha-v2
go mod init katana-recaptcha-v2
# Cài đặt phụ thuộc
go get github.com/go-rod/rod@latest
# Thiết lập khóa API
export CAPSOLVER_API_KEY="YOUR_API_KEY"
# Chạy
go run main.go
Giải CAPTCHA Cloudflare Turnstile với CapSolver
Cloudflare Turnstile là một loại CAPTCHA tập trung vào quyền riêng tư. Dưới đây là đoạn mã hoàn chỉnh:
go
// Giải CAPTCHA Cloudflare Turnstile - Ví dụ hoàn chỉnh
// Cách sử dụng: go run main.go
// Yêu cầu: biến môi trường CAPSOLVER_API_KEY
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"regexp"
"strings"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
)
// Cấu hình
var (
CAPSOLVER_API_KEY = os.Getenv("CAPSOLVER_API_KEY")
CAPSOLVER_API = "https://api.capsolver.com"
)
// Các cấu trúc phản hồi API
type CreateTaskResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
TaskID string `json:"taskId"`
}
type GetTaskResultResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
Status string `json:"status"`
Solution struct {
Token string `json:"token"`
} `json:"solution"`
}
type BalanceResponse struct {
ErrorID int `json:"errorId"`
Balance float64 `json:"balance"`
}
// CapsolverClient xử lý giao tiếp API
type CapsolverClient struct {
APIKey string
Client *http.Client
}
// NewCapsolverClient tạo một client Capsolver mới
func NewCapsolverClient(apiKey string) *CapsolverClient {
return &CapsolverClient{
APIKey: apiKey,
Client: &http.Client{Timeout: 120 * time.Second},
}
}
// GetBalance lấy số dư tài khoản
func (c *CapsolverClient) GetBalance() (float64, error) {
payload := map[string]string{"clientKey": c.APIKey}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getBalance", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result BalanceResponse
json.Unmarshal(body, &result)
return result.Balance, nil
}
// SolveTurnstile giải thách thức Cloudflare Turnstile
func (c *CapsolverClient) SolveTurnstile(websiteURL, siteKey string) (string, error) {
log.Printf("Tạo nhiệm vụ Turnstile cho %s", websiteURL)
// Tạo nhiệm vụ
task := map[string]interface{}{
"type": "AntiTurnstileTaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("không thể tạo nhiệm vụ: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("lỗi API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Nhiệm vụ được tạo: %s", createResult.TaskID)
// Kiểm tra kết quả
for i := 0; i < 120; i++ {
result, err := c.getTaskResult(createResult.TaskID)
if err != nil {
return "", err
}
if result.Status == "ready" {
log.Printf("CAPTCHA được giải thành công!")
return result.Solution.Token, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("nhiệm vụ thất bại: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Đang chờ giải pháp... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("hết thời gian chờ đợi cho giải pháp")
}
func (c *CapsolverClient) getTaskResult(taskID string) (*GetTaskResultResponse, error) {
payload := map[string]string{
"clientKey": c.APIKey,
"taskId": taskID,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result GetTaskResultResponse
json.Unmarshal(body, &result)
return &result, nil
}
// extractSiteKey trích xuất site key từ HTML trang
func extractSiteKey(html string) string {
// Tìm thuộc tính sitekey
patterns := []string{
`"sitekey":"`,
`'sitekey':'`,
`data-sitekey="`,
`data-sitekey='`,
}
for _, pattern := range patterns {
if idx := strings.Index(html, pattern); idx != -1 {
start := idx + len(pattern)
end := start
for end < len(html) && html[end] != '"' && html[end] != '\'' {
end++
}
if end > start {
return html[start:end]
}
}
}
return ""
}
// injectTurnstileToken chèn token đã giải vào trang
func injectTurnstileToken(page *rod.Page, token string) error {
js := fmt.Sprintf(`
(function() {
// Chèn token vào trường
var tokenField = document.querySelector("input[name='cf-turnstile-response']");
if (tokenField) {
tokenField.value = '%s';
}
// Gửi sự kiện thay đổi
var event = new Event('input', { bubbles: true });
tokenField.dispatchEvent(event);
// Gửi sự kiện thay đổi cho các trường khác
var otherFields = document.querySelectorAll("input[name='cf-turnstile-response']");
for (var i = 0; i < otherFields.length; i++) {
otherFields[i].value = '%s';
otherFields[i].dispatchEvent(event);
}
return true;
})();
`, token, token)
_, err := page.Eval(js)
return err
}
func main() {
// Kiểm tra khóa API
if CAPSOLVER_API_KEY == "" {
log.Fatal("Biến môi trường CAPSOLVER_API_KEY là bắt buộc")
}
// URL mục tiêu - trang demo Cloudflare Turnstile
targetURL := "https://cf-turnstile-demo.com"
log.Println("==============================================")
log.Println("Katana + Capsolver - Hướng dẫn giải Cloudflare Turnstile")
log.Println("==============================================")
// Khởi tạo client Capsolver
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Kiểm tra số dư
balance, err := client.GetBalance()
if err != nil {
log.Printf("Cảnh báo: Không thể kiểm tra số dư: %v", err)
} else {
log.Printf("Số dư Capsolver: $%.2f", balance)
}
// Khởi động trình duyệt
log.Println("Khởi động trình duyệt...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(true).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Điều hướng đến trang mục tiêu
log.Printf("Điều hướng đến: %s", targetURL)
page := browser.MustPage(targetURL)
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Lấy HTML trang và trích xuất site key
html := page.MustHTML()
// Kiểm tra Cloudflare Turnstile
if !strings.Contains(html, "cf-turnstile") {
log.Fatal("Không tìm thấy Cloudflare Turnstile trên trang")
}
log.Println("Phát hiện Cloudflare Turnstile!")
// Trích xuất site key
siteKey := extractSiteKey(html)
if siteKey == "" {
log.Fatal("Không thể trích xuất site key")
}
log.Printf("Site key: %s", siteKey)
// Giải CAPTCHA
log.Println("Giải CAPTCHA bằng Capsolver...")
token, err := client.SolveTurnstile(targetURL, siteKey)
if err != nil {
log.Fatalf("Không thể giải CAPTCHA: %v", err)
}
log.Printf("Token nhận được: %s...", token[:50])
// Chèn token
log.Println("Chèn token vào trang...")
err = injectTurnstileToken(page, token)
if err != nil {
log.Fatalf("Không thể chèn token: %v", err)
}
// Gửi biểu mẫu
log.Println("Gửi biểu mẫu...")
submitBtn := page.MustElement("button[type='submit']")
submitBtn.MustClick()
// Chờ kết quả
time.Sleep(3 * time.Second)
// Kiểm tra kết quả
newHTML := page.MustHTML()
if strings.Contains(newHTML, "success") || strings.Contains(newHTML, "verified") {
log.Println("==============================================")
log.Println("THÀNH CÔNG! Cloudflare Turnstile đã được giải và xác minh!")
log.Println("==============================================")
} else {
log.Println("Biểu mẫu đã được gửi - kiểm tra trang để xem kết quả")
}
// Lấy tiêu đề trang
title := page.MustEval(`document.title`).String()
log.Printf("Tiêu đề trang cuối cùng: %s", title)
}
"websiteKey": siteKey,
}
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("không thể tạo nhiệm vụ: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("lỗi API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Nhiệm vụ được tạo: %s", createResult.TaskID)
// Kiểm tra kết quả
for i := 0; i < 120; i++ {
result, err := c.getTaskResult(createResult.TaskID)
if err != nil {
return "", err
}
if result.Status == "ready" {
log.Printf("Đã giải thành công!")
return result.Solution.Token, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("nhiệm vụ thất bại: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Đang chờ giải pháp... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("hết thời gian chờ giải pháp")
}
func (c *CapsolverClient) getTaskResult(taskID string) (*GetTaskResultResponse, error) {
payload := map[string]string{
"clientKey": c.APIKey,
"taskId": taskID,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result GetTaskResultResponse
json.Unmarshal(body, &result)
return &result, nil
}
// extractTurnstileSiteKey trích xuất site key từ HTML trang
func extractTurnstileSiteKey(html string) string {
// Mẫu 1: thuộc tính data-sitekey trên div cf-turnstile
patterns := []string{
cf-turnstile[^>]*data-sitekey=['"]([^'"]+)['"],
data-sitekey=['"]([^'"]+)['"][^>]*class=['"][^'"]*cf-turnstile,
turnstile\.render\s*\([^,]+,\s*\{[^}]*sitekey['":\s]+['"]([^'"]+)['"],
sitekey['":\s]+['"]([0-9a-zA-Z_-]+)['"],
}
for _, pattern := range patterns {
re := regexp.MustCompile(pattern)
matches := re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
}
return ""
}
// injectTurnstileToken chèn token đã giải vào trang
func injectTurnstileToken(page *rod.Page, token string) error {
js := fmt.Sprintf(`
(function() {
// Thiết lập trường cf-turnstile-response
var responseField = document.querySelector('[name="cf-turnstile-response"]');
if (responseField) {
responseField.value = '%s';
}
// Thử tìm bằng ID
var byId = document.getElementById('cf-turnstile-response');
if (byId) {
byId.value = '%s';
}
// Tạo trường ẩn nếu cần
if (!responseField && !byId) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'cf-turnstile-response';
input.value = '%s';
var form = document.querySelector('form');
if (form) {
form.appendChild(input);
}
}
// Thử kích hoạt callback
if (window.turnstile && window.turnstileCallback) {
window.turnstileCallback('%s');
}
return true;
})();
`, token, token, token, token)
_, err := page.Eval(js)
return err
}
func main() {
// Kiểm tra API key
if CAPSOLVER_API_KEY == "" {
log.Fatal("Biến môi trường CAPSOLVER_API_KEY là bắt buộc")
}
// URL mục tiêu - Thay thế bằng trang sử dụng Cloudflare Turnstile
targetURL := "https://example.com"
log.Println("==============================================")
log.Println("Katana + Capsolver - Demo Turnstile")
log.Println("==============================================")
// Khởi tạo client Capsolver
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Kiểm tra số dư
balance, err := client.GetBalance()
if err != nil {
log.Printf("Cảnh báo: Không thể kiểm tra số dư: %v", err)
} else {
log.Printf("Số dư Capsolver: $%.2f", balance)
}
// Khởi chạy trình duyệt
log.Println("Đang khởi chạy trình duyệt...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(true).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Điều hướng đến mục tiêu
log.Printf("Đang điều hướng đến: %s", targetURL)
page := browser.MustPage(targetURL)
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Lấy HTML trang
html := page.MustHTML()
// Kiểm tra Turnstile
if !strings.Contains(html, "cf-turnstile") && !strings.Contains(html, "turnstile") {
log.Println("Không tìm thấy Turnstile trên trang")
log.Println("Lưu ý: Thay thế targetURL bằng trang sử dụng Cloudflare Turnstile")
return
}
log.Println("Phát hiện Cloudflare Turnstile!")
// Trích xuất site key
siteKey := extractTurnstileSiteKey(html)
if siteKey == "" {
log.Fatal("Không thể trích xuất site key")
}
log.Printf("Site key: %s", siteKey)
// Giải Turnstile
log.Println("Giải Turnstile bằng Capsolver...")
token, err := client.SolveTurnstile(targetURL, siteKey)
if err != nil {
log.Fatalf("Không thể giải Turnstile: %v", err)
}
log.Printf("Token nhận được: %s...", token[:min(50, len(token))])
// Chèn token
log.Println("Đang chèn token vào trang...")
err = injectTurnstileToken(page, token)
if err != nil {
log.Fatalf("Không thể chèn token: %v", err)
}
log.Println("==============================================")
log.Println("THÀNH CÔNG! Token Turnstile đã được chèn!")
log.Println("==============================================")
// Lấy tiêu đề trang
title := page.MustEval(`document.title`).String()
log.Printf("Tiêu đề trang: %s", title)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
### Các điểm chính của Turnstile
1. **Loại nhiệm vụ**: Sử dụng `AntiTurnstileTaskProxyLess`
2. **Trường phản hồi**: Turnstile sử dụng `cf-turnstile-response` thay vì `g-recaptcha-response`
3. **Giải nhanh hơn**: Turnstile thường giải nhanh hơn reCAPTCHA (1-10 giây)
4. **Trường token**: Giải pháp nằm ở `solution.token` thay vì `solution.gRecaptchaResponse`
---
## Crawler CAPTCHA tổng quát
Dưới đây là một crawler hoàn chỉnh, có thể xử lý tất cả các loại CAPTCHA tự động:
```go
// Crawler CAPTCHA tổng quát - Ví dụ đầy đủ
// Tự động phát hiện và giải reCAPTCHA v2 và Turnstile
// Cách sử dụng: go run main.go -url "https://example.com"
// Yêu cầu: Biến môi trường CAPSOLVER_API_KEY
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"regexp"
"strings"
"time"
"github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher"
)
// ============================================
// Cấu hình
// ============================================
var (
CAPSOLVER_API_KEY = os.Getenv("CAPSOLVER_API_KEY")
CAPSOLVER_API = "https://api.capsolver.com"
)
// Loại CAPTCHA đại diện cho các loại CAPTCHA khác nhau
type CaptchaType string
const (
RecaptchaV2 CaptchaType = "recaptcha_v2"
Turnstile CaptchaType = "turnstile"
Unknown CaptchaType = "unknown"
)
// CaptchaInfo chứa thông tin CAPTCHA đã trích xuất
type CaptchaInfo struct {
Type CaptchaType
SiteKey string
}
// ============================================
// Định nghĩa kiểu API
// ============================================
type CreateTaskResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
TaskID string `json:"taskId"`
}
type GetTaskResultResponse struct {
ErrorID int `json:"errorId"`
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
Status string `json:"status"`
Solution struct {
GRecaptchaResponse string `json:"gRecaptchaResponse"`
Token string `json:"token"`
} `json:"solution"`
}
type BalanceResponse struct {
ErrorID int `json:"errorId"`
Balance float64 `json:"balance"`
}
// ============================================
// Client Capsolver
// ============================================
type CapsolverClient struct {
APIKey string
Client *http.Client
}
func NewCapsolverClient(apiKey string) *CapsolverClient {
return &CapsolverClient{
APIKey: apiKey,
Client: &http.Client{Timeout: 120 * time.Second},
}
}
func (c *CapsolverClient) GetBalance() (float64, error) {
payload := map[string]string{"clientKey": c.APIKey}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getBalance", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var result BalanceResponse
json.Unmarshal(body, &result)
return result.Balance, nil
}
func (c *CapsolverClient) Solve(info *CaptchaInfo, websiteURL string) (string, error) {
switch info.Type {
case RecaptchaV2:
return c.solveRecaptchaV2(websiteURL, info.SiteKey)
case Turnstile:
return c.solveTurnstile(websiteURL, info.SiteKey)
default:
return "", fmt.Errorf("loại CAPTCHA không hỗ trợ: %s", info.Type)
}
}
func (c *CapsolverClient) solveRecaptchaV2(websiteURL, siteKey string) (string, error) {
task := map[string]interface{}{
"type": "ReCaptchaV2TaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
return c.solveTask(task, "recaptcha")
}
func (c *CapsolverClient) solveTurnstile(websiteURL, siteKey string) (string, error) {
task := map[string]interface{}{
"type": "AntiTurnstileTaskProxyLess",
"websiteURL": websiteURL,
"websiteKey": siteKey,
}
return c.solveTask(task, "turnstile")
}
func (c *CapsolverClient) solveTask(task map[string]interface{}, tokenType string) (string, error) {
// Tạo nhiệm vụ
payload := map[string]interface{}{
"clientKey": c.APIKey,
"task": task,
}
jsonData, _ := json.Marshal(payload)
resp, err := c.Client.Post(CAPSOLVER_API+"/createTask", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", fmt.Errorf("không thể tạo nhiệm vụ: %w", err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var createResult CreateTaskResponse
json.Unmarshal(body, &createResult)
if createResult.ErrorID != 0 {
return "", fmt.Errorf("lỗi API: %s - %s", createResult.ErrorCode, createResult.ErrorDescription)
}
log.Printf("Nhiệm vụ được tạo: %s", createResult.TaskID)
// Kiểm tra kết quả
for i := 0; i < 120; i++ {
getPayload := map[string]string{
"clientKey": c.APIKey,
"taskId": createResult.TaskID,
}
jsonData, _ := json.Marshal(getPayload)
resp, err := c.Client.Post(CAPSOLVER_API+"/getTaskResult", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return "", err
}
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var result GetTaskResultResponse
json.Unmarshal(body, &result)
if result.Status == "ready" {
if tokenType == "turnstile" {
return result.Solution.Token, nil
}
return result.Solution.GRecaptchaResponse, nil
}
if result.Status == "failed" {
return "", fmt.Errorf("nhiệm vụ thất bại: %s", result.ErrorDescription)
}
if i%10 == 0 {
log.Printf("Đang chờ giải pháp... (%ds)", i)
}
time.Sleep(1 * time.Second)
}
return "", fmt.Errorf("hết thời gian chờ giải pháp")
}
// ============================================
// Phát hiện CAPTCHA
// ============================================
func DetectCaptcha(html string) *CaptchaInfo {
// Kiểm tra reCAPTCHA v2 (checkbox)
if strings.Contains(html, "g-recaptcha") {
siteKey := extractDataSiteKey(html, "g-recaptcha")
if siteKey != "" {
return &CaptchaInfo{
Type: RecaptchaV2,
SiteKey: siteKey,
}
}
}
// Kiểm tra Cloudflare Turnstile
if strings.Contains(html, "cf-turnstile") || strings.Contains(html, "challenges.cloudflare.com/turnstile") {
siteKey := extractDataSiteKey(html, "cf-turnstile")
if siteKey != "" {
return &CaptchaInfo{
Type: Turnstile,
SiteKey: siteKey,
}
}
}
return nil
}
func extractDataSiteKey(html, className string) string {
pattern := fmt.Sprintf(`class=['"][^'"]*%s[^'"]*['"][^>]*data-sitekey=['"]([^'"]+)['"]`, className)
re := regexp.MustCompile(pattern)
matches := re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
// Mẫu thay thế
pattern = fmt.Sprintf(`data-sitekey=['"]([^'"]+)['"][^>]*class=['"][^'"]*%s`, className)
re = regexp.MustCompile(pattern)
matches = re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
// Mẫu chung
re = regexp.MustCompile(`data-sitekey=['"]([^'"]+)['"]`)
matches = re.FindStringSubmatch(html)
if len(matches) > 1 {
return matches[1]
}
return ""
}
// ============================================
// Chèn token
// ============================================
func InjectToken(page *rod.Page, token string, captchaType CaptchaType) error {
var js string
switch captchaType {
case RecaptchaV2:
js = fmt.Sprintf(`
(function() {
var responseField = document.getElementById('g-recaptcha-response');
if (responseField) {
responseField.style.display = 'block';
responseField.value = '%s';
}
var textareas = document.querySelectorAll('textarea[name="g-recaptcha-response"]');
for (var i = 0; i < textareas.length; i++) {
textareas[i].value = '%s';
}
if (typeof ___grecaptcha_cfg !== 'undefined') {
var clients = ___grecaptcha_cfg.clients;
for (var key in clients) {
var client = clients[key];
if (client) {
try {
var callback = client.callback ||
(client.Q && client.Q.callback) ||
(client.S && client.S.callback);
if (typeof callback === 'function') {
callback('%s');
}
} catch(e) {}
}
}
}
return true;
})();
`, token, token, token)
case Turnstile:
js = fmt.Sprintf(`
(function() {
var responseField = document.querySelector('[name="cf-turnstile-response"]');
if (responseField) {
responseField.value = '%s';
}
if (!responseField) {
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'cf-turnstile-response';
input.value = '%s';
var form = document.querySelector('form');
if (form) form.appendChild(input);
}
if (window.turnstile && window.turnstileCallback) {
window.turnstileCallback('%s');
}
return true;
})();
`, token, token, token)
default:
return fmt.Errorf("loại CAPTCHA không hỗ trợ: %s", captchaType)
}
_, err := page.Eval(js)
return err
}
// ============================================
// Crawler
// ============================================
type CrawlResult struct {
URL string
Title string
Success bool
CaptchaFound bool
CaptchaType CaptchaType
CaptchaSolved bool
Error string
}
func Crawl(browser *rod.Browser, client *CapsolverClient, targetURL string) *CrawlResult {
result := &CrawlResult{
URL: targetURL,
Thất bại: false,
}
// Điều hướng đến mục tiêu
log.Printf("Đang điều hướng đến: %s", targetURL)
page := browser.MustPage(targetURL)
defer page.MustClose()
page.MustWaitLoad()
time.Sleep(2 * time.Second)
// Lấy HTML trang
html := page.MustHTML()
// Phát hiện CAPTCHA
captchaInfo := DetectCaptcha(html)
if captchaInfo != nil && captchaInfo.Type != Unknown {
result.CaptchaFound = true
result.CaptchaType = captchaInfo.Type
log.Printf("Phát hiện CAPTCHA: %s (siteKey: %s)", captchaInfo.Type, captchaInfo.SiteKey)
// Giải CAPTCHA
log.Println("Giải CAPTCHA bằng Capsolver...")
token, err := client.Solve(captchaInfo, targetURL)
if err != nil {
result.Error = fmt.Sprintf("không thể giải CAPTCHA: %v", err)
log.Printf("Lỗi: %s", result.Error)
return result
}
log.Printf("Token nhận được: %s...", token[:min(50, len(token))])
// Chèn token
log.Println("Chèn token...")
err = InjectToken(page, token, captchaInfo.Type)
if err != nil {
result.Error = fmt.Sprintf("không thể chèn token: %v", err)
log.Printf("Lỗi: %s", result.Error)
return result
}
result.CaptchaSolved = true
log.Println("Chèn token thành công!")
// Thử gửi biểu mẫu
submitForm(page)
time.Sleep(3 * time.Second)
} else {
log.Println("Không phát hiện CAPTCHA trên trang")
}
// Lấy thông tin trang cuối
result.Title = page.MustEval(`document.title`).String()
result.Success = true
return result
}
func submitForm(page *rod.Page) {
selectors := []string{
"button[type='submit']",
"input[type='submit']",
"#recaptcha-demo-submit",
".submit-button",
}
for _, selector := range selectors {
js := fmt.Sprintf(`
(function() {
var btn = document.querySelector('%s');
if (btn && btn.offsetParent !== null) {
btn.click();
return true;
}
return false;
})();
`, selector)
result := page.MustEval(js)
if result.Bool() {
log.Printf("Đã nhấp vào nút gửi: %s", selector)
return
}
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
// ============================================
// Chính
// ============================================
func main() {
// Phân tích tham số
targetURL := flag.String("url", "https://www.google.com/recaptcha/api2/demo", "URL mục tiêu để quét")
headless := flag.Bool("headless", true, "Chạy trình duyệt ở chế độ không giao diện")
checkBalance := flag.Bool("balance", false, "Chỉ kiểm tra số dư tài khoản")
flag.Parse()
// Kiểm tra khóa API
if CAPSOLVER_API_KEY == "" {
log.Fatal("Biến môi trường CAPSOLVER_API_KEY là bắt buộc")
}
log.Println("==============================================")
log.Println("Katana + Capsolver - Crawler CAPTCHA toàn diện")
log.Println("==============================================")
// Khởi tạo client
client := NewCapsolverClient(CAPSOLVER_API_KEY)
// Kiểm tra số dư
balance, err := client.GetBalance()
if err != nil {
log.Printf("Cảnh báo: Không thể kiểm tra số dư: %v", err)
} else {
log.Printf("Số dư Capsolver: $%.2f", balance)
}
if *checkBalance {
return
}
// Khởi động trình duyệt
log.Println("Đang khởi động trình duyệt...")
path, _ := launcher.LookPath()
u := launcher.New().Bin(path).Headless(*headless).MustLaunch()
browser := rod.New().ControlURL(u).MustConnect()
defer browser.MustClose()
// Quét
result := Crawl(browser, client, *targetURL)
// Xuất kết quả
log.Println("==============================================")
log.Println("KẾT QUẢ QUÉT")
log.Println("==============================================")
log.Printf("URL: %s", result.URL)
log.Printf("Tiêu đề: %s", result.Title)
log.Printf("Thành công: %v", result.Success)
log.Printf("Phát hiện CAPTCHA: %v", result.CaptchaFound)
if result.CaptchaFound {
log.Printf("Loại CAPTCHA: %s", result.CaptchaType)
log.Printf("CAPTCHA đã giải: %v", result.CaptchaSolved)
}
if result.Error != "" {
log.Printf("Lỗi: %s", result.Error)
}
log.Println("==============================================")
}
Cách sử dụng
bash
# Tạo dự án
mkdir katana-universal-crawler
cd katana-universal-crawler
go mod init katana-universal-crawler
# Cài đặt phụ thuộc
go get github.com/go-rod/rod@latest
# Thiết lập khóa API
export CAPSOLVER_API_KEY="KHÓA_API_CỦA_BẠN"
# Chạy với mặc định (demo reCAPTCHA v2)
go run main.go
# Chạy với URL tùy chỉnh
go run main.go -url "https://example.com"
# Kiểm tra số dư
go run main.go -balance
# Chạy với trình duyệt hiển thị
go run main.go -headless=false
Nguyên tắc tốt nhất
1. Tối ưu hiệu suất
- Sử dụng loại nhiệm vụ ProxyLess:
ReCaptchaV2TaskProxyLesssử dụng proxy nội bộ của Capsolver để giải nhanh hơn - Xử lý song song: Bắt đầu giải CAPTCHA khi các phần khác của trang đang tải
- Lưu trữ token: Token reCAPTCHA có hiệu lực trong ~2 phút; lưu trữ khi có thể
2. Quản lý chi phí
- Phát hiện trước khi giải: Chỉ gọi Capsolver khi CAPTCHA thực sự tồn tại
- Xác minh site key: Đảm bảo các khóa được trích xuất là hợp lệ trước khi gọi API
- Theo dõi sử dụng: Theo dõi các cuộc gọi API để quản lý chi phí hiệu quả
3. Xử lý lỗi
go
func SolveWithRetry(client *CapsolverClient, info *CaptchaInfo, url string, maxRetries int) (string, error) {
var lastErr error
for i := 0; i < maxRetries; i++ {
token, err := client.Solve(info, url)
if err == nil {
return token, nil
}
lastErr = err
log.Printf("Lần thử %d thất bại: %v", i+1, err)
// Chậm dần theo cấp số nhân
time.Sleep(time.Duration(i+1) * time.Second)
}
return "", fmt.Errorf("thất bại sau %d lần thử: %w", maxRetries, lastErr)
}
4. Hạn chế tốc độ
Thiết lập khoảng thời gian chờ giữa các yêu cầu để tránh bị phát hiện:
go
type RateLimiter struct {
requests int
interval time.Duration
lastRequest time.Time
mu sync.Mutex
}
func (r *RateLimiter) Wait() {
r.mu.Lock()
defer r.mu.Unlock()
elapsed := time.Since(r.lastRequest)
if elapsed < r.interval {
time.Sleep(r.interval - elapsed)
}
r.lastRequest = time.Now()
}
Giải quyết sự cố
Lỗi phổ biến
| Lỗi | Nguyên nhân | Giải pháp |
|---|---|---|
ERROR_ZERO_BALANCE |
Số dư không đủ | Nạp tiền vào tài khoản Capsolver |
ERROR_CAPTCHA_UNSOLVABLE |
Khóa site không hợp lệ | Kiểm tra logic trích xuất |
ERROR_INVALID_TASK_DATA |
Thiếu tham số | Kiểm tra cấu trúc nhiệm vụ |
context deadline exceeded |
Thời gian chờ hết | Tăng thời gian chờ hoặc kiểm tra mạng |
Mẹo gỡ lỗi
- Bật chế độ hiển thị trình duyệt: Thiết lập
Headless(false)để xem những gì đang xảy ra - Ghi nhật ký giao thức mạng: Giám sát các yêu cầu để xác định vấn đề
- Lưu ảnh chụp màn hình: Lưu trạng thái trang để gỡ lỗi
- Xác minh token: Ghi định dạng token trước khi chèn
Câu hỏi thường gặp
Câu hỏi: Tôi có thể sử dụng Katana mà không cần chế độ headless cho trang CAPTCHA không?
Trả lời: Không, trang CAPTCHA yêu cầu rendering JavaScript, chỉ hoạt động ở chế độ headless.
Câu hỏi: Token CAPTCHA có hiệu lực bao lâu?
Trả lời: Token reCAPTCHA: ~2 phút. Turnstile: tùy theo cấu hình.
Câu hỏi: Thời gian giải trung bình là bao nhiêu?
Trả lời: reCAPTCHA v2: 5-15 giây, Turnstile: 1-10 giây.
Câu hỏi: Tôi có thể sử dụng proxy riêng của mình không?
Trả lời: Có, sử dụng loại nhiệm vụ không có "ProxyLess" và cung cấp cấu hình proxy.
Kết luận
Việc tích hợp Capsolver với Katana cho phép xử lý CAPTCHA mạnh mẽ cho nhu cầu quét web của bạn. Các đoạn mã hoàn chỉnh ở trên có thể được sao chép trực tiếp và sử dụng với các dự án Go của bạn.
Sẵn sàng bắt đầu? Đăng ký Capsolver và tăng tốc crawler của bạn!
💡 Ưu đãi đặc biệt cho người dùng Katana:
Nhân dịp tích hợp này, chúng tôi đang cung cấp mã ưu đãi 6% — Katana cho tất cả người dùng Capsolver đăng ký qua hướng dẫn này.
Chỉ cần nhập mã trong phần nạp tiền trên Dashboard để nhận thêm 6% số dư ngay lập tức.
12. Tài liệu tham khảo
- 12.1. Kho lưu trữ Katana trên GitHub
- 12.2. Tài liệu Katana
- 12.3. Tài liệu Capsolver
- 12.4. Tự động hóa trình duyệt Go Rod
Tuyên bố Tuân thủ: Thông tin được cung cấp trên blog này chỉ mang tính chất tham khảo. CapSolver cam kết tuân thủ tất cả các luật và quy định hiện hành. Việc sử dụng mạng lưới CapSolver cho các hoạt động bất hợp pháp, gian lận hoặc lạm dụng là hoàn toàn bị cấm và sẽ bị điều tra. Các giải pháp giải captcha của chúng tôi nâng cao trải nghiệm người dùng trong khi đảm bảo tuân thủ 100% trong việc giúp giải quyết các khó khăn về captcha trong quá trình thu thập dữ liệu công khai. Chúng tôi khuyến khích việc sử dụng dịch vụ của chúng tôi một cách có trách nhiệm. Để biết thêm thông tin, vui lòng truy cập Điều khoản Dịch vụ và Chính sách Quyền riêng tư.
Thêm

Giải quyết lỗi 403 Truy cập bị từ chối khi quét trang web bằng Python
Học cách vượt qua các lỗi 403 Forbidden khi quét trang web bằng Python. Hướng dẫn này bao gồm quay vòng IP, giả mạo user-agent, kiểm soát tần suất yêu cầu, xử lý xác thực và sử dụng trình duyệt không đầu để vượt qua các hạn chế truy cập và tiếp tục quét web thành công.

Sora Fujimoto
13-Jan-2026

Cách giải Captcha trong Agno với tích hợp CapSolver
Học cách tích hợp CapSolver với Agno để giải các bài kiểm tra reCAPTCHA v2/v3, Cloudflare Turnstile và WAF trong các tác nhân AI tự động. Bao gồm các ví dụ Python thực tế cho việc quét web và tự động hóa.

Anh Tuan
13-Jan-2026

Tích hợp Katana với CapSolver: Giải CAPTCHA tự động cho quét web
Học cách tích hợp Katana với Capsolver để giải tự động reCAPTCHA v2 và Cloudflare Turnstile trong quét không cần giao diện.

Anh Tuan
12-Jan-2026

Top Thư viện Thu thập dữ liệu web 2026
Khám phá các thư viện quét web Python tốt nhất năm 2026. So sánh tính năng, dễ sử dụng và hiệu suất cho nhu cầu trích xuất dữ liệu của bạn. Bao gồm các phân tích chuyên gia và Câu hỏi thường gặp.

Anh Tuan
12-Jan-2026

Tích hợp Crawlab với CapSolver: Giải CAPTCHA tự động cho việc quét dữ liệu phân tán
Học cách tích hợp CapSolver với Crawlab để giải quyết reCAPTCHA và Cloudflare Turnstile ở quy mô lớn.

Anh Tuan
09-Jan-2026

Những Công Cụ Gỡ Dữ Liệu Trí Tuệ Nhân Tạo Tốt Nhất Bạn Phải Biết Năm 2026
Khám phá các lựa chọn công cụ quét AI tốt nhất năm 2026. Chúng tôi so sánh các công cụ quét web AI hàng đầu, bao gồm Bright Data, Crawl4AI và Browse AI, với giá cả cụ thể để giúp bạn thành thạo việc trích xuất dữ liệu tự động và giải quyết các thách thức bảo mật.

Aloísio Vítor
07-Jan-2026


