Hướng Dẫn Thực Hành

Chèn token GeeTest vào framework tự động hóa trình duyệt

Script automation của bạn gọi CaptchaAI, nhận về kết quả GeeTest v3 hợp lệ, nhưng form vẫn báo lỗi xác thực? Nguyên nhân gần như luôn là thiếu một trong ba giá trị bắt buộc.

GeeTest v3 không trả về một token duy nhất như reCAPTCHA hay Turnstile — nó trả về bộ ba geetest_challenge, geetest_validate, geetest_seccode, và cả ba phải được đặt đúng chỗ trang web mong đợi: trường ẩn trong form, callback JavaScript của widget, hoặc payload XHR. Bài này đi thẳng vào cách tiêm chính xác cho từng framework phổ biến.

Bạn cần tiêm những gì?

Ba trường bắt buộc, đúng tên, đúng thứ tự trang web mong đợi:

  • geetest_challenge — theo dõi phiên giải
  • geetest_validate — bằng chứng challenge đã được giải
  • geetest_seccode — hash chống giả mạo

CaptchaAI trả về cả ba trong một lần gọi:

{
  "geetest_challenge": "a1b2c3d4e5...modified_challenge",
  "geetest_validate": "abc123def456_validate",
  "geetest_seccode": "abc123def456_validate|jordan"
}

Tất cả ba giá trị phải được thêm vào. Thiếu bất kỳ cái nào sẽ gây ra lỗi xác minh.

Bước 1: Trích xuất tham số và giải bằng CaptchaAI

Bước này giống nhau ở mọi framework — lấy gtchallenge từ trang, rồi gửi cho CaptchaAI giải:

import requests
import time

def solve_geetest(gt, challenge, page_url):
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_API_KEY",
        "method": "geetest",
        "gt": gt,
        "challenge": challenge,
        "pageurl": page_url,
        "json": 1
    })
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]  # Returns dict with three values
    raise TimeoutError("GeeTest solve timed out")

Mẹo: gộp bước giải và bước tiêm vào cùng một hàm/luồng xử lý — khoảng cách càng ngắn, tỷ lệ challenge còn hiệu lực khi tiêm càng cao.

Bước 2: Tiêm token vào Playwright (Python)

Bắt gt và challenge

Playwright cho phép nghe response ngay khi trang gọi API đăng ký GeeTest, nên bạn không cần đọc DOM để lấy tham số:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    # Capture GeeTest registration response
    geetest_data = {}
    def capture_geetest(response):
        if "register" in response.url and response.status == 200:
            try:
                data = response.json()
                if "gt" in data and "challenge" in data:
                    geetest_data.update(data)
            except Exception:
                pass

    page.on("response", capture_geetest)
    page.goto("https://staging.example.com/qa-login")
    page.wait_for_selector(".geetest_holder")

    gt = geetest_data["gt"]
    challenge = geetest_data["challenge"]

Tiêm kết quả vào form

Với đa số trang, chỉ cần tạo (hoặc cập nhật) ba trường ẩn rồi submit form như bình thường:

# Solve with CaptchaAI
solution = solve_geetest(gt, challenge, page.url)

# Method 1: Set hidden form fields
page.evaluate(f"""
    const fields = {{
        'geetest_challenge': '{solution["geetest_challenge"]}',
        'geetest_validate': '{solution["geetest_validate"]}',
        'geetest_seccode': '{solution["geetest_seccode"]}'
    }};
    for (const [name, value] of Object.entries(fields)) {{
        let input = document.querySelector(`input[name="${{name}}"]`);
        if (!input) {{
            input = document.createElement('input');
            input.type = 'hidden';
            input.name = name;
            document.querySelector('form').appendChild(input);
        }}
        input.value = value;
    }}
""")

# Submit the form
page.click("#submit-button")

Phương án 2: kích hoạt callback GeeTest

Một số trang không đọc trường ẩn mà chờ callback JavaScript của chính widget GeeTest báo thành công:

page.evaluate(f"""
    // Find the GeeTest captcha object
    if (window.captchaObj) {{
        // Simulate a successful solve
        const result = {{
            geetest_challenge: '{solution["geetest_challenge"]}',
            geetest_validate: '{solution["geetest_validate"]}',
            geetest_seccode: '{solution["geetest_seccode"]}'
        }};

        // Override getValidate to return our solution
        window.captchaObj.getValidate = function() {{ return result; }};

        // Trigger the success callback
        const successEvent = new Event('geetest_success');
        document.dispatchEvent(successEvent);
    }}
""")

Chọn phương án nào phụ thuộc vào cách trang xử lý widget:

  • Trang đọc giá trị từ <form> khi submit → dùng Phương án 1 (trường ẩn).
  • Trang chờ sự kiện geetest_success hoặc gọi getValidate() → dùng Phương án 2 (callback).

Bước 3: Tiêm token vào Puppeteer (JavaScript)

Logic giống Playwright — bắt response chứa gt/challenge, giải, rồi tiêm ba trường ẩn:

const puppeteer = require('puppeteer');

async function solveAndInject() {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();

  // Capture GeeTest params
  let gt, challenge;
  page.on('response', async (response) => {
    if (response.url().includes('register') && response.status() === 200) {
      try {
        const data = await response.json();
        if (data.gt && data.challenge) {
          gt = data.gt;
          challenge = data.challenge;
        }
      } catch (e) {}
    }
  });

  await page.goto('https://staging.example.com/qa-login');
  await page.waitForSelector('.geetest_holder');

  // Solve with CaptchaAI (implementation from earlier)
  const solution = await solveCaptcha(gt, challenge, page.url());

  // Inject the three values
  await page.evaluate((sol) => {
    // Set hidden inputs
    const form = document.querySelector('form');
    ['geetest_challenge', 'geetest_validate', 'geetest_seccode'].forEach(name => {
      let input = document.querySelector(`input[name="${name}"]`);
      if (!input) {
        input = document.createElement('input');
        input.type = 'hidden';
        input.name = name;
        form.appendChild(input);
      }
      input.value = sol[name];
    });
  }, solution);

  await page.click('#submit-button');
}

Bước 4: Tiêm token vào Selenium (Python)

Selenium không có API "on response" tiện dụng như Playwright/Puppeteer, nên bạn thường đọc gt/challenge trực tiếp từ thuộc tính data-* trên DOM rồi tiêm bằng execute_script:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://staging.example.com/qa-login")

# Wait for GeeTest widget
WebDriverWait(driver, 15).until(
    EC.presence_of_element_located((By.CLASS_NAME, "geetest_holder"))
)

# Extract gt and challenge from the page
gt = driver.execute_script(
    "return document.querySelector('[data-gt]')?.dataset.gt"
)
challenge = driver.execute_script(
    "return document.querySelector('[data-challenge]')?.dataset.challenge"
)

# Solve with CaptchaAI
solution = solve_geetest(gt, challenge, driver.current_url)

# Inject via JavaScript
driver.execute_script(f"""
    var fields = {{
        'geetest_challenge': '{solution["geetest_challenge"]}',
        'geetest_validate': '{solution["geetest_validate"]}',
        'geetest_seccode': '{solution["geetest_seccode"]}'
    }};
    var form = document.querySelector('form');
    for (var name in fields) {{
        var input = document.querySelector('input[name="' + name + '"]');
        if (!input) {{
            input = document.createElement('input');
            input.type = 'hidden';
            input.name = name;
            form.appendChild(input);
        }}
        input.value = fields[name];
    }}
""")

driver.find_element(By.ID, "submit-button").click()

Xử lý submit qua XHR

Không phải trang nào cũng dùng form POST truyền thống. Nhiều trang gửi kết quả GeeTest qua XHR/fetch, nên bạn phải chặn request đó và chèn ba giá trị vào payload trước khi cho nó tiếp tục:

# Playwright: Intercept the XHR and inject values
def handle_route(route):
    if "login" in route.request.url and route.request.method == "POST":
        # Modify the POST data to include our solution
        post_data = route.request.post_data
        # Add GeeTest values to the request
        route.continue_(post_data=modified_data)
    else:
        route.continue_()

page.route("**/api/login**", handle_route)

Ví dụ thực tế: pipeline QA regression cho đội automation

Một tình huống phổ biến ở các đội outsourcing/QA tại TP.HCM và Hà Nội: form đăng nhập trên môi trường staging được bảo vệ bằng GeeTest v3, và bộ regression suite chạy mỗi đêm cần đăng nhập hàng chục tài khoản test để kiểm tra luồng nghiệp vụ.

Thay vì tạm tắt CAPTCHA trên staging — cách này khiến hành vi test lệch khỏi production — đội automation làm theo trình tự sau:

  1. Gắn solve_geetest() vào bước setup của mỗi test case.
  2. Tiêm ba giá trị theo đúng framework đang dùng: Playwright cho web app mới, Selenium cho các dự án legacy.
  3. Để suite chạy tiếp như bình thường, không cần mock hay tắt widget.

Cách này giữ nguyên đường xác thực thật của trang, đúng tinh thần kiểm thử trên hệ thống mà đội có quyền truy cập.

Câu hỏi thường gặp

Vì sao GeeTest dùng ba giá trị riêng biệt thay vì một token?

Mỗi giá trị có vai trò khác nhau: geetest_challenge theo dõi phiên giải, geetest_validate chứng minh challenge đã được giải đúng, còn geetest_seccode là hash để server phát hiện giả mạo dữ liệu. Thiếu một giá trị, server không thể xác minh đủ ba điều kiện này.

Token GeeTest hết hạn nhanh không, có cần tiêm ngay sau khi giải?

Có — càng tiêm sớm càng tốt. Khoảng cách giữa lúc CaptchaAI trả kết quả và lúc bạn submit form nên tính bằng giây, không phải phút. Nếu script của bạn có bước xử lý trung gian (ghi log, gọi thêm API khác…) trước khi tiêm, hãy đưa bước solve GeeTest lại gần bước submit nhất có thể.

CaptchaAI có hỗ trợ GeeTest v4 không?

Chưa. CaptchaAI hiện chỉ hỗ trợ GeeTest v3 (chính là loại token ba phần trong bài này); GeeTest v4 đang ở trạng thái sắp ra mắt, chưa có trong sản phẩm.

Giải và tiêm token GeeTest cho vài nghìn request mỗi ngày tốn bao nhiêu?

CaptchaAI tính phí theo thread đang chạy song song, không theo số lần giải, nên khối lượng request mỗi ngày không làm phát sinh phụ phí. Gói BASIC ($15/tháng, 5 thread) phù hợp cho pipeline QA/regression quy mô nhỏ; nếu suite chạy nhiều test song song hơn, nâng lên STANDARD ($30/tháng, 15 thread) hoặc ADVANCE ($90/tháng, 50 thread) để tăng thông lượng.

Các lỗi thường gặp khi tiêm token GeeTest

Những lỗi hay gặp nhất, xếp theo thứ tự xuất hiện trong thực tế:

  • "Xác thực GeeTest không thành công" → nguyên nhân gần như chắc chắn là thiếu một trong ba giá trị; kiểm tra lại cả geetest_challenge, geetest_validate, geetest_seccode đã được tiêm.
  • Challenge hết hạn trước khi tiêm → khoảng cách quá dài giữa lúc trích xuất và lúc giải; trích xuất và gọi CaptchaAI giải ngay, không delay.
  • Form submit nhưng thiếu giá trị → sai tên trường hoặc sai selector của form; mở DevTools kiểm tra form thật để lấy đúng tên trường.
  • Callback không được kích hoạt → trang dùng tên callback tùy chỉnh; tìm tên callback trong tham số truyền vào initGeetest.
  • Đã tiêm giá trị nhưng widget vẫn hiển thị lỗi → trạng thái nội bộ của widget chưa cập nhật; kích hoạt callback thành công bằng script, như ở Phương án 2 của Bước 2.

Bước tiếp theo

Áp dụng cách tiêm này cho bất kỳ framework nào bạn đang dùng — lấy API key CaptchaAI và bắt đầu giải GeeTest v3 ngay trong pipeline hiện tại.

Bài viết liên quan

Os comentários estão desativados para este artigo.