Scarnox · Blog

Cách giải PopularCaptcha bằng Python (ví dụ chạy được, 2026)

Nếu script Python của bạn đã gặp một PopularCaptcha, bạn không cần một trang trại trình duyệt hay mô hình riêng. Bạn cần sitekey của trang, một POST request, và token trả về.

Hướng dẫn này đưa bạn từ con số không đến một token chạy được trong khoảng năm phút: lấy khóa API, tìm sitekey, yêu cầu token, gửi token vào form, và xử lý các trường hợp lỗi xuất hiện khi script chạy không giám sát. Lần giải thất bại thì miễn phí, nên một lần thử lại chỉ tốn thời gian.

Scarnox giải PopularCaptcha hiện nay, có proxy hoặc không có proxy. Các loại captcha khác như reCAPTCHA, Cloudflare Turnstile và AWS WAF sắp ra mắt, nên mọi ví dụ ở đây dùng PopularCaptcha. Tổng quan các loại captcha giải thích chúng khác nhau thế nào.

1. Lấy khóa API

Tạo tài khoản và bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ. Khóa API của bạn nằm trên bảng điều khiển. Giữ nó ngoài mã nguồn, và đọc từ biến môi trường:

export SCARNOX_KEY="scarnox_YOURAPIKEY"

2. Tìm sitekey

Mở trang có captcha, mở DevTools, và tìm data-sitekey trong bảng Elements. Sitekey của PopularCaptcha là UUID:

<div class="h-captcha" data-sitekey="10000000-ffff-ffff-ffff-000000000001"></div>

Không thấy data-sitekey trong HTML? Widget có lẽ được render bởi JavaScript. Hãy kiểm tra tab Network để tìm request cấu hình của widget; tham số truy vấn sitekey của nó là thứ bạn cần. Các cách khác để tìm sitekey bao gồm URL iframe và widget vô hình.

Gửi URL của trang hiển thị widget làm site_url. Host là quan trọng nhất, nhưng URL trang đầy đủ là lựa chọn an toàn.

3. Yêu cầu token

import os
import requests

API_URL = "https://api.scarnox.com/api/tasks/create"
API_KEY = os.environ["SCARNOX_KEY"]

def solve_captcha(sitekey: str, page_url: str) -> str:
    r = requests.post(
        API_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "captcha_type": "PopularCaptchaTaskProxyless",
            "site_key": sitekey,
            "site_url": page_url,
        },
        timeout=185,  # lệnh gọi 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"]

if __name__ == "__main__":
    token = solve_captcha("10000000-ffff-ffff-ffff-000000000001", "https://example.com/signup")
    print(token[:40], "...")

Đó là toàn bộ tích hợp. Không có vòng lặp polling và không có lệnh gọi "lấy kết quả" thứ hai. Request trả về khi token sẵn sàng, nên thiết lập quan trọng duy nhất là timeout đủ dài để bao phủ một lần giải chậm. Dùng khoảng 185 giây.

Một phản hồi thành công có errorId đặt là 0, với token nằm trong solution.token. Bất kỳ errorId nào khác nghĩa là lần giải thất bại, và phản hồi mang theo errorCode và errorDescription cho biết lý do.

4. Gửi token

Token đi vào nơi trang mong đợi phản hồi captcha. Với PopularCaptcha, hầu như luôn là trường form tên h-captcha-response. Nhiều trang cũng chấp nhận g-recaptcha-response, nên hãy gửi cả hai:

import requests

def sign_up(session: requests.Session, form_url: str, email: str, sitekey: str) -> requests.Response:
    token = solve_captcha(sitekey, form_url)
    return session.post(form_url, data={
        "email": email,
        "h-captcha-response": token,
        "g-recaptcha-response": token,
    }, timeout=30)

Hai chi tiết quan trọng ở đây. Các trang thật thường cũng kiểm tra cookie phiên từ lần tải trang, nên hãy tải form bằng cùng một requests.Session trước khi post. Và post tới URL action của form, không phải lúc nào cũng là URL trang.

Giải ngay trước khi gửi. Token hết hạn trong khoảng hai phút, nên đừng lấy nó ở đầu một công việc dài rồi giữ lại. Nếu trang từ chối token, hãy mở form trong DevTools và xác nhận tên các trường ẩn.

5. Xử lý lỗi

Lần giải thất bại thì miễn phí, nên thử lại chỉ tốn thời gian. Tuy nhiên, một số lỗi sẽ không bao giờ thành công khi thử lại, nên đừng lặp mù quáng. Kiểm tra errorId mỗi lần, ghi log errorCode và errorDescription, và chỉ retry các lỗi tạm thời.

Lỗi mạng cần xử lý riêng, vì requests ném ngoại lệ trước khi có phản hồi nào:

import requests

try:
    token = solve_captcha(sitekey, page_url)
except requests.RequestException as exc:
    print("network problem, retry later:", exc)
except RuntimeError as exc:
    print("solve rejected:", exc)

Hướng dẫn xử lý lỗi có một chính sách retry đầy đủ với backoff và jitter.

Các lỗi thường gặp

Dùng thử miễn phí

Tạo tài khoản và bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ. Chạy ví dụ trên trên một form bạn sở hữu trước khi đưa nó vào một công việc lớn hơn. Để biết chi phí khi giải ở khối lượng lớn, xem giá hiện tại.

Ghi log từng lần giải

Ghi một dòng cho mỗi lần giải: sitekey, host của trang, errorId, errorCode khi thất bại, và thời gian lệnh gọi mất. Đừng ghi khóa API hay mật khẩu proxy. Những dòng này cho bạn biết một lần chạy chậm là do trang, proxy hay bộ giải. Chúng cũng là thứ đầu tiên bạn sẽ cần khi mở yêu cầu hỗ trợ.

Giải trong vòng lặp

Nếu script của bạn giải nhiều captcha liên tiếp, hãy giữ vòng lặp đơn giản. Giải, gửi, kiểm tra kết quả, rồi mới chuyển sang bước tiếp theo. Đừng giải trước và xếp token vào hàng chờ, vì mỗi token hết hạn trong khoảng hai phút. Một khoảng dừng ngắn giữa các lần giải trên cùng một trang giữ tốc độ request của bạn gần với những gì một người sẽ tạo ra.

Bước tiếp theo