# hCaptcha 图像识别模拟点击（Selenium）

> 用 Python Selenium 4 和 HCaptchaClassification 在浏览器里自动完成 hCaptcha 的九宫格图片题：点复选框、读题目和 9 张图片、调用识别接口、点击、提交，完整示例代码。

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

这篇教程用 Selenium 4 打开 hCaptcha 的演示页，在真实浏览器里完成九宫格图片题：读出题目和 9 张图片，交给 [hCaptcha 图像识别](https://docs.yescaptcha.com/zh/tasks/hcaptcha-classification/)（`HCaptchaClassification`）判断哪些是目标，再模拟点击。

hCaptcha 还有“点击指定位置”“拖动”等题型，它们返回坐标而不是格子，处理方法见 [hCaptcha 图像识别](https://docs.yescaptcha.com/zh/tasks/hcaptcha-classification/)。不想写代码可以直接用 [浏览器插件](https://docs.yescaptcha.com/zh/extension/)，这些题型插件都支持。

## 准备

```bash
pip install requests selenium
export YESCAPTCHA_CLIENT_KEY=你的clientKey
```

## 流程

1.  **点复选框。** 复选框在地址带 `frame=checkbox` 的 hCaptcha iframe 里，切换进去点 `#checkbox`。
2.  **切到题目的 iframe。** 题目在地址带 `frame=challenge` 的 iframe 里。
3.  **读题目。** `.prompt-text` 的文字就是题目，原样传给接口（建议把 hCaptcha 的界面语言切成简体中文或英文）。题目旁边有示例小图的，把小图也放进 `anchors`。
4.  **取图片。** 每个格子 `.task-image .image` 的背景图就是一张小图，按顺序下载、转成 Base64，组成 `queries` 列表。
5.  **识别。** 调用 `createTask`，类型 `HCaptchaClassification`。返回的 `objects` 和格子一一对应，`true` 的是目标。
6.  **点击并提交。** 点击是目标的格子，点提交按钮。可能会连续出几轮，重复第 3 到 6 步。
7.  **完成。** 通过后，页面上 `h-captcha-response` 字段里就有 token，随表单一起提交。

## 完整代码

hcaptcha_click_selenium.py

```python
"""Click through an hCaptcha 3x3 image challenge with Selenium 4 and HCaptchaClassification.

Flow: open the page -> click the checkbox -> read the question and the 9 images ->
ask YesCaptcha which images match -> click them -> submit. Repeat while new rounds appear.

Run:  pip install requests selenium
      export YESCAPTCHA_CLIENT_KEY=your_client_key
      python hcaptcha_click_selenium.py
Each recognized round is charged (see the price list; rounds with no target found are free).
hCaptcha changes its page structure often; adjust the selectors below if a step stops working.
Click and drag questions return coordinates instead; see the HCaptchaClassification page.
"""
import base64
import os
import re
import time

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.wait import WebDriverWait

CLIENT_KEY = os.environ["YESCAPTCHA_CLIENT_KEY"]
PAGE_URL = "https://accounts.hcaptcha.com/demo"

def classify(question, images_b64):
    task = {"type": "HCaptchaClassification", "question": question, "queries": images_b64}
    data = requests.post(
        "https://api.yescaptcha.com/createTask",
        json={"clientKey": CLIENT_KEY, "task": task},
        timeout=60,
    ).json()
    if data.get("errorId"):
        raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
    return data["solution"]  # synchronous

def background_image_b64(element):
    """The tiles are <div>s with a CSS background image; download it and Base64-encode it."""
    style = element.get_attribute("style") or ""
    match = re.search(r'url\("?(.*?)"?\)', style)
    if not match:
        return None
    return base64.b64encode(requests.get(match.group(1), timeout=30).content).decode()

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

    # 1. Click the checkbox (inside the first hCaptcha iframe)
    wait.until(EC.frame_to_be_available_and_switch_to_it(
        (By.CSS_SELECTOR, 'iframe[src*="hcaptcha"][src*="frame=checkbox"]')))
    wait.until(EC.element_to_be_clickable((By.ID, "checkbox"))).click()
    driver.switch_to.default_content()

    # 2. Solve rounds in the challenge iframe
    wait.until(EC.frame_to_be_available_and_switch_to_it(
        (By.CSS_SELECTOR, 'iframe[src*="hcaptcha"][src*="frame=challenge"]')))
    for _ in range(5):
        question = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".prompt-text"))).text.strip()
        tiles = driver.find_elements(By.CSS_SELECTOR, ".task-image")
        images = [background_image_b64(t.find_element(By.CSS_SELECTOR, ".image")) for t in tiles]
        if not tiles or None in images:
            print("not a 3x3 image challenge; see the docs for click/drag questions")
            break
        solution = classify(question, images)
        for tile, hit in zip(tiles, solution.get("objects", [])):
            if hit:
                tile.click()
                time.sleep(0.3)
        driver.find_element(By.CSS_SELECTOR, ".button-submit").click()
        time.sleep(3)
        if not driver.find_elements(By.CSS_SELECTOR, ".task-image"):
            break

    driver.switch_to.default_content()
    token = driver.find_element(By.NAME, "h-captcha-response").get_attribute("value")
    print("token received" if token else "not passed")
finally:
    driver.quit()
```

## 注意

-   hCaptcha 的页面结构经常变化，代码里的选择器失效时要按实际页面调整。
-   每轮识别扣点；没有识别出目标的不扣点。点击后验证失败的可以在 1 小时内 [上报](https://docs.yescaptcha.com/zh/api/report/)，申请返点。
-   遇到新题目返回 `ERROR_UNKNOWN_QUESTION` 时，可以刷新换一题，或者联系客服。
