Scarnox · Blog

Bộ giải captcha Selenium bằng Python: ví dụ chạy được (2026)

Selenium chạy một trình duyệt thật, nên trang được render và các script chạy như khi có người dùng. Bước duy nhất nó không làm được là giải widget PopularCaptcha. Hướng dẫn này bổ sung bước đó: đọc sitekey từ trang đã render, lấy token bằng một lệnh gọi API, ghi token vào form, và gửi.

Nó bao quát widget PopularCaptcha, loại mà Scarnox giải hiện 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à thử script trên một form thử nghiệm trước.

Selenium cần làm gì

Luồng có bốn phần:

  1. Tải trang và chờ widget được render.
  2. Đọc thuộc tính data-sitekey từ widget.
  3. Lấy token từ API và đặt vào form.
  4. Gửi form trước khi token hết hạn.

Bước thứ ba là bước duy nhất chạm tới captcha. Mọi thứ khác là công việc Selenium bình thường.

Cài đặt gói

pip install selenium requests

Selenium 4.6 trở lên tự tìm ChromeDriver phù hợp, nên bạn không cần tải driver bằng tay.

Script đầy đủ

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

API_KEY = "scarnox_YOURAPIKEY"
API_URL = "https://api.scarnox.com/api/tasks/create"
PAGE_URL = "https://example.com/signup"


def solve(sitekey: str, page_url: str, invisible: bool = False) -> str:
    body = {
        "captcha_type": "PopularCaptchaTaskProxyless",
        "site_key": sitekey,
        "site_url": page_url,
    }
    if invisible:
        body["is_invisible"] = True

    r = requests.post(
        API_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json=body,
        timeout=185,  # chặn cho đến khi token sẵn sàng
    )
    data = r.json()
    if data.get("errorId") != 0:
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data["solution"]["token"]


INJECT_TOKEN_JS = """
const token = arguments[0];
let field = document.querySelector('[name="h-captcha-response"]');
if (!field) {
    field = document.createElement("textarea");
    field.name = "h-captcha-response";
    field.style.display = "none";
    document.querySelector("form").appendChild(field);
}
field.value = token;
let extra = document.querySelector('[name="g-recaptcha-response"]');
if (!extra) {
    extra = document.createElement("textarea");
    extra.name = "g-recaptcha-response";
    extra.style.display = "none";
    document.querySelector("form").appendChild(extra);
}
extra.value = token;
"""


def main() -> None:
    driver = webdriver.Chrome()
    try:
        driver.get(PAGE_URL)

        widget = WebDriverWait(driver, 15).until(
            EC.presence_of_element_located((By.CSS_SELECTOR, "[data-sitekey]"))
        )
        sitekey = widget.get_attribute("data-sitekey")
        invisible = widget.get_attribute("data-size") == "invisible"

        token = solve(sitekey, driver.current_url, invisible=invisible)

        # Điền các trường khác trước, rồi chèn token ngay trước khi gửi,
        # vì token hết hạn sau khoảng hai phút.
        driver.find_element(By.NAME, "email").send_keys("you@example.com")
        driver.execute_script(INJECT_TOKEN_JS, token)

        driver.find_element(By.CSS_SELECTOR, "form button[type=submit]").click()
        WebDriverWait(driver, 15).until(EC.url_changes(PAGE_URL))
        print("Submitted. Now at:", driver.current_url)
    finally:
        driver.quit()


if __name__ == "__main__":
    main()

Script không cần proxy cho lần giải PopularCaptcha proxyless. Bỏ trường proxy và để trang thấy kết nối bình thường của bạn.

Thứ tự thao tác là phần hay sai nhất. Điền phần còn lại của form trước, rồi giải, rồi chèn, rồi click. Nếu bạn giải trước khi nhập vào các trường khác, một trang chậm hoặc form chậm có thể đẩy việc gửi vượt quá thời gian sống của token. Giữ khoảng cách giữa lần giải và lần click càng ngắn càng tốt, và kiểm tra trang kết quả có lỗi hay không trước khi cho rằng captcha đã qua.

Vì sao token được chèn bằng JavaScript

Hầu hết widget PopularCaptcha ghi token vào một textarea ẩn tên h-captcha-response khi một người giải chúng. Vì Selenium không có người, script tự ghi trường tương tự. Đoạn INJECT_TOKEN_JS làm hai việc:

Nó cũng điền g-recaptcha-response, vì nhiều trang đọc tên đó để tương thích.

Widget vô hình

Widget vô hình không có ô đánh dấu. Nó chạy ngầm và thường được kích hoạt khi gửi form. Script kiểm tra data-size="invisible" và gửi is_invisible: true khi thấy nó. Một số trang cũng gắn một callback JavaScript vào widget. Nếu form vẫn đứng yên sau khi bạn chèn token, hãy tìm thuộc tính data-callback và gọi hàm đó với token. Bài viết về captcha vô hình bao quát trường hợp này.

Dùng trong kiểm thử

Selenium cũng là công cụ thường dùng cho kiểm thử end-to-end. Nếu trang staging của bạn hiển thị captcha, cùng code này cho phép một bài kiểm thử vượt qua nó. Hãy giữ môi trường kiểm thử tách biệt với production, và đừng chạy trên các trang bạn không sở hữu. Thiết lập cho bộ kiểm thử nằm trong captcha trong kiểm thử tự động.

Các vấn đề thường gặp

Giá

Bạn trả tiền theo mỗi lần giải thành công. Lần giải thất bại không bị tính phí. Xem giá hiện tại và bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ.

Bước tiếp theo