# Mô phỏng click nhận dạng hình ảnh reCAPTCHA v2 (Selenium)

> Dùng Python Selenium 4 và ReCaptchaV2Classification để tự động click các ô ảnh của reCAPTCHA v2 trong trình duyệt: kích hoạt captcha, đọc câu hỏi và hình ảnh, gọi API nhận dạng, click các ô, gửi biểu mẫu; kèm mã ví dụ đầy đủ.

Source: https://docs.yescaptcha.com/vi/guides/recaptcha-click-selenium/ · Last verified: 2026-10-15

> This page was machine-translated from the Chinese version. If anything differs, the Chinese version prevails: https://docs.yescaptcha.com/zh/guides/recaptcha-click-selenium/

Bài hướng dẫn này dùng Selenium 4 mở trang demo reCAPTCHA của Google và hoàn thành bài kiểm tra hình ảnh trong trình duyệt thật: đọc câu hỏi và hình ảnh, giao cho [nhận dạng hình ảnh reCAPTCHA v2](https://docs.yescaptcha.com/vi/tasks/recaptcha-classification.md) (`ReCaptchaV2Classification`) xác định cần click những ô nào, rồi mô phỏng click. So với cách lấy token, cách này hoàn thành xác minh ngay trong trình duyệt, phù hợp với các website có tỷ lệ token được chấp nhận thấp.

Nếu không muốn viết mã, bạn có thể dùng trực tiếp [tiện ích trình duyệt](https://docs.yescaptcha.com/vi/extension.md), vì nó tự động click dựa trên cùng một API.

## Chuẩn bị

```bash
pip install requests selenium pillow
export YESCAPTCHA_CLIENT_KEY=your_client_key
```

Cần cài Chrome. Selenium 4 sẽ tự động tải chromedriver phù hợp.

## Quy trình

1.  **Click ô checkbox.** Checkbox nằm trong iframe có `title="reCAPTCHA"`; hãy chuyển vào iframe đó rồi click `#recaptcha-anchor`.
2.  **Chuyển sang iframe của câu hỏi.** Câu hỏi nằm trong một iframe khác (title chứa “challenge”).
3.  **Đọc câu hỏi.** Phần chữ in đậm trong `.rc-imageselect-desc-wrapper strong` chính là đối tượng cần tìm, ví dụ “traffic lights”. API cần ID câu hỏi, vì vậy hãy dùng bảng đối chiếu để đổi tên thành ID (`/m/015qff`), xem [ID câu hỏi](https://docs.yescaptcha.com/vi/tasks/recaptcha-classification.md). Giao diện trình duyệt bằng tiếng Anh là thuận tiện nhất để đối chiếu.
4.  **Lấy hình ảnh.** Các ô thực chất là các phần của cùng một ảnh lớn; hãy tải `src` của `td.rc-imageselect-tile img`. Với 9 ô (3x3), thu phóng về 300×300; với 16 ô (4x4), thu phóng về 450×450, rồi chuyển sang Base64.
5.  **Nhận dạng.** Gọi `createTask` với loại `ReCaptchaV2Classification`, truyền `image` và `question`. Thêm `"confidence": 0.5` thì ảnh 3x3 sẽ trả về một lần tất cả các ô phù hợp, không cần xử lý các ảnh nhỏ xuất hiện sau khi click.
6.  **Click.** `objects` trả về là số thứ tự các ô, bắt đầu từ 0, từ trái sang phải, từ trên xuống dưới; hãy lần lượt click các `td` tương ứng.
7.  **Xác minh.** Click `#recaptcha-verify-button`. Nếu xuất hiện vòng câu hỏi mới, quay lại bước 3; khi cửa sổ câu hỏi đóng lại nghĩa là đã vượt qua.
8.  **Gửi biểu mẫu.** Quay lại trang chính và click nút gửi.

## Mã đầy đủ

recaptcha_click_selenium.py

```python
"""Click through a reCAPTCHA v2 image challenge with Selenium 4 and ReCaptchaV2Classification.

Flow: open the page -> click the checkbox -> read the target name and the grid image ->
ask YesCaptcha which tiles to click -> click them -> press Verify. Repeat if a new challenge appears.

Run:  pip install requests selenium pillow
      export YESCAPTCHA_CLIENT_KEY=your_client_key
      python recaptcha_click_selenium.py
Each recognized image is charged (see the price list). reCAPTCHA's page structure can change;
adjust the selectors below if a step stops working.
"""
import base64
import io
import os
import time

import requests
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

CLIENT_KEY = os.environ["YESCAPTCHA_CLIENT_KEY"]
PAGE_URL = "https://www.google.com/recaptcha/api2/demo"

# Target names shown in the challenge (English UI) -> question IDs.
# Full list: https://docs.yescaptcha.com/en/tasks/recaptcha-classification/
QUESTION_IDS = {
    "taxis": "/m/0pg52", "bus": "/m/01bjv", "buses": "/m/01bjv", "motorcycles": "/m/04_sv",
    "tractors": "/m/013xlm", "chimneys": "/m/01jk_4", "crosswalks": "/m/014xcs",
    "traffic lights": "/m/015qff", "bicycles": "/m/0199g", "parking meters": "/m/015qbp",
    "cars": "/m/0k4j", "bridges": "/m/015kr", "boats": "/m/019jd", "palm trees": "/m/0cdl1",
    "mountains or hills": "/m/09d_r", "fire hydrants": "/m/01pns0", "a fire hydrant": "/m/01pns0",
    "stairs": "/m/01lynh",
}

def classify(image_b64, question):
    task = {"type": "ReCaptchaV2Classification", "image": image_b64, "question": question, "confidence": 0.5}
    data = requests.post(
        "https://api.yescaptcha.com/createTask",
        json={"clientKey": CLIENT_KEY, "task": task},
        timeout=30,
    ).json()
    if data.get("errorId"):
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data["solution"]  # synchronous: the solution comes back right away

def grid_image_b64(url, size):
    """Download the challenge image and scale it to the standard size (300 for 3x3, 450 for 4x4)."""
    img = Image.open(io.BytesIO(requests.get(url, timeout=30).content)).convert("RGB")
    img = img.resize((size, size))
    buf = io.BytesIO()
    img.save(buf, format="JPEG")
    return base64.b64encode(buf.getvalue()).decode()

driver = webdriver.Chrome()
wait = WebDriverWait(driver, 15)
try:
    driver.get(PAGE_URL)

    # 1. Click the checkbox (inside the "reCAPTCHA" iframe)
    wait.until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, 'iframe[title="reCAPTCHA"]')))
    wait.until(EC.element_to_be_clickable((By.ID, "recaptcha-anchor"))).click()
    driver.switch_to.default_content()

    # 2. Switch to the challenge iframe and solve rounds until it closes
    wait.until(EC.frame_to_be_available_and_switch_to_it(
        (By.XPATH, '//iframe[contains(@title, "challenge")]')))
    for _ in range(5):
        target = wait.until(EC.presence_of_element_located(
            (By.CSS_SELECTOR, ".rc-imageselect-desc-wrapper strong"))).text.strip().lower()
        question = QUESTION_IDS.get(target)
        if not question:
            print("unsupported target, reloading:", target)
            driver.find_element(By.ID, "recaptcha-reload-button").click()
            time.sleep(2)
            continue
        tiles = driver.find_elements(By.CSS_SELECTOR, "td.rc-imageselect-tile")
        size = 450 if len(tiles) == 16 else 300
        url = driver.find_element(By.CSS_SELECTOR, "td.rc-imageselect-tile img").get_attribute("src")
        solution = classify(grid_image_b64(url, size), question)
        for index in solution.get("objects", []):
            tiles[index].click()
            time.sleep(0.3)
        time.sleep(1)
        driver.find_element(By.ID, "recaptcha-verify-button").click()
        time.sleep(3)
        driver.switch_to.default_content()
        frame = driver.find_element(By.XPATH, '//iframe[contains(@title, "challenge")]')
        if not frame.is_displayed():  # challenge closed: passed
            break
        driver.switch_to.frame(frame)

    driver.switch_to.default_content()
    driver.find_element(By.ID, "recaptcha-demo-submit").click()
    time.sleep(2)
    print("passed" if "Verification Success" in driver.page_source else "not passed")
finally:
    driver.quit()
```

## Lưu ý

-   Cấu trúc trang của reCAPTCHA thay đổi theo thời gian; khi các selector trong mã không còn hiệu lực, hãy điều chỉnh theo trang thực tế.
-   Mỗi lần nhận dạng một ảnh đều bị trừ điểm (giá xem tại [nhận dạng hình ảnh reCAPTCHA v2](https://docs.yescaptcha.com/vi/tasks/recaptcha-classification.md)). Các tác vụ xác minh thất bại sau khi click có thể được [báo cáo](https://docs.yescaptcha.com/vi/api/report.md) trong vòng 1 giờ để yêu cầu hoàn điểm.
-   Nếu gặp câu hỏi không có trong bảng đối chiếu, bạn có thể nhấn nút làm mới để đổi câu khác, hoặc liên hệ bộ phận hỗ trợ để bổ sung.
