Playwright处理验证码的自动化解决方案

验证码(CAPTCHA)一直是自动化测试中最让人头疼的环节之一。每次碰到那些扭曲的文字、点选图片的挑战,自动化脚本就像撞上了一堵墙。我负责的电商项目最近就卡在了登录自动化这个环节——那个该死的滑动验证码让我们的回归测试屡屡失败。

经过几周的实战踩坑和方案对比,我总结出几种用Playwright处理验证码的可行方案。这些方案各有适用场景,没有绝对的“银弹”,但足够帮你绕过大多数验证码障碍。

方案一:最直接的方式——测试环境关闭验证码

如果你们公司有测试环境的管理权限,这是最干净利落的解决方案。

// 在测试环境中,通过修改配置或调用管理接口禁用验证码 asyncfunction disableCaptchaInTestEnv(page) { // 方式1:如果有管理后台接口 await page.goto('http://test-admin.example.com/features'); await page.click('#toggle-captcha'); // 方式2:通过设置测试用户白名单 await page.evaluate(() => { localStorage.setItem('bypass_captcha', 'true'); }); // 方式3:修改hosts或使用mock服务(需运维配合) console.log('验证码已在测试环境关闭'); }

优点:零成本,100%稳定,执行速度快。
缺点:仅限测试环境,生产环境模拟不了完整流程。

方案二:半自动方案——人工介入一次,重复使用凭证

对于无法关闭验证码但又不频繁变更的场景,这个方案很实用。

const fs = require('fs'); const path = require('path'); class CaptchaHandler { constructor() { this.tokenFile = path.join(__dirname, '.auth_token'); } async handleCaptcha(page) { // 检查是否有缓存的登录凭证 if (fs.existsSync(this.tokenFile)) { const token = fs.readFileSync(this.tokenFile, 'utf8'); awaitthis.useCachedToken(page, token); returntrue; } // 首次需要人工处理 console.log('请手动完成验证码验证...'); // Playwright会暂停,等待人工操作 await page.pause(); // 这是关键!手动完成后按回车继续 // 保存获取到的凭证(如cookie、token) const cookies = await page.context().cookies(); const authToken = cookies.find(c => c.name === 'auth_token'); if (authToken) { fs.writeFileSync(this.tokenFile, authToken.value); console.log('凭证已保存,后续测试将自动使用'); } returntrue; } async useCachedToken(page, token) { // 使用缓存的token设置cookie await page.context().addCookies([{ name: 'auth_token', value: token, domain: 'your-domain.com', path: '/' }]); // 刷新页面使cookie生效 await page.reload(); } } // 使用示例 const handler = new CaptchaHandler(); await handler.handleCaptcha(page); await page.goto('https://your-app.com/dashboard');

优点:平衡了自动化与可靠性,只需人工介入一次。
缺点:凭证过期后需要重新人工处理。

方案三:全自动方案——第三方OCR服务

当需要完全自动化且验证码不算太复杂时,可以考虑OCR方案。

const axios = require('axios'); const fs = require('fs'); asyncfunction solveCaptchaWithOCR(page) { // 1. 定位并截图验证码元素 const captchaElement = await page.$('.captcha-image'); const screenshotPath = 'captcha.png'; await captchaElement.screenshot({ path: screenshotPath }); // 2. 读取图片并编码为base64 const imageBuffer = fs.readFileSync(screenshotPath); const base64Image = imageBuffer.toString('base64'); try { // 3. 调用OCR API(这里以2Captcha为例,需注册获取API key) const apiKey = process.env.CAPTCHA_API_KEY; const response = await axios.post('https://2captcha.com/in.php', { key: apiKey, method: 'base64', body: base64Image, json: 1 }); const requestId = response.data.request; // 4. 轮询获取结果 let result = null; for (let i = 0; i < 30; i++) { await page.waitForTimeout(2000); const checkResponse = await axios.get( `https://2captcha.com/res.php?key=${apiKey}&action=get&id=${requestId}&json=1` ); if (checkResponse.data.status === 1) { result = checkResponse.data.request; break; } } if (!result) thrownewError('OCR识别超时'); // 5. 输入识别结果 await page.fill('#captcha-input', result); await page.click('#submit-btn'); returntrue; } catch (error) { console.error('OCR识别失败:', error.message); // 失败时保存截图供后续分析 fs.renameSync(screenshotPath, `failed_${Date.now()}.png`); returnfalse; } }

费用提示:2Captcha每1000次识别约$1-3,具体看复杂度。对于大型测试套件,这是笔不小的开销。

方案四:智能等待与重试机制

有时候验证码的出现是有条件的,可以通过优化测试逻辑来减少触发。

async function smartLogin(page, username, password) { const MAX_RETRIES = 3; for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { try { await page.goto('https://example.com/login'); // 填写登录表单 await page.fill('#username', username); await page.fill('#password', password); // 检测验证码是否出现 const captchaVisible = await page.isVisible('.captcha-container'); if (captchaVisible) { console.log(`第${attempt}次尝试出现验证码,尝试绕过...`); // 尝试刷新验证码(有时新的验证码更简单) await page.click('.refresh-captcha'); await page.waitForTimeout(1000); // 这里可以集成上述的任一解决方案 // await solveCaptchaWithOCR(page); // 或者使用备用账号 if (attempt > 1) { await page.fill('#username', `${username}_backup`); } } // 提交登录 await page.click('#login-btn'); // 等待登录成功标志 await page.waitForSelector('.user-dashboard', { timeout: 5000 }); console.log('登录成功!'); returntrue; } catch (error) { console.log(`第${attempt}次登录尝试失败: ${error.message}`); if (attempt === MAX_RETRIES) { thrownewError(`登录失败,已重试${MAX_RETRIES}次`); } // 等待一段时间后重试 await page.waitForTimeout(2000); } } }

方案五:针对特定类型验证码的专项处理

滑动验证码处理

async function handleSlideCaptcha(page) { const slider = await page.$('.slider'); const sliderBox = await slider.boundingBox(); const target = await page.$('.slider-target'); const targetBox = await target.boundingBox(); // 计算需要滑动的距离 const slideDistance = targetBox.x - sliderBox.x; // 模拟人类滑动(先快后慢) await slider.hover(); await page.mouse.down(); // 分段滑动,模拟真实轨迹 const steps = 10; const stepDistance = slideDistance / steps; for (let i = 0; i < steps; i++) { // 越靠近目标越慢 const speed = 50 + Math.random() * 100 - i * 10; await page.mouse.move( sliderBox.x + stepDistance * (i + 1), sliderBox.y + (Math.random() * 10 - 5), // 加入微小垂直抖动 { steps: 1 } ); await page.waitForTimeout(speed); } await page.mouse.up(); }

点选文字验证码(简单版)

async function handleClickCaptcha(page) { // 获取需要点击的文字 const promptText = await page.$eval('.captcha-prompt', el => el.textContent); const wordsToClick = promptText.match(/点击【(.*?)】/)[1].split(''); // 获取所有可点击的文字元素 const charElements = await page.$$('.captcha-char'); for (const char of wordsToClick) { for (const element of charElements) { const text = await element.textContent(); if (text === char) { // 随机延迟后点击,模拟人类反应时间 await page.waitForTimeout(300 + Math.random() * 500); await element.click(); break; } } } }

最佳实践建议

根据我们项目的经验,我推荐以下策略:

  1. 分层处理策略

    // 策略优先级:禁用 > 缓存 > OCR > 重试 class CaptchaStrategy { async solve(page) { if (awaitthis.tryDisableCaptcha(page)) return; if (awaitthis.tryCachedToken(page)) return; if (awaitthis.tryOCR(page)) return; if (awaitthis.tryAlternativeAccount(page)) return; // 最后手段:标记测试失败并保存截图 awaitthis.saveDebugInfo(page); thrownewError('无法处理验证码'); } }
  2. 验证码监控

    // 记录验证码出现频率,用于优化测试策略 const captchaStats = { totalAttempts: 0, captchaShown: 0, successRate: 0, lastCaptchaTime: null };
  3. 环境感知配置

    // 根据环境选择不同策略 const CAPTCHA_CONFIG = { development: { strategy: 'disable' }, staging: { strategy: 'cached_token' }, production: { strategy: 'mixed', fallback: 'ocr' } };

总结

验证码的处理没有一劳永逸的方案,但通过组合策略,我们基本能保证自动化测试的稳定性。我们团队目前的方案是:测试环境完全禁用,预发环境使用缓存令牌,只有少量的生产环境监控脚本会使用OCR服务。

最后提醒一点:尊重网站的验证码机制。这些措施旨在提升测试效率,而不是滥用或攻击服务。对于特别复杂的验证码(如行为验证),与其花费大量精力破解,不如考虑与开发团队协商,为自动化测试提供专门的测试接口或令牌。

如果你有更好的验证码处理方案,欢迎在评论区分享——毕竟,每个项目的验证码实现都可能不一样,多交流才能少踩坑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1170140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20260116紫题训练总结 - Link

A - 算术天才⑨与等差数列 简单题,切了。考虑用线段树维护 \(\max\)、\(\min\)、\(\gcd\) 和每个数字前面最后一个等于祂的数字的位置,判断是简单的。 B - Building Bridges 由于 \(T1\) 写+调了一整场,根本没看题。…

【2026目标检测】高质量模型汇总

目标检测模型选型指南&#xff1a;从高精度慢模型到实用型算法全盘点 在目标检测领域&#xff0c;模型的速度与效果往往难以两全&#xff0c;有一批模型虽运行速度较慢&#xff0c;但凭借出色的检测效果占据一席之地&#xff0c;同时还有各类实用型算法和框架可供选择&#xff…

工具Cursor(三)MCP(1)介绍

一、在哪里添加McpServers 1、位置 Cursor是一个很好的Mcp Client&#xff0c;可以通过Cursor Setting--Tools & MCP --New Mcp Server来管理mcp tools。 添加之后都会展示在tools列表&#xff1a; 2、mcpServers 与 MCP 协议的边界 这是很多人会混淆的地方&#xff1a;…

拥有AI员工,才发现误会了领导

人工智能爆火三年&#xff0c;大模型和AI工具好用之后&#xff1a;职场从个人单刷模式&#xff0c;转变成带几个AI助手打团战&#xff0c;可以更高效的干活&#xff0c;但节奏却慢不下来。打工人成领导&#xff0c;不知薪水涨多少&#xff1f;虽说只是几个AI助手&#xff0c;但…

阿里千问落地谷歌UCP+A2UI,中国率先进入AI办事时代

刚刚&#xff0c;阿里千问App上线千问任务助理1.0&#xff0c;目前可以通过客户端申请邀测。千问打通了淘宝、支付宝等核心业务&#xff0c;标志着中国互联网正式进入AI办事时代。AI从单纯的对话框聊天&#xff0c;迈向了真正的办事助手。前不久&#xff0c;谷歌在大洋彼岸联合…

浙大陆展团队突破铁催化难题,实现高效氢联硅化反应 | 乐研试剂

在有机硅化学与合成化学的前沿领域&#xff0c;如何在不破坏关键Si–Si键的前提下&#xff0c;实现联硅前体的高选择性官能团化&#xff0c;一直是困扰研究人员的重大挑战。近日&#xff0c;浙江大学化学系陆展教授及其合作团队在联硅化学领域取得里程碑式突破。他们创新性地设…

P3349 [ZJOI2016] 小星星 - Link

先枚举一个集合 \(S\),设状态 \(f_{i,j}\) 表示树上 \(i\) 号点对应图上 \(j\) 号点 \((j\in S)\) 的方案数(可以多个树上的点对应一个图上的点)。转移是简单的。最后对于集合 \(S\),有容斥系数 \((-1)^{\left|S\r…

企业如何破解业法财融合痛点?AI风控探针的 4 个落地步骤

本文由幂律智能团队发布&#xff0c;核心探讨了 2026 年法律科技的关键技术——AI 风控探针。文章详细拆解了 AI 如何通过多 Agent 协作模式解决业法财深度融合中的数据割裂难题。重点涵盖&#xff1a;1. 如何通过拆解任务解决大模型幻觉&#xff0c;使合同审查准确率提升至 95…

【RAG召回排序】2025最全排序模型梳理

2025年检索重排模型全景盘点&#xff1a;从顶尖榜单到实用工具 在检索增强生成&#xff08;RAG&#xff09;和智能搜索领域&#xff0c;检索与重排模型的性能直接决定了系统的最终效果。今天我们就从权威排行榜出发&#xff0c;盘点当前最受关注的模型与工具。 一、权威检索重…

Nature发表、Science点赞!清华揭秘AI让科学家走捷径却让科学走窄路

AlphaFold获得诺贝尔奖标志着人工智能工具已深入科学的核心地带。清华大学一项基于41,298,433篇论文的深度研究揭示了一个令人深思的悖论。AI显著提升了科学家的个人产出与职业进程&#xff0c;却导致整个科学探索的领域变得狭窄且固化。该研究发表在Nature上&#xff0c;而且被…

AI技术唾手可得的时代,挖掘新需求是产品突围的关键——某知名聚合DNS管理系统的需求洞察

a.内容描述 核心功能定位&#xff1a;该项目是一个聚合DNS管理系统&#xff0c;旨在为用户提供一个统一的Web界面&#xff0c;集中管理分布在多个主流云服务商和DNS服务平台的域名解析记录。其核心定位是解决用户在多平台间切换管理DNS解析的繁琐问题。关键应用场景&#xff1a…

编程已终结!AI时代的原生智能软件架构长啥样?Claude给了个指南

近期&#xff0c;完全由 Claude code 自主编程开发软件已经成为现实&#xff0c;人们惊呼编程已经终结&#xff0c;该领域的奇点已至&#xff1a;革了程序员再革打工人&#xff1a;Anthropic 发布 Cowork&#xff0c;Claude Code 走进数字办公自动化。 那AI时代的软件应该如何…

安卓神器 --- 浏览器 之 yandex 狐猴浏览器 chrome firefox

安卓神器 --- 浏览器 之 yandex 狐猴浏览器 chrome firefox安卓神器 --- 浏览器 之 yandex 狐猴浏览器 chrome firefox

P11714 [清华集训 2014] 主旋律 Sol

计数神题。 题目链接 前言 这篇题解没有什么特别的,纯粹是快速题解区变换。仅在一些地方加上了自己的理解,希望会有所帮助。 做本题之前,可以先看看P6846 [CEOI 2019] Amusement Park,可能会有所启发。 解题思路 D…

GD5F1GM7UEYIGR:兆易创新1Gbit SPI NAND闪存,高效低功耗

品牌&#xff1a;兆易创新(GigaDevice) 型号&#xff1a;GD5F1GM7UEYIGR 容量&#xff1a;1Gbit 产品类型&#xff1a;NAND FLASH 接口类型&#xff1a; 标准SPI接口&#xff0c;支持1、2、4线模式。这意味着它只需要极少&#xff08;通常6-7个&#xff09;的GPIO引脚即可实…

夏天还不算开始——我,不会退役

或许每个人都会遇到吧,起起落落才是常态。一切过往,皆为序章。 :::align{right} ——莎士比亚 :::十月底,浙江从夏入冬,没有秋天。桂花?夹在冷热间开了一个星期,落完了。 具体经历,我不想回忆了。 2025/11/1 mo…

4B超越8B比肩30B!清华、面壁智能端侧智能体天花板开源

清华大学自然语言处理实验室&#xff08;THUNLP&#xff09;、中国人民大学、面壁智能&#xff08;ModelBest&#xff09;与OpenBMB开源社区联合发布并开源了端侧智能体模型AgentCPM-Explore。该模型仅凭4B参数规模便在深度探索类任务上取得了同尺寸模型的SOTA&#xff08;Stat…

企业软件供应链安全治理立项,方案书/立项书该怎么写?

当CTO或安全负责人指示“今年要把软件供应链安全做起来”时&#xff0c;很多项目负责人往往会陷入迷茫&#xff1a;“到底是应该买个SCA工具扫一扫&#xff1f;还是建立一套复杂的流程呢&#xff1f;我的项目立项书/方案书到底应该怎么写&#xff1f;后续的落地要怎么规划呢&am…

[Non] 字符串问题

字符串问题 大意 插入字符,查询字符。 初始串 \(s\), \(|s| \le 10^6\)。 思路 可以用平衡树,但是我选择更为强势的 STL 中的 rope。 头文件:#include<ext/rope> crope r1; // 存储 char 的 rope w…

谷歌Veo 3.1更新:更一致性、更具创造力和控制力

刚刚&#xff0c;谷歌Veo 3.1发布更新。通过素材转视频功能&#xff0c;实现了角色身份、背景与物体在动态场景中的高度一致性&#xff0c;并首次支持原生竖屏生成与4K超分画质&#xff0c;彻底打通了从静态图像到专业级动态叙事的创作链路。三大更新都是当前市场需求最大的领域…