使用 Playwright for Python 提交 CAPTCHA 解答

作者:solvethree阅读需 1 分钟

先求解 CAPTCHA,然后使用相同的代理和返回的浏览器身份创建 Playwright 上下文。通过应用实际发送的字段提交 solution.token。

下面的示例在你控制的页面上提交表单。它等待应用的 POST 响应,不会拦截 reCAPTCHA,也不依赖通用 CAPTCHA 回调。

本页内容

准备目标和环境

使用 Python 3.10+,安装 Playwright 及其 Chromium 浏览器,并安装官方 solvethree Python SDK wheel 包。将工作区 API 密钥保留在 Python 进程中,不要放入浏览器 JavaScript。

  • TARGET_URL:表单页面的完整 URL。示例会提取其中的主机名用于求解请求。
  • SOLVETHREE_PROXY:代理 URL,如有需要,应包含身份验证凭据。求解和浏览器流量使用同一代理。
  • SOLVETHREE_API_KEY:工作区 API 密钥。

目标主机名需要有一个已激活且工作区可用的配置文件。运行示例之前,请先设置站点配置文件。将示例中的 login 操作改为集成所需的操作;发送主机名和操作不会创建配置文件。请求默认使用 reCAPTCHA Enterprise v3。

求解、配置 Chromium 并提交

Python Playwright · 实际提交表单
# Install playwright and the official Python SDK; run playwright install chromium.
import asyncio
import os
from urllib.parse import urlsplit, unquote
from playwright.async_api import async_playwright
from solvethree import Solvethree

async def main():
    target = os.environ["TARGET_URL"]
    proxy = urlsplit(os.environ["SOLVETHREE_PROXY"])
    client = Solvethree(os.environ["SOLVETHREE_API_KEY"])
    task = {"site": urlsplit(target).hostname, "action": "login", "proxy": os.environ["SOLVETHREE_PROXY"]}
    result = await asyncio.to_thread(client.solve, task, timeout=180)
    solution = result["solution"]
    async with async_playwright() as playwright:
        browser = await playwright.chromium.launch()
        try:
            netloc = proxy.netloc.rsplit("@", 1)[-1]
            context = await browser.new_context(
                proxy={"server": f"{proxy.scheme}://{netloc}", "username": unquote(proxy.username or ""), "password": unquote(proxy.password or "")},
                user_agent=solution["userAgent"], extra_http_headers={"sec-ch-ua": solution["secChUa"]})
            page = await context.new_page()
            await page.goto(target)
            field = page.locator('[name="g-recaptcha-response"]')
            await field.wait_for(state="attached")
            await field.evaluate("(field, token) => { field.value=token; field.dispatchEvent(new Event('input',{bubbles:true})); }", solution["token"])
            async with page.expect_response(lambda r: r.request.method == "POST" and urlsplit(r.url).netloc == urlsplit(target).netloc) as outcome:
                await page.get_by_role("button", name="Submit", exact=True).click()
            print("Application HTTP status:", (await outcome.value).status)
            # Check the application's actual response; adapt form/CSRF/session setup.
            await context.close()
        finally:
            await browser.close()

asyncio.run(main())

Solvethree 是同步的。通过 asyncio.to_thread 调用 client.solve,可避免其创建和轮询操作阻塞 Playwright 的事件循环。它会返回就绪结果或抛出异常;SDK 没有原生异步客户端。

浏览器上下文使用 solution.userAgent 和 solution.secChUa。示例还将代理服务器与其用户名和密码分离,并在传递给 Playwright 之前对凭据进行解码。由于此任务使用一个代理,因此没有候选代理选择步骤。

  1. 1.

    匹配应用的令牌输入字段

    示例会等待字段挂载,赋入 solution.token,并触发 input 事件。它不要求字段可见。如果应用提交的是其他字段,或从其他位置读取令牌状态,请调整此交接步骤,使其与发出的请求相匹配。

  2. 2.

    补充表单的会话要求

    示例从新的浏览器上下文开始。提交之前,请添加应用所需的会话、表单准备或 CSRF 处理。

  3. 3.

    等待正确的 POST 请求

    响应等待在点击提交按钮之前注册。其判断条件会匹配发往目标 URL 网络位置的 POST 请求。如果页面还会向该位置发送其他 POST 请求,请将判断条件缩小到表单提交端点。

读取应用响应

打印的 HTTP 状态可确认已收到匹配的响应。请检查应用的响应体或最终页面状态,以确定应用是否接受了提交。solvethree 任务就绪表示已生成解决方案,并不表示目标已接受它。

应用拒绝了已就绪的令牌。

在付费进行另一次求解之前,请检查发出的表单请求。

试试这样做: 确认请求包含的是 solution.token,而不是任务 ID 或旧值。检查提交是否使用了求解时的代理、返回的用户代理和客户端提示,以及预期目标。

响应等待捕获了无关请求。

将捕获的 POST URL 与实际表单端点进行比较。

试试这样做: 让响应判断条件专门匹配该端点,而不是匹配发往同一网络位置的所有 POST 请求。

求解调用在浏览器提交之前抛出异常。

区分 HTTP 请求错误与已进入失败终态的任务。

试试这样做: 单独处理非成功 HTTP 响应。对于失败的任务,请检查 errorCode 和 error 并修正原因,不要使用不变的输入重试。SDK 的 solve 方法本身就会在失败时停止。

运行您的第一个任务。

使用 $5 工作区额度开始体验。

创建工作区
使用 Playwright for Python 提交 CAPTCHA 解答 · solvethree