验证码通常嵌入一个或多个 iframe 中。 CAPTCHA 小部件可能位于支付 iframe 内,而支付 iframe 本身位于模式 iframe 内。要解决这些验证码,您需要导航 iframe 树,提取 sitekey,通过 CaptchaAI 解决,并将token 提交回正确的框架上下文中。
iframe 验证码的工作原理
典型的嵌套结构:
Main page
└── iframe#payment-frame (cross-origin)
└── iframe[src*="recaptcha/api2/anchor"] (Google-hosted)
└── reCAPTCHA widget
sitekey 位于外部 iframe(呈现小部件的 iframe)中。最里面的 Google iframe 是视觉挑战——你不直接与它交互。iframe src 中的 data-sitekey 属性或 k= 参数就是你所需要的。
Python:Selenium iframe 切换
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import requests
import re
import time
API_KEY = "YOUR_API_KEY"
driver = webdriver.Chrome()
driver.get("https://example.com/checkout")
# Step 1: Switch into the outer iframe
wait = WebDriverWait(driver, 15)
outer_iframe = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, "iframe#payment-frame"))
)
driver.switch_to.frame(outer_iframe)
# Step 2: Find the reCAPTCHA iframe and extract sitekey
recaptcha_iframe = wait.until(
EC.presence_of_element_located(
(By.CSS_SELECTOR, 'iframe[src*="recaptcha/api2/anchor"]')
)
)
src = recaptcha_iframe.get_attribute("src")
sitekey = re.search(r"k=([A-Za-z0-9_-]+)", src).group(1)
page_url = driver.current_url
print(f"Sitekey: {sitekey}")
print(f"Page URL: {page_url}")
# Step 3: Solve with CaptchaAI
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": page_url,
"json": "1"
})
task_id = resp.json()["request"]
token = None
for _ in range(24):
time.sleep(5)
poll = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": task_id, "json": "1"
}).json()
if poll["status"] == 1:
token = poll["request"]
break
if poll["request"] != "CAPCHA_NOT_READY":
raise Exception(poll["request"])
print(f"Token: {token[:50]}...")
# Step 4: Inject token (still inside the outer iframe)
driver.execute_script("""
document.querySelector('textarea[name="g-recaptcha-response"]').value = arguments[0];
if (typeof grecaptcha !== 'undefined') {
grecaptcha.getResponse = function() { return arguments[0]; };
}
""", token)
# Step 5: Switch back to main page
driver.switch_to.default_content()
print("Token injected, switched back to main frame")
预期输出:
Sitekey: 6Le-SITEKEY-abc123
Page URL: https://example.com/checkout
Token: 03AGdBq26ZfPxL...
Token injected, switched back to main frame
JavaScript:Puppeteer 框架处理
Puppeteer 拥有一流的框架支持 - 无需手动切换。
const puppeteer = require('puppeteer');
const axios = require('axios');
const API_KEY = 'YOUR_API_KEY';
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
await page.goto('https://example.com/checkout', {
waitUntil: 'networkidle2'
});
// Step 1: Find the payment iframe
const paymentFrameHandle = await page.waitForSelector('iframe#payment-frame');
const paymentFrame = await paymentFrameHandle.contentFrame();
// Step 2: Find reCAPTCHA iframe inside the payment frame
const recaptchaFrameHandle = await paymentFrame.waitForSelector(
'iframe[src*="recaptcha/api2/anchor"]'
);
const src = await paymentFrame.evaluate(
el => el.getAttribute('src'), recaptchaFrameHandle
);
const sitekey = src.match(/k=([A-Za-z0-9_-]+)/)[1];
const pageUrl = page.url();
console.log(`Sitekey: ${sitekey}`);
// Step 3: Solve with CaptchaAI
const submitResp = await axios.post('https://ocr.captchaai.com/in.php', null, {
params: {
key: API_KEY,
method: 'userrecaptcha',
googlekey: sitekey,
pageurl: pageUrl,
json: 1
}
});
const taskId = submitResp.data.request;
let token = null;
for (let i = 0; i < 24; i++) {
await new Promise(r => setTimeout(r, 5000));
const pollResp = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: 1 }
});
if (pollResp.data.status === 1) {
token = pollResp.data.request;
break;
}
if (pollResp.data.request !== 'CAPCHA_NOT_READY') {
throw new Error(pollResp.data.request);
}
}
console.log(`Token: ${token.substring(0, 50)}...`);
// Step 4: Inject token into the payment frame
await paymentFrame.evaluate((tkn) => {
const textarea = document.querySelector(
'textarea[name="g-recaptcha-response"]'
);
textarea.value = tkn;
const callback = document.querySelector('.g-recaptcha')
?.getAttribute('data-callback');
if (callback && typeof window[callback] === 'function') {
window[callback](tkn);
}
}, token);
console.log('Token injected into payment iframe');
})();
深度嵌套的 iframe
对于三个或更多嵌套级别,链接框架开关:
硒
# Main → iframe-1 → iframe-2 → CAPTCHA
driver.switch_to.frame(driver.find_element(By.ID, "iframe-1"))
driver.switch_to.frame(driver.find_element(By.ID, "iframe-2"))
# Now extract sitekey from this context
# To go back up one level:
driver.switch_to.parent_frame()
# To go back to main:
driver.switch_to.default_content()
Puppeteer
const frame1 = await (await page.$('iframe#iframe-1')).contentFrame();
const frame2 = await (await frame1.$('iframe#iframe-2')).contentFrame();
// Extract sitekey from frame2
常见错误
| 错误 | 会发生什么 | 处理方式 |
|---|---|---|
使用主页 URL 作为 pageurl |
令牌被网站拒绝 | 使用呈现 CAPTCHA 小部件的框架的 URL |
| 不切换回父框架 | 后续操作失败 | 注入后调用switch_to.parent_frame()或switch_to.default_content() |
| 从错误的 iframe 中提取 sitekey | ERROR_WRONG_GOOGLEKEY |
sitekey位于包含.g-recaptcha的iframe上,而不是内部Google挑战iframe |
| 跨域iframe阻塞JS | 控制台中的 SecurityError |
您无法在跨源 iframe 内使用 execute_script - 从 src 属性中提取 sitekey |
常问问题
iframe 是跨域的。我还可以提取站点密钥吗?
是的。 sitekey位于iframe元素的src属性或data-sitekey属性中,可以从父框架中读取。您永远不需要在跨源 iframe 内执行 JavaScript。
我应该使用 pageurl 的父页面 URL 还是 iframe URL?
使用加载 reCAPTCHA 小部件的页面的 URL。这通常是 iframe 自己的 URL,而不是顶级页面。检查站点的 reCAPTCHA 配置进行确认。
我如何知道将token 提交哪个框架?
将token 提交到包含 textarea[name="g-recaptcha-response"] 元素的框架中 - 这始终是呈现 .g-recaptcha div 的框架。
使用 CaptchaAI 解决任意 iframe 深度的验证码
获取您的 API 密钥:验证码网站。
相关指南
- 处理单个页面上的多个验证码
- 从页面源中提取 reCAPTCHA 参数
- 使用 Selenium + Python 解决验证码