使用 Playwright for Python 提交 CAPTCHA 解答
先求解 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 并提交
# 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.
匹配应用的令牌输入字段
示例会等待字段挂载,赋入 solution.token,并触发 input 事件。它不要求字段可见。如果应用提交的是其他字段,或从其他位置读取令牌状态,请调整此交接步骤,使其与发出的请求相匹配。
- 2.
补充表单的会话要求
示例从新的浏览器上下文开始。提交之前,请添加应用所需的会话、表单准备或 CSRF 处理。
- 3.
等待正确的 POST 请求
响应等待在点击提交按钮之前注册。其判断条件会匹配发往目标 URL 网络位置的 POST 请求。如果页面还会向该位置发送其他 POST 请求,请将判断条件缩小到表单提交端点。
读取应用响应
打印的 HTTP 状态可确认已收到匹配的响应。请检查应用的响应体或最终页面状态,以确定应用是否接受了提交。solvethree 任务就绪表示已生成解决方案,并不表示目标已接受它。
- 应用拒绝了已就绪的令牌。
在付费进行另一次求解之前,请检查发出的表单请求。
试试这样做: 确认请求包含的是 solution.token,而不是任务 ID 或旧值。检查提交是否使用了求解时的代理、返回的用户代理和客户端提示,以及预期目标。
- 响应等待捕获了无关请求。
将捕获的 POST URL 与实际表单端点进行比较。
试试这样做: 让响应判断条件专门匹配该端点,而不是匹配发往同一网络位置的所有 POST 请求。
- 求解调用在浏览器提交之前抛出异常。
区分 HTTP 请求错误与已进入失败终态的任务。
试试这样做: 单独处理非成功 HTTP 响应。对于失败的任务,请检查 errorCode 和 error 并修正原因,不要使用不变的输入重试。SDK 的 solve 方法本身就会在失败时停止。
来源与 API 参考
运行您的第一个任务。
使用 $5 工作区额度开始体验。