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

简介: 验证码是自动化测试的常见难题。本文总结了Playwright处理验证码的五种实用方案:测试环境禁用、凭证缓存、OCR识别、智能重试及专项处理滑动/点选验证码,并提出分层策略与最佳实践,助你提升测试稳定性,兼顾效率与合规。

验证码(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;
}
}
}
}
最佳实践建议
根据我们项目的经验,我推荐以下策略:

分层处理策略:

// 策略优先级:禁用 > 缓存 > 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('无法处理验证码');

}
}
验证码监控:

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

// 根据环境选择不同策略
const CAPTCHA_CONFIG = {
development: { strategy: 'disable' },
staging: { strategy: 'cached_token' },
production: { strategy: 'mixed', fallback: 'ocr' }
};
总结
验证码的处理没有一劳永逸的方案,但通过组合策略,我们基本能保证自动化测试的稳定性。我们团队目前的方案是:测试环境完全禁用,预发环境使用缓存令牌,只有少量的生产环境监控脚本会使用OCR服务。

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

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

相关文章
|
25天前
|
人工智能 Cloud Native 测试技术
2026大厂测试技术栈全景:新人该学什么?
2026年大厂测试技术栈全景:Playwright成自动化首选,k6+云真机+契约测试普及,AI辅助提效。测试工程师需从“质量检查”转向“质量工程”,掌握主流工具,保持技术敏感,以实战能力应对变化。
|
1月前
|
人工智能 架构师 安全
软件测试没有天花板,从“工具人”到“质量架构师”的破局之路
软件测试的瓶颈从不在于岗位,而在于能力边界与职业规划。18年老兵亲述:手工测试只是起点,真正的出路在于技术深耕、管理进阶或跨界转型。打破认知陷阱,拥抱AI与业务双轮驱动,用“T型能力”拓展职业天花板。你的未来,由每一次主动选择决定。
|
6月前
|
Web App开发 开发框架 前端开发
Playwright与PyTest结合指南
本教程介绍如何结合Playwright与PyTest进行Web自动化测试,涵盖环境搭建、测试编写、配置管理、Fixtures使用及高级技巧,助你高效构建稳定、可维护的测试方案。
|
XML Java 数据库连接
IDEA添加Mapper.xml文件模板
IDEA添加Mapper.xml文件模板
IDEA添加Mapper.xml文件模板
|
2月前
|
XML 人工智能 自然语言处理
禅道文档 300 条用例一键生成:一次看懂爱测智能化测试平台的实力
测试团队面临需求碎片化、迭代加速的挑战,传统用例编写效率瓶颈凸显。爱测智能化测试平台借助生成式AI,实现从需求文档自动生成多场景、多格式、可执行的测试用例。通过大模型理解文档、智能体配置、知识图谱与自然语言驱动执行,平台几分钟内生成近300条高覆盖用例,支持导出至禅道等系统,全链路自动化。未来测试的竞争,是“会用AI”与“不会用AI”的差距。
|
2月前
|
前端开发 测试技术 开发者
Cypress:架构原理与环境设置全解析
Cypress 以开发者体验为核心,通过内嵌浏览器运行、双引擎架构与智能命令队列,实现高速稳定的端到端测试。本文深入解析其工作原理,系统讲解环境搭建、配置管理、数据模拟与工程化落地实践,助你构建可靠自动化测试体系,提升团队质量效能。
|
29天前
|
人工智能 安全 测试技术
新手也能用好AI:10个提示词技巧,让测试效率翻倍
不是工具不行,而是你会不会问。同样用AI,有人生成普通用例,有人却挖出弱网登录、暴力破解等深层场景。差距在哪?在于提问方式。本文揭秘4大指令技巧:赋予角色、拆解任务、指定格式、提供范例,助你用精准提示词激发AI潜能,提升测试效率。附10个新手实战模板,从写用例到报Bug,一学就会。掌握“好指令”,让AI成为你的高效助手。
|
2月前
|
人工智能 算法 数据可视化
别卷手工测试了!这6个大模型应用场景让你身价翻倍
大语言模型正重塑软件测试:从AI生成用例、智能代码审查到需求深度解析,推动测试自动化、智能化升级。测试工程师需掌握AI协同技能,聚焦高阶质量设计,实现职业跃迁。
|
2月前
|
人工智能 架构师 算法
AI时代,测试工程师的自我重塑
当AI能生成测试用例、预测缺陷,测试工程师的未来何在?答案不是被取代,而是进化。AI将接管重复劳动,释放人力投身复杂逻辑、用户体验与质量体系设计。未来的测试专家需成为AI训练师、质量架构师,深耕机器不擅长的领域。人机协同,方见真章。
|
24天前
|
JavaScript 前端开发 测试技术
Playwright高级技巧:自定义选择器与定位器
在Web自动化测试中,面对动态元素和脆弱的选择器,Playwright的自定义选择器与定位器提供了强大解决方案。通过注册如`testId`、穿透影子DOM等策略,结合可组合的定位链与页面对象模式,可大幅提升测试稳定性与可读性。推荐以`data-testid`为基础,逐步构建清晰、健壮的定位体系,让测试不再因样式或结构变动而频繁失败。