Trường Hợp Sử Dụng

Xử lý CAPTCHA Selenium bằng Python và CaptchaAI

Trong một script Selenium, phần khó của CAPTCHA không nằm ở trình duyệt. Chrome chỉ làm hai việc: lấy data-sitekey trong DOM và đặt token nhận về vào form; phần giải do API CaptchaAI xử lý phía server. Script vì thế không cần mô phỏng chuột hay nhận diện ảnh, và vẫn chạy được ở chế độ headless.

Bài dành cho dev Python đã có script Selenium và cần nó đi qua trang bật reCAPTCHA v2 hoặc Turnstile trên staging.

Những thứ cần có trước khi viết dòng code đầu tiên

Thành phần Ghi chú
Python 3.7+ Đã có sẵn pip
Selenium pip install selenium
Chrome + ChromeDriver Hai phiên bản phải khớp nhau
requests pip install requests
API key CaptchaAI Lấy trong dashboard tại captchaai.com

Chrome tự cập nhật còn ChromeDriver thì không — đó là lỗi session not created quen thuộc; trên CI hãy ghim cả hai phiên bản trong Dockerfile.

Luồng bốn bước: Selenium đọc sitekey, CaptchaAI trả token

  1. Selenium mở trang đích và chờ widget CAPTCHA render xong.
  2. Script đọc data-sitekey từ DOM, lấy pageurl từ driver.current_url.
  3. Script gửi task tới in.php, rồi polling res.php cho tới khi CaptchaAI trả token.
  4. Script đặt token vào trường g-recaptcha-response, gọi callback nếu form cần, rồi submit.

Bước 1: khởi tạo Chrome driver cho phiên kiểm thử

Driver Chrome với user-agent tường minh:

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

options = Options()
options.add_argument("")
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")

driver = webdriver.Chrome(options=options)

Nhiều trang trả HTML khác nhau theo user-agent; nếu HTML mà script nhận được khác bản bạn mở bằng tay, selector sẽ trượt. Hai tùy chọn dưới tắt thanh thông báo "Chrome đang được điều khiển bởi phần mềm tự động".

options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)

Bước 2: viết hàm giải reCAPTCHA v2 qua in.php và res.php

Hàm gửi task tới in.php, nhận ID task rồi gọi res.php mỗi 5 giây. CAPCHA_NOT_READY nghĩa là chờ tiếp; mọi chuỗi khác không bắt đầu bằng OK| là lỗi, nên dừng ngay.

import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_recaptcha_v2(site_key, page_url):
    """Solve reCAPTCHA v2 using CaptchaAI API."""
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url
    })
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit failed: {resp.text}")

    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY,
            "action": "get",
            "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY":
            continue
        if result.text.startswith("OK|"):
            return result.text.split("|")[1]
        raise Exception(f"Solve failed: {result.text}")

    raise TimeoutError("CAPTCHA solve timed out")

Trần chờ là 60 lần × 5 giây, tức 300 giây. reCAPTCHA v2 thường xong dưới 60 giây, nên chạm trần liên tục là dấu hiệu sitekey hoặc pageurl sai.

Bước 3: đọc sitekey và pageurl ngay trong phiên đang mở

Phần tử .g-recaptcha chỉ xuất hiện sau khi script bên thứ ba nạp xong, nên phải có WebDriverWait. Lấy pageurl từ driver.current_url thay vì hardcode: có redirect thì URL gửi lên phải là URL cuối cùng.

# Navigate to the target page
driver.get("https://staging.example.com/qa-login")

# Wait for the reCAPTCHA to load
wait = WebDriverWait(driver, 10)
recaptcha = wait.until(
    EC.presence_of_element_located((By.CLASS_NAME, "g-recaptcha"))
)

# Extract the site key
site_key = recaptcha.get_attribute("data-sitekey")
page_url = driver.current_url

print(f"Site key: {site_key}")
print(f"Page URL: {page_url}")

# Solve the CAPTCHA
token = solve_recaptcha_v2(site_key, page_url)
print(f"Token received: {token[:50]}...")

Hai dòng print ở cuối để đối chiếu nhanh: sitekey rỗng hoặc pageurl lệch là nguyên nhân phổ biến nhất của ERROR_WRONG_GOOGLEKEY.

Bước 4: đặt token vào form rồi submit

Token được ghi vào textarea ẩn g-recaptcha-response. Nhiều form chờ callback JavaScript thay vì submit trực tiếp, nên đoạn thứ hai duyệt ___grecaptcha_cfg.clients và gọi callback kèm token — xem cách xử lý reCAPTCHA v2 dạng callback.

# Inject the token into the reCAPTCHA response field
driver.execute_script(f"""
    document.getElementById('g-recaptcha-response').innerHTML = '{token}';
    document.getElementById('g-recaptcha-response').style.display = '';
""")

# If the form uses a callback function, trigger it
driver.execute_script(f"""
    if (typeof ___grecaptcha_cfg !== 'undefined') {{
        Object.keys(___grecaptcha_cfg.clients).forEach(function(key) {{
            var client = ___grecaptcha_cfg.clients[key];
            if (client.callback) client.callback('{token}');
        }});
    }}
""")

# Submit the form
driver.find_element(By.CSS_SELECTOR, "form").submit()

# Wait for navigation
wait.until(EC.url_changes(page_url))
print(f"Success! Now on: {driver.current_url}")

Đặt token và submit trong cùng một nhịp: quá khoảng 120 giây là server từ chối, dù token vẫn hợp lệ lúc nhận.

Script Python hoàn chỉnh cho reCAPTCHA v2

Ghép bốn bước thành một file, thay YOUR_API_KEY rồi trỏ driver.get() vào staging của bạn.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import requests
import time

API_KEY = "YOUR_API_KEY"

def solve_recaptcha_v2(site_key, page_url):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url
    })
    if not resp.text.startswith("OK|"):
        raise Exception(f"Submit failed: {resp.text}")
    task_id = resp.text.split("|")[1]

    for _ in range(60):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id
        })
        if result.text == "CAPCHA_NOT_READY":
            continue
        if result.text.startswith("OK|"):
            return result.text.split("|")[1]
        raise Exception(f"Solve failed: {result.text}")
    raise TimeoutError("Timed out")

def main():
    options = Options()
    options.add_argument("")
    driver = webdriver.Chrome(options=options)

    try:
        driver.get("https://staging.example.com/qa-login")
        wait = WebDriverWait(driver, 10)

        # Extract site key
        recaptcha = wait.until(
            EC.presence_of_element_located((By.CLASS_NAME, "g-recaptcha"))
        )
        site_key = recaptcha.get_attribute("data-sitekey")

        # Solve
        token = solve_recaptcha_v2(site_key, driver.current_url)

        # Inject and submit
        driver.execute_script(
            f"document.getElementById('g-recaptcha-response').innerHTML = '{token}';"
        )
        driver.find_element(By.CSS_SELECTOR, "form").submit()

        wait.until(EC.url_changes(driver.current_url))
        print("Login successful!")

    finally:
        driver.quit()

if __name__ == "__main__":
    main()

Đổi loại CAPTCHA: chỉ tham số trong in.php thay đổi

Khung gửi task rồi polling giữ nguyên cho mọi loại được hỗ trợ; chỉ tham số trong in.php là khác.

reCAPTCHA v3

v3 không có widget để chờ trong DOM, nhưng phải gửi kèm versionaction đúng giá trị trang dùng — sai action thì token vẫn về mà bị chấm điểm thấp. Token v3 về trong dưới 4 giây.

def solve_recaptcha_v3(site_key, page_url, action="verify"):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "version": "v3",
        "action": action
    })
    task_id = resp.text.split("|")[1]
    # ... same polling logic

Cloudflare Turnstile

Turnstile dùng sitekey chứ không phải googlekey, và token đi vào trường ẩn riêng của widget. Turnstile thường được giải trong dưới 10 giây, nên polling 5 giây là đủ dày.

def solve_turnstile(site_key, page_url):
    resp = requests.get("https://ocr.captchaai.com/in.php", params={
        "key": API_KEY,
        "method": "turnstile",
        "sitekey": site_key,
        "pageurl": page_url
    })
    task_id = resp.text.split("|")[1]
    # ... same polling logic

Lỗi hay gặp khi ghép Selenium với CaptchaAI

Triệu chứng Nguyên nhân thường gặp Cách xử lý
Server từ chối token vừa nhận Token hết hạn trước khi form gửi Đặt token và submit trong vòng 120 giây
Không đọc được data-sitekey Widget nạp động sau khi DOM sẵn sàng Tăng timeout của WebDriverWait
ERROR_WRONG_GOOGLEKEY Nhầm sitekey, hoặc lấy pageurl trước redirect So lại data-sitekey với driver.current_url
session not created Chrome và ChromeDriver lệch phiên bản Cài ChromeDriver khớp Chrome đang dùng

Checklist trước khi cho script chạy định kỳ

  • API key nằm trong biến môi trường, không hardcode rồi commit lên repo.
  • try/finally gọi driver.quit() để job lỗi không để lại process Chrome treo.
  • Ghi log ID task mà in.php trả về: khi đối soát số dư hoặc mở ticket, đó là thứ duy nhất tra ngược được.

Chọn gói theo số phiên Selenium chạy song song

Ở các đội QA và data tại Hà Nội hay TP.HCM, Selenium thường chạy hai workload: regression test ban đêm qua màn hình đăng nhập có reCAPTCHA v2 trên staging, và job theo dõi giá công khai cho danh mục sản phẩm của chính công ty trên Shopee hay Tiki.

CaptchaAI thu tiền theo thread (luồng giải đồng thời) chứ không theo từng lần giải: mỗi phiên đang chờ token chiếm một thread, trả lại ngay khi token về.

  • Thử nghiệm script trên máy cá nhân: BASIC ($15/tháng, 5 thread).
  • Regression khoảng 12 job song song: STANDARD ($30/tháng, 15 thread).
  • Vài chục worker crawl ban đêm: ADVANCE ($90/tháng, 50 thread).

Số lần giải không giới hạn nên hóa đơn không nhảy khi crawl tăng gấp đôi; giá niêm yết bằng USD. Chỉ lấy dữ liệu công khai trong phạm vi được phép và ghi log thời điểm, URL, mục đích từng lượt chạy — với dữ liệu cá nhân, Nghị định 13/2023/NĐ-CP là lý do thực tế để giữ log tối thiểu.

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

Nên chọn gói nào cho 20 phiên Selenium chạy song song?

ADVANCE ($90/tháng, 50 thread) là mức thoải mái; STANDARD ($30/tháng, 15 thread) vẫn đủ nếu 20 job không cùng lúc chờ token. Đếm số phiên chờ token tại một thời điểm, không phải tổng job trong ngày.

CaptchaAI có giải được hCaptcha không?

Không. Danh sách hiện tại gồm reCAPTCHA v2/v3 (kể cả Enterprise), Cloudflare Turnstile và Cloudflare Challenge, GeeTest v3, CAPTCHA ảnh/OCR, grid image và BLS, cùng CaptchaFox, Friendly Captcha và Lemin ở mức beta. hCaptcha và FunCaptcha (Arkose Labs) không có; GeeTest v4 mới ở mức sắp ra mắt.

Vì sao form vẫn bị từ chối dù API trả về OK|token?

Ba nguyên nhân phổ biến: token quá hạn vì script chèn thêm bước chờ; form dùng callback nên .submit() không kích hoạt xác thực; hoặc trang còn lớp kiểm tra khác ngoài CAPTCHA. So request của phiên chạy tay với phiên tự động để tách ba khả năng.

Hướng dẫn liên quan

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