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服务。

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

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

相关文章
|
1天前
|
人工智能 JavaScript Linux
【Claude Code 全攻略】终端AI编程助手从入门到进阶(2026最新版)
Claude Code是Anthropic推出的终端原生AI编程助手,支持40+语言、200k超长上下文,无需切换IDE即可实现代码生成、调试、项目导航与自动化任务。本文详解其安装配置、四大核心功能及进阶技巧,助你全面提升开发效率,搭配GitHub Copilot使用更佳。
|
3天前
|
存储 人工智能 自然语言处理
OpenSpec技术规范+实例应用
OpenSpec 是面向 AI 智能体的轻量级规范驱动开发框架,通过“提案-审查-实施-归档”工作流,解决 AI 编程中的需求偏移与不可预测性问题。它以机器可读的规范为“单一真相源”,将模糊提示转化为可落地的工程实践,助力开发者高效构建稳定、可审计的生产级系统,实现从“凭感觉聊天”到“按规范开发”的跃迁。
654 11
|
8天前
|
存储 JavaScript 前端开发
JavaScript基础
本节讲解JavaScript基础核心知识:涵盖值类型与引用类型区别、typeof检测类型及局限性、===与==差异及应用场景、内置函数与对象、原型链五规则、属性查找机制、instanceof原理,以及this指向和箭头函数中this的绑定时机。重点突出类型判断、原型继承与this机制,助力深入理解JS面向对象机制。(238字)
|
6天前
|
云安全 人工智能 安全
阿里云2026云上安全健康体检正式开启
新年启程,来为云上环境做一次“深度体检”
1624 6
|
3天前
|
消息中间件 人工智能 Kubernetes
阿里云云原生应用平台岗位急招,加入我们,打造 AI 最强基础设施
云原生应用平台作为中国最大云计算公司的基石,现全面转向 AI,打造 AI 时代最强基础设施。寻找热爱技术、具备工程极致追求的架构师、极客与算法专家,共同重构计算、定义未来。杭州、北京、深圳、上海热招中,让我们一起在云端,重构 AI 的未来。
|
5天前
|
IDE 开发工具 C语言
【2026最新】VS2026下载安装使用保姆级教程(附安装包+图文步骤)
Visual Studio 2026是微软推出的最新Windows专属IDE,启动更快、内存占用更低,支持C++、Python等开发。推荐免费的Community版,安装简便,适合初学者与个人开发者使用。
693 11
|
8天前
|
缓存 算法 关系型数据库
深入浅出分布式 ID 生成方案:从原理到业界主流实现
本文深入探讨分布式ID的生成原理与主流解决方案,解析百度UidGenerator、滴滴TinyID及美团Leaf的核心设计,涵盖Snowflake算法、号段模式与双Buffer优化,助你掌握高并发下全局唯一ID的实现精髓。
375 160
|
8天前
|
人工智能 自然语言处理 API
n8n:流程自动化、智能化利器
流程自动化助你在重复的业务流程中节省时间,可通过自然语言直接创建工作流啦。
466 6
n8n:流程自动化、智能化利器
|
6天前
|
人工智能 Shell 开发工具
Claude Code 2.1.2超详细更新说明,小白也能10分钟上手
Claude Code 2.1.x重磅更新:Shift+Enter换行、Esc+Esc撤销、Ctrl+B后台运行,Skills技能系统全面升级,支持多语言、通配符权限与动态MCP检测,性能提升50%,迭代速度惊人,开发者效率暴涨!
Claude Code 2.1.2超详细更新说明,小白也能10分钟上手