Scarnox · Blog

Python Selenium 验证码识别:完整示例(2026)

Selenium 运行的是真实的浏览器,页面会正常渲染,脚本也会像在人手操作时那样运行。它唯一无法完成的步骤,就是识别 PopularCaptcha 控件。本指南补上这一步:从渲染后的页面读取 sitekey,用一次 API 调用获取 token,把 token 写入表单,然后提交。

本指南涵盖 PopularCaptcha 控件,Scarnox 目前以无代理方式识别它。请先注册账户,免费试用额度,无需信用卡,并先在测试表单上运行这段脚本。

Selenium 需要做什么

整个流程分为四部分:

  1. 加载页面,并等待控件渲染完成。
  2. 从控件中读取 data-sitekey 属性。
  3. 从 API 获取 token,并把它放入表单。
  4. 在 token 过期之前提交表单。

只有第三步涉及验证码。其余都是常规的 Selenium 操作。

安装依赖包

pip install selenium requests

Selenium 4.6 及更高版本会自动找到匹配的 ChromeDriver,因此你不需要手动下载驱动。

完整脚本

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,  # 阻塞直到 token 准备好
    )
    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)

        # 先填写其他字段,再在提交前注入 token,
        # 因为 token 约两分钟后过期。
        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()

对于无代理的 PopularCaptcha 识别,脚本不需要代理。省略代理字段,让网站看到你正常的网络连接即可。

操作顺序是最容易出错的地方。先填写表单的其余部分,然后识别,然后注入,最后点击。如果你在填写其他字段之前就识别,页面或表单缓慢时,提交可能会超过 token 的有效期。请尽量缩短识别与点击之间的间隔,并且在认为验证码已通过之前,先检查结果页面是否有错误。

为什么用 JavaScript 注入 token

大多数 PopularCaptcha 控件在有人完成验证时,会把 token 写入名为 h-captcha-response 的隐藏文本域。由于 Selenium 看不到真人,脚本需要自己写入同一个字段。INJECT_TOKEN_JS 片段做了两件事:

它还会填充 g-recaptcha-response,因为许多网站为了兼容性会读取这个名字。

隐形控件

隐形控件没有复选框。它在后台运行,通常在表单提交时触发。脚本会检查 data-size="invisible",看到它时发送 is_invisible: true。有些网站还会给控件附加一个 JavaScript 回调。如果你注入 token 之后表单仍然停留在原处,请查找 data-callback 属性,并用 token 调用该函数。隐形验证码指南涵盖了这种情况。

在测试中使用

Selenium 也是端到端测试的常用工具。如果你自己的预发布网站显示验证码,同样的代码可以让测试通过验证码。请让测试环境与生产环境分开,并且不要对你不拥有的网站运行。测试套件的配置方法见自动化测试中的验证码。

常见问题

定价

你按成功的识别付费,识别失败不收费。费率请查看当前定价,并免费试用额度,无需信用卡,立即注册。

下一步