资讯详情

资讯详情

Python+Selenium 处理 reCAPTCHA V2/V3 验证码的正规解法

在自动化测试和爬虫开发里reCAPTCHA 这三个字基本等同于“拦路虎”。很多时候脚本写得好好的一跑到表单提交就被一个“我不是机器人”的框卡住点击验证失败、页面拒绝响应、接口返回异常整套流程直接瘫痪。这篇文章不讲那些打着“破解验证码”旗号的灰色手段而是聚焦在一个更实际的问题上当你的 Python Selenium 项目被 reCAPTCHA V2/V3 挡住时在测试和开发环境里有哪些官方认可、可以稳定复现的正规解法以及在真实生产环境里为什么我劝你不要去“绕过”。全文代码和步骤都是我实测跑过的方案可以直接参考。1. 先捋清楚验证码到底拦的是什么很多人对验证码的理解就是“让我证明我是人类”但 reCAPTCHA 真正做的远不止这么简单。它不只是在判断答没答对题而是在评估“这一次浏览器交互的整体可信度”。把这一点搞明白后面所有的处理方案才有依据。1.1 为什么自动化会撞上 reCAPTCHASelenium 模拟的浏览器行为和真实人类操作之间存在大量可被量化的差异。典型的有鼠标移动轨迹人的轨迹是平滑带加速度的曲线而自动化工具的坐标跳动往往是直线或瞬时到位、键盘输入节奏真人敲键盘时快慢不一脚本通常匀速、页面停留时间分布、甚至浏览器指纹和 Cookie 历史的连续性。reCAPTCHA 在后端把这些特征综合成一个“可疑程度”再决定要不要弹验证框、弹哪种难度的验证框。这里有一个容易误解的地方V2 弹图验证码时你看到的图片识别只是一部分。真正影响判定结果的是你展开图片之前那几秒在页面上的各种行为。换句话说图片识别不是目的行为分析才是。这也是为什么很多人在“识别图片”上花了大功夫结果通过率还是不稳定。1.2 V2 和 V3 的验证逻辑差异V2 和 V3 是两个设计思路完全不同的版本。V2 走的是“显式打断”用户在关键操作前必须主动完成一次验证。V3 走的是“后台打分”页面加载时它在后台默默给当前会话打一个 0.0 到 1.0 的分数网站拿到分数后自己在服务端设阈值比如低于 0.5 就不让提交。V3 没有验证框看起来“无感”但这不代表它对自动化更友好。相反V3 对行为特征的采集范围更广而且它在服务端会把分数和这个会话的浏览器指纹、IP 信誉、Cookie 历史绑在一起。我在测试里观察过一个有意思的现象同一个 Selenium 实例跑同一个测试页V2 模式下时不时还能通过但 V3 的分数经常稳定在 0.1 到 0.3。这个差异说明 V3 对自动化特征的识别优先级更高。对比项reCAPTCHA V2reCAPTCHA V3是否打断用户是需要显式验证否后台运行判定依据图片识别 行为特征行为特征综合评分默认阈值策略服务端判断网站自行设置阈值对自动化友好度较低偶有通过很低分数普遍偏低官方测试支持测试密钥直接通过测试密钥固定返回高分理解了这两个版本的差异你就会明白一个道理任何号称“一键绕过”的方案本质都是在跟一个不断更新的行为分析系统做对抗今天能用不代表明天能用。2. 自动化测试场景下的正规解法如果是在写自动化测试用例最不需要纠结的就是验证码问题。reCAPTCHA 官方本身就提供了测试专用的 sitekey 和 secret key配好之后验证码在测试环境里会自动通过根本不给你添堵。这套方案不是我发明的是官方文档明明白白写出来的测试支持能力。2.1 官方测试密钥最简单的“通行证”reCAPTCHA 提供了一套长期有效的测试密钥作用就是给开发者联调和测试用。用它配置的页面V2 模式下勾选会自动判定通过V3 模式下会稳定返回最高分。核心参数如下sitekey6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhIsecret key6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe这套密钥必须是配套使用的如果 sitekey 用了测试的、secret key 用了真实环境的服务端校验直接失败。我在项目里栽过这个跟头当时测了半天前端看起来验证通过了一提交后端报invalid-key-response查了半天才发现是密钥配对出了问题。所以这里有一个最基本的检查顺序前端验证成功与否和后端校验是否成功是完全独立的两件事要分开排查。另外要特别注意一点测试密钥只能配到你自己的测试环境绝对不要因为图省事把它写到生产配置里。测试密钥的特性是“永远通过”也就是说只要它上线你的产品验证码就等于全关了到时候被脚本灌数据、被恶意提交都不关 reCAPTCHA 的事因为脚本也全部能通过。这个后果远比想象严重。2.2 Selenium 集成 reCAPTCHA 测试页面的完整步骤拿到了测试密钥接下来就是把测试页面搭出来。我在本地验证时的做法是写一个最小的 HTML 页面引入 reCAPTCHA 的脚本用 V2 checkbox 模式跑一遍 Selenium 操作。HTML 测试页的关键结构如下!DOCTYPE html html head script srchttps://www.recaptcha.net/recaptcha/api.js async defer/script /head body form idcaptcha-form div classg-recaptcha>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 driver webdriver.Chrome() driver.get(http://localhost:8000/captcha-demo.html) # 关键点reCAPTCHA 的 iframe 加载速度不稳定一定要用显式等待 iframe WebDriverWait(driver, 10).until( EC.frame_to_be_available_and_switch_to_it( (By.CSS_SELECTOR, iframe[src*recaptcha]) ) ) # 勾选框在 iframe 内部 checkbox WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, .recaptcha-checkbox-border)) ) checkbox.click() # 验证成功后 iframe 内会出现一个带对勾的遮罩层 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, .recaptcha-checkbox-checked)) ) # 操作完一定要切回主文档否则后续定位主页面元素会报错 driver.switch_to.default_content()这套流程我跑了多次稳定性和可复现性都不错。有一点值得说明iframe[src*recaptcha]这个选择器匹配的是 reCAPTCHA 的 iframe src 中包含recaptcha关键字的那个节点比写死完整 src 要稳因为不同模式下 iframe 的完整地址可能带不同的参数。2.3 处理 iframe 和交互细节再往下抠一些细节。reCAPTCHA 的 DOM 结构在不同版本和不同渲染模式下差异很大V2 checkbox 模式、V2 invisible 模式、V3 模式iframe 的级别和数量都不一样。V2 checkbox 模式通常只有一个 iframe。V2 invisible 模式没有可见的勾选框它会在表单提交时静默触发验证Selenium 侧基本不需要专门跟它交互。V3 模式连 iframe 都没有它是通过页面脚本在后台调grecaptcha.execute()拿 tokentoken 会写进一个隐藏的 textarea 里。所以如果你想在测试里拿到 V3 的 token 值正确姿势是从这个隐藏 textarea 里读取而不是试图去点任何可见元素。我写过一个简单的读取逻辑from selenium.webdriver.common.by import By token driver.find_element(By.NAME, g-recaptcha-response).get_attribute(value) print(token)如果拿到的 token 是空的大概率是页面脚本还没执行完或者grecaptcha.execute()没有触发。这时候用WebDriverWait去轮询这个 textarea 的值比固定sleep靠谱得多因为 reCAPTCHA 的脚本加载时间在不同网络环境下差异非常大我见过最慢的要十几秒才返回 token。还有一个小技巧在测试用例最后最好断言一下g-recaptcha-response这个 textarea 的 value 非空而不是断言出现了某个元素。因为有 value 才代表验证流程在脚本层面走通了这是更接近“业务成功”的判断标准。3. 如果被测系统不允许用测试密钥怎么办很多项目不会为每个环境单独准备一套验证码配置尤其是一些内部系统前端的 sitekey 写死在代码里你既不能改页面也拿不到对应的 secret key。这种情况更要学会用正规手段绕开验证码对测试流程的干扰。3.1 本地模拟验证码通过的 Mock 方案思路很简单前端加载的 reCAPTCHA 脚本来自外部域名那我就在测试环境里把这个外部脚本替换成一个本地假脚本让它不加载真实逻辑直接执行成功回调。具体做法分两步。第一步用本地 Web 服务托管一个假的 reCAPTCHA API// mock-recaptcha.js window.grecaptcha { ready: function (cb) { cb(); }, render: function (container, params) { // 模拟渲染完成直接触发成功回调 if (params typeof params.callback function) { params.callback(mock-token); } }, execute: function (sitekey, options) { return Promise.resolve(mock-token); }, reset: function () {} };第二步在页面中把引入脚本的地址改成本地这个假脚本。如果是 Selenium 直接控制浏览器可以用 Chrome DevTools Protocol 的Page.addScriptToEvaluateOnNewDocument在页面任何脚本执行之前先注入 mock。代码示意from selenium import webdriver driver webdriver.Chrome() # 在每次页面加载前立刻注入 mock 逻辑 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: window.grecaptcha { ready: function(cb) { cb(); }, render: function(container, params) { if (params typeof params.callback function) { params.callback(mock-token); } }, execute: function(sitekey, options) { return Promise.resolve(mock-token); }, reset: function() {} }; }) driver.get(http://your-test-page/)这个方案的好处是彻底绕开了外部脚本的网络依赖测试环境断网也能跑。但要注意mock 逻辑必须和被测页面实际用的 reCAPTCHA 版本匹配。如果页面用的是 V2 模式且依赖grecaptcha.render你的 mock 就要重点实现 render如果页面用的是 V3 模式且依赖execute那 mock 的重点就是 execute。写反了的话页面会报grecaptcha is not defined或者调不到对应方法。这里有一个我在接手别人项目时发现的小坑被测页面里的 reCAPTCHA 脚本往往不是等着回调用它会调用grecaptcha.render并把返回的 widgetId 保存下来后续提交时再调用grecaptcha.getResponse(widgetId)。如果你的 mock 只实现了 render 而没有保存 widgetId提交阶段还是拿不到 token。所以 mock 时最好把 widgetId 用一个变量存起来getResponse 直接返回 mock-token 或者读取里面存的状态。3.2 在测试环境关闭验证码开关Mock 脚本是前端层面的处理还有一种更粗暴但很有效的思路从代码入口开关入手。很多系统的验证码接入并不是完全不可分割的代码里通常有一个判断环境变量的地方来决定要不要渲染 reCAPTCHA。有些系统是后端模板里判断有些是前端配置里判断。在测试环境把对应的开关关掉前端不渲染验证码后端也不校验 token整个流程就走通验证之前的所有逻辑。这个操作看起来简单但里面有一个容易忽视的细节如果页面前端不渲染验证码那么原本被 reCAPTCHA 脚本绑定的提交逻辑可能会变化。比如原本提交按钮是 reCAPTCHA 的callback触发后再打开的验证码一关提交按钮可能一直处于禁用状态。我遇到过好几次这样的情况明明关了验证码按钮反而点不了了最后发现是页面的 JavaScript 还在等grecaptcha.getResponse()的返回结果。所以关闭验证码开关的同时要检查一遍前端是否有依赖验证码回调的业务逻辑有的话一并调整。3.3 用请求级集成测试跳过 UI 验证如果你测的重点是后端接口逻辑而不是前端交互那就完全没必要非走 Selenium。直接在接口测试层面对 secret key 的校验做 mock。常见的做法是本地启动一个假的/siteverify服务。reCAPTCHA 的服务端校验逻辑是前端拿到 token 后后端拿着这个 token 加上 secret key向 reCAPTCHA 的验证接口发起请求验明正身。这个验证接口的 URL 是可配置的你可以把它指到本地的一个假服务让它永远返回{success: true, score: 1.0}。这样被测系统的后端以为自己完成了验证实际上走的是你的本地 mock。对 Python 项目来说可以用 unittest.mock 直接替换发送请求的那个网络函数比起假服务更快from unittest import mock fake_response {success: True, score: 1.0} with mock.patch(your_project.recaptcha.verify_captcha_token) as mock_verify: mock_verify.return_value fake_response # 执行你的业务测试代码 result your_business_function()请求级 mock 的优点是速度快、不依赖浏览器、不依赖网络适合大批量回归测试。缺点很明显它没有覆盖到前端页面加载 reCAPTCHA 脚本、生成 token 这条完整链路。所以我的建议是分层处理前端交互的真实性交给 Selenium 的少量冒烟用例业务逻辑的大批量覆盖交给请求级 mock两边互补既保证关键链路真实性又保证测试效率。4. 为什么“绕过”方案靠不住这个行业里从来不缺“XXX 一键过验证码”的工具但我可以直接说结论生产环境下的 reCAPTCHA真正稳定可靠的绕过手段基本不存在存在的话也不会被公开分享。这里我不去讨论具体工具只从技术原理上讲清楚为什么那些方案靠不住。4.1 V3 的风险评分不是只靠答题我在前面提过V3 评估的是整个会话的可信度。这意味着什么意味着你通过的这次验证表面上拿到一个 token但这个 token 是跟它生成时的浏览器环境绑定的。拿下这个 token 的人如果换一个环境去提交服务端一对照就能发现指纹不一致直接给你判定为攻击流量。V3 的评分会看很多东西鼠标移动曲线、键盘事件的时间分布、页面滚动节奏、设备指纹、IP 的过去行为记录、Cookie 的年龄和来源。一个刚从数据中心 IP 发起的请求即使模拟了所有鼠标动作IP 信誉这一项基本就拉低了整体分数。所以你会发现一个现象很多本地看起来“完美通过”的操作一旦放到服务器上跑分数立刻变成 0.1。原因就是这个打分是综合的不是单个环节的比拼。我遇到过不少不信邪的开发者反复问“我模拟了真实用户行为为什么还是低分”。答案很简单真实用户行为不是一个动作两个动作而是几十个特征在几分钟内的持续稳定输出。你的脚本可以模拟鼠标轨迹但很难模拟一个人在看一段长文章时偶尔停顿、回滚、思考的节奏。与其去跟这种系统死磕不如把自己的自动化流程做得合规合法。4.2 那些“一键破解”工具的代价从实际成本角度看市面上的采集识别服务和打码服务存在三个问题。第一是稳定性差验证码策略一变服务跟着失效你今天接入明天可能就不能用了。第二是成本不划算应对少量验证码可能还能承受一旦流量上来打码成本会拖垮整个项目的经费预算。第三是风险不可控把自动化流量交给第三方服务等于把重要的业务链路交给一个不受你控制的中间环节安全性、数据隐私都面临不可预知的问题。更关键的是这些手段一不小心就跨过了合规边界。验证码本来是为了防止自动化滥用而存在的安全问题你想办法去突破它在法律上可能被认定为规避安全措施这个后果不是技术能力能兜底的。我的观点很明确技术方案的选型之前先画清楚合规红线。生产环境的验证码该走人工审核就走人工审核该降低自动化频率就降低频率这不是技术妥协这是工程判断。5. 常见问题与排查实录写到这里我把自己在实际调试中遇到的高频问题整理成一个速查表。如果你是按前面的方案做的可以把这些问题作为排查第一参考。现场现象可能原因排查顺序iframe 等了 10 秒还找不到reCAPTCHA 脚本加载失败或网络原因加载缓慢先看浏览器 network 面板有没有 api.js 请求再确认脚本地址用的是不是recaptcha.net勾选框点击后没反应点击的节点不对或 iframe 内部脚本尚未初始化完成换.recaptcha-checkbox-border再试点击前加显式等待测试密钥配置了但 V2 弹图验证sitekey 写错了或页面实际加载的是另一个环境的配置检查页面源码确认渲染出的 sitekey 是不是测试密钥V3 的 token 一直为空grecaptcha.execute()未触发或回调逻辑依赖用户行为在 console 手动执行grecaptcha.execute().then(console.log)观察是否正常出值后端校验一直报密钥错误secret key 配错或者用了测试 sitekey 真实 secret key核对密钥配对测试密钥必须一套一起用本地环境点了验证码但提交后仍被拦后端拿到 token 后请求验证接口的网络路径不通检查服务端是否能访问 reCAPTCHA 验证接口部分内网环境需要配置网络白名单5.1 测试密钥在 V2 上点了没反应这个我查过很久。表面现象是勾选框可以点但点完不会像预期那样直接通过而是没有任何反馈。后来发现是手上这个测试页面引用的 reCAPTCHA 脚本版本和 sitekey 配置方式不匹配。现在 reCAPTCHA 推荐用render参数显式渲染而不是老式的>
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →