0

0

Puppeteer自动化:处理动态密码键盘点击与XPath策略

聖光之護

聖光之護

发布时间:2025-11-07 19:09:23

|

715人浏览过

|

来源于php中文网

原创

Puppeteer自动化:处理动态密码键盘点击与XPath策略

在使用puppeteer进行自动化测试时,处理动态密码键盘这类非标准输入组件常遇到点击失效问题,表现为`node is either not clickable or not an htmlelement`错误。本教程将详细介绍如何通过将密码拆分为字符、利用xpath精确匹配键盘按键,并结合shift键处理大小写,从而有效解决此类复杂ui元素的交互挑战,确保自动化流程的稳定执行。

自动化动态密码键盘点击的挑战

在许多银行或安全敏感的网站中,为了提高安全性,会采用动态密码键盘(虚拟键盘)来代替传统的文本输入框。这种键盘的按键通常是动态渲染的HTML元素,它们的ID可能不固定,或者在页面加载后才出现。当尝试使用Puppeteer直接选择并点击这些元素时,可能会遇到以下问题:

  1. 元素未就绪: 在尝试点击时,元素可能尚未完全渲染或处于可交互状态。
  2. 元素选择不准确: 仅通过类名或部分ID可能无法唯一且稳定地选中特定按键。
  3. Node is either not clickable or not an HTMLElement错误: 这通常发生在Puppeteer试图点击一个实际上不可点击的元素(例如,它被其他元素遮挡,或者它不是一个可交互的HTML元素,如<a>、<button>、<input>等)。

为了克服这些挑战,我们需要一种更精确、更健壮的元素选择和交互策略。

解决方案:XPath与字符级交互

核心解决方案在于结合以下策略:

  1. 字符级密码输入: 将密码分解为单个字符,逐个模拟点击键盘上的对应按键。
  2. XPath精确选择: 利用XPath的强大功能,根据元素的文本内容和类名来精确匹配动态键盘上的每个按键。
  3. 处理特殊按键: 对于大小写字母,需要模拟“Shift”键的按下和释放操作。
  4. 健壮的等待机制: 在每次交互前,确保目标元素已经加载并可点击。

1. 准备工作与基本设置

首先,确保您的项目中已安装Puppeteer。

const puppeteer = require('puppeteer');

为了提高代码的可读性和复用性,我们可以创建一个辅助函数来封装“等待元素并点击”的常见操作。

阿里妈妈·创意中心
阿里妈妈·创意中心

阿里妈妈营销创意中心

下载
// 辅助函数:等待元素出现并点击
async function waitClick(page, selector, isXPath = false) {
    let btn;
    if (isXPath) {
        // 使用page.waitForXPath等待XPath元素
        btn = await page.waitForXPath(selector, { visible: true, timeout: 5000 });
        // Puppeteer的XPath结果是ElementHandle数组,需要取第一个
        await btn.click();
    } else {
        // 使用page.waitForSelector等待CSS选择器元素
        btn = await page.waitForSelector(selector, { visible: true, timeout: 5000 });
        await btn.click();
    }
}

注意: 当使用XPath时,page.waitForXPath返回的是ElementHandle的数组,但click()方法通常直接作用于单个ElementHandle。如果XPath预期只匹配一个元素,可以直接在返回的ElementHandle上调用click()。如果waitClick函数需要通用,可以考虑page.waitForXPath返回的ElementHandle数组的第一个元素,或者直接使用page.click(selector)如果selector是XPath字符串且page.click支持。在上述修改后的waitClick中,为了简化,假设page.waitForXPath返回的btn是可直接点击的ElementHandle(这通常是正确的行为)。

2. 自动化登录流程

以下是完整的自动化登录函数,它将处理用户名输入、密码键盘交互和最终的登录提交。

const puppeteer = require('puppeteer');

// 辅助函数:等待元素出现并点击
async function waitClick(page, selector, isXPath = false) {
    let elementHandle;
    try {
        if (isXPath) {
            // 使用page.waitForXPath等待XPath元素,并确保可见
            const elements = await page.waitForXPath(selector, { visible: true, timeout: 10000 });
            // 如果XPath返回多个元素,通常我们希望点击第一个匹配项
            if (Array.isArray(elements) && elements.length > 0) {
                elementHandle = elements[0];
            } else {
                throw new Error(`XPath selector "${selector}" did not find any visible elements.`);
            }
        } else {
            // 使用page.waitForSelector等待CSS选择器元素,并确保可见
            elementHandle = await page.waitForSelector(selector, { visible: true, timeout: 10000 });
        }
        await elementHandle.click();
    } catch (error) {
        console.error(`Error clicking element with selector "${selector}" (isXPath: ${isXPath}):`, error);
        throw error; // 重新抛出错误以便上层捕获
    }
}

async function login(user, password) {
    let browser;
    try {
        browser = await puppeteer.launch({ headless: false, defaultViewport: null }); // 设置headless: false便于观察,defaultViewport: null防止默认视口裁剪
        const page = await browser.newPage();

        const url = 'https://ebanking.cpa-bank.dz/customer/';
        await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 }); // 等待网络空闲,确保页面完全加载

        // 1. 输入用户名
        await page.waitForSelector('#form\:username', { visible: true }); // 等待用户名输入框出现
        await page.type('#form\:username', user, { delay: 50 }); // 模拟人类输入,增加延迟

        // 2. 点击“下一步”或提交按钮
        await waitClick(page, '#form\:submit');

        // 3. 等待密码键盘区域加载
        await page.waitForSelector('body', { visible: true }); // 等待页面体加载完毕
        await waitClick(page, '#inputPassId'); // 点击密码输入区域,激活键盘

        // 4. 处理密码输入
        const passArr = [...password]; // 将密码字符串拆分为字符数组
        for (const char of passArr) {
            if (/[A-Z]/.test(char)) { // 如果字符是大写字母
                // 模拟按下Shift键
                await waitClick(page, "xpath/" + `//button[contains(@class,"keypad-key") and text()="Shift"]`, true);
                // 点击大写字母
                await waitClick(page, "xpath/" + `//button[contains(@class,"keypad-key") and text()="${char}"]`, true);
                // 模拟释放Shift键
                await waitClick(page, "xpath/" + `//button[contains(@class,"keypad-key") and text()="Shift"]`, true);
            } else {
                // 点击普通字符
                await waitClick(page, "xpath/" + `//button[contains(@class,"keypad-key") and text()="${char}"]`, true);
            }
        }

        // 5. 点击登录按钮 (根据实际页面调整选择器)
        // 示例中有一个显示密码的按钮,可能不需要点击,直接点击登录
        // await waitClick(page, '#form\:showPasswordId a'); // 如果有显示密码按钮,可能需要点击或跳过

        await waitClick(page, '#form\:loginButton'); // 点击最终的登录按钮

        console.log('登录流程执行完毕。');

        // 可以在这里添加一些断言来验证登录是否成功
        // 例如:await page.waitForSelector('.dashboard-element');

    } catch (error) {
        console.error('登录过程中发生错误:', error);
        throw error; // 重新抛出错误,方便外部调用者处理
    } finally {
        if (browser) {
            // await browser.close(); // 登录成功或失败后关闭浏览器
        }
    }
}

// 执行登录函数
(async () => {
    try {
        await login("96391281", "AadBaiudhw");
    } catch (error) {
        console.error('主程序捕获到错误:', error);
    }
})();

3. 代码解析与注意事项

  • puppeteer.launch({ headless: false, defaultViewport: null }): headless: false 允许您在浏览器中直观地看到自动化过程,这对于调试非常有用。defaultViewport: null 可以防止Puppeteer设置默认的视口大小,而是使用浏览器本身的默认大小,这在某些响应式网站上可能更自然。
  • page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 }): waitUntil: 'networkidle2' 会等待页面加载完成且网络连接在500ms内没有超过2个请求,这通常比domcontentloaded或load更可靠,能确保所有资源(包括JS动态加载的内容)都已加载。timeout设置了页面导航的最大等待时间。
  • page.type(selector, text, { delay: 50 }): delay 参数模拟了人类打字的速度,这有助于规避一些反爬机制,并使自动化行为更自然。
  • waitClick(page, selector, isXPath) 辅助函数: 这个函数封装了等待元素可见并点击的逻辑,提高了代码的健壮性。
    • page.waitForSelector(selector, { visible: true, timeout: 10000 }):等待CSS选择器匹配的元素出现在DOM中并变得可见。
    • page.waitForXPath(selector, { visible: true, timeout: 10000 }):等待XPath选择器匹配的元素出现在DOM中并变得可见。
    • visible: true 是关键,它确保元素不仅存在于DOM中,而且用户可以看到并与之交互。
  • XPath 表达式 //button[contains(@class,"keypad-key") and text()="a"]:
    • //button:选择页面上所有的<button>元素。
    • contains(@class,"keypad-key"):筛选出class属性包含keypad-key的按钮。
    • text()="a":进一步筛选出其文本内容为"a"的按钮。
    • 这种组合方式可以非常精确地定位到动态键盘上具有特定文本内容的按键,即使它们的其他属性(如ID)是动态变化的。
  • [...password]: 这是一个ES6语法,用于将字符串转换为字符数组,方便遍历。
  • 处理大小写 (/[A-Z]/.test(char)): 通过正则表达式判断字符是否为大写字母。如果是,则在点击该字母前和后分别点击“Shift”键,模拟Shift键的按下和释放,以确保输入的是大写字符。
  • 错误处理与资源清理: 使用try...catch...finally结构来捕获可能发生的错误,并在finally块中确保浏览器实例被关闭,即使在发生错误的情况下也能释放资源。

总结

通过本教程,我们学习了如何使用Puppeteer有效处理动态密码键盘的自动化点击问题。关键在于:将复杂的输入分解为字符级操作,利用XPath进行精确且稳定的元素定位,并妥善处理特殊按键(如Shift)。这种方法不仅解决了Node is either not clickable or not an HTMLElement错误,也为自动化其他复杂的、非标准的用户界面元素提供了通用的策略。在实际应用中,务必根据目标网站的具体DOM结构调整CSS选择器和XPath表达式,并利用headless: false进行充分的调试。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

531

2023.06.20

正则表达式不包含
正则表达式不包含

正则表达式,又称规则表达式,,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式的文本。php中文网给大家带来了有关正则表达式的相关教程以及文章,希望对大家能有所帮助。

258

2023.07.05

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

767

2023.07.05

java正则表达式匹配字符串
java正则表达式匹配字符串

在Java中,我们可以使用正则表达式来匹配字符串。本专题为大家带来java正则表达式匹配字符串的相关内容,帮助大家解决问题。

219

2023.08.11

正则表达式空格
正则表达式空格

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。本专题为大家提供正则表达式相关的文章、下载、课程内容,供大家免费下载体验。

357

2023.08.31

Python爬虫获取数据的方法
Python爬虫获取数据的方法

Python爬虫可以通过请求库发送HTTP请求、解析库解析HTML、正则表达式提取数据,或使用数据抓取框架来获取数据。更多关于Python爬虫相关知识。详情阅读本专题下面的文章。php中文网欢迎大家前来学习。

293

2023.11.13

正则表达式空格如何表示
正则表达式空格如何表示

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。想了解更多正则表达式空格怎么表示的内容,可以访问下面的文章。

245

2023.11.17

正则表达式中如何匹配数字
正则表达式中如何匹配数字

正则表达式中可以通过匹配单个数字、匹配多个数字、匹配固定长度的数字、匹配整数和小数、匹配负数和匹配科学计数法表示的数字的方法匹配数字。更多关于正则表达式的相关知识详情请看本专题下面的文章。php中文网欢迎大家前来学习。

548

2023.12.06

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.9万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

CSS教程
CSS教程

共754课时 | 43.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号