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

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

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

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

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

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

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

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

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

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

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:选择页面上所有的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进行充分的调试。

以上就是Puppeteer自动化:处理动态密码键盘点击与XPath策略的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1536910.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
C# 反射详解
上一篇 2026年5月10日 10:34:56
使用数据库数据计算每日增量:SQL窗口函数与PHP实现
下一篇 2026年5月10日 10:34:58

相关推荐

  • Python自定义类实现集合行为:__getitem__与继承策略

    本文深入探讨了在python中如何让自定义类表现得像内置的列表、元组或字典。通过实现特定的特殊方法(如`__getitem__`和`__setitem__`)或利用继承机制,开发者可以赋予自定义对象索引、切片和迭代等集合特性,从而提升代码的灵活性和可读性。文章将通过具体示例,详细阐述两种实现策略及其…

    2026年5月10日
    000
  • C++中的异常处理性能影响如何?

    c++++异常处理对程序性能有显著影响,主要体现在异常抛出、堆栈展开和异常捕获的开销。1. 异常抛出需要创建对象和填充堆栈信息。2. 堆栈展开涉及调用析构函数,增加性能开销。3. 异常捕获需要时间,尤其在多catch块匹配时。 引言 当我们谈到C++中的异常处理时,很多人都会好奇这对程序性能到底有多…

    2026年5月10日
    100
  • 如何实现C++中的着色器程序?

    如何实现C++中的着色器程序?如何实现C++中的着色器程序?如何实现C++中的着色器程序?如何实现C++中的着色器程序?

    如何实现c++++中的着色器程序?在c++中实现着色器程序需要使用图形api如opengl或directx,具体步骤包括:1. 编写着色器代码:使用glsl或hlsl编写顶点和片段着色器;2. 编译和链接着色器:使用api函数加载、编译着色器并创建程序;3. 将数据传递给着色器:通过统一变量和属性传…

    2026年5月10日 用户投稿
    000
  • 使用数据库数据计算每日增量:SQL窗口函数与PHP实现

    本文详细介绍了如何利用mysql 8.0及更高版本提供的窗口函数,结合php编程语言,从包含时间戳和计数数据的数据库表中高效计算每日的增量。教程涵盖了sql查询的构建、php中pdo和mysqli的集成示例,并指导读者如何从数据库中提取每日的初始值和最终值,进而计算出每日变化量。 在许多数据监控和分…

    2026年5月10日
    000
  • Go项目交叉编译失败有哪些常见原因

    Go项目交叉编译失败有哪些常见原因Go项目交叉编译失败有哪些常见原因Go项目交叉编译失败有哪些常见原因Go项目交叉编译失败有哪些常见原因

    go项目交叉编译失败通常因缺少目标平台依赖库或编译参数错误。1. 检查goos和goarch环境变量设置,确保指定正确的操作系统和架构;2. 若项目不含c代码,设置cgo_enabled=0以避免cgo引发问题;3. 若依赖c库,需安装交叉编译工具链或改用纯go实现的库;4. 确保使用支持目标平台的…

    2026年5月10日 用户投稿
    000
  • c++怎么在类外部定义成员函数_c++类成员函数外部定义语法

    C++中类成员函数可在类外定义,通过作用域解析运算符::关联到类;2. 声明放头文件,实现放源文件,提升代码组织与编译效率;3. 定义时需保持返回类型、函数名、参数列表与声明一致,const或静态成员函数也需对应修饰。 在C++中,类的成员函数可以在类外部定义。这种做法常用于将类声明放在头文件(.h…

    2026年5月10日
    100
  • 如何使用CSS更好地格式化HTML元素_CSS格式化HTML元素最佳实践

    使用语义化HTML和有意义的类名,2. 采用BEM命名法模块化CSS,3. 重置默认样式并统一基础设置,4. 利用Flexbox和Grid实现现代布局,5. 避免深层选择器以提升性能,6. 使用CSS自定义属性管理主题变量,7. 优先移动端进行响应式设计。 要让网页看起来整洁、专业,关键在于如何用C…

    2026年5月10日
    000
  • Go 语言中从 io.Reader 读取 UTF-8 编码数据并转换为字符串

    在 Go 语言中,从 io.Reader 接口读取数据时,通常会得到字节切片([]byte),但很多场景下我们需要将其转换为 UTF-8 编码的字符串。本文将详细介绍如何利用标准库中的 bytes.Buffer,结合 io.Copy 或 ReadFrom 方法,高效、便捷地实现这一转换过程,并探讨其…

    2026年5月10日
    000
  • unity如何显示html_Unity引擎中HTML内容集成与显示方法

    使用Webview插件可在Unity中嵌入HTML内容,支持移动端和桌面端;本地HTML可通过StreamingAssets目录加载,简单富文本可用TextMeshPro的富文本功能实现,复杂内容建议结合服务器解析后动态展示。 在Unity中直接显示HTML内容存在限制,因为Unity原生不支持HT…

    2026年5月10日
    000
  • Golang goroutine如何使用 轻量级线程创建与管理

    Goroutine是Go的轻量级并发单元,通过go关键字启动,由Go运行时调度,相比操作系统线程更高效,具备小栈、低开销、高并发优势,配合WaitGroup、channel、context等机制可实现安全的并发控制与资源管理。 Golang中的goroutine,说白了,就是Go语言提供的一种轻量级…

    2026年5月10日
    000
  • JavaScript实现表单提交前的确认与取消机制

    本文详细介绍了如何使用JavaScript为网页表单添加提交前的确认对话框。通过监听表单的submit事件,并在用户点击确认框中的“取消”按钮时,利用event.preventDefault()方法有效阻止表单的默认提交行为,从而提升用户操作的安全性与体验。 理解表单提交事件与确认机制 在网页开发中…

    2026年5月10日
    000
  • Golang如何在测试中使用临时文件

    使用os.CreateTemp和t.Cleanup可安全创建并自动清理临时文件,避免路径冲突与系统污染。1. 创建临时文件或目录时使用唯一命名;2. 通过t.Cleanup注册删除函数确保资源释放;3. 多文件场景建议统一置于临时目录下,测试后递归删除;4. 注意关闭文件、设置合理权限及使用默认临时…

    2026年5月10日
    000
  • 用html创建canvas画布生成图片

    本篇文章主要介绍如何用html创建canvas画布生成图片,感兴趣的朋友参考下,希望对大家有所帮助。 1,在html里新建canvas画布 /**要生成图片的html*/ 思路惊奇 思路惊奇 @@##@@ @@##@@/*生成的canvas和最终生成的图片*/ @@##@@ //设置canva画布大…

    用户投稿 2026年5月10日
    000
  • 什么是NFT碎片化?它如何让普通投资者也能拥有高价NFT的一部分?

    NFT碎片化通过智能合约将高价值NFT拆分为多个ERC-20代币,降低投资门槛。用户可使用Fractional等去中心化协议,登录Web3钱苞后选择“Fractionalize”功能,授权并锁定NFT,设置代币参数后完成铸造,生成可交易的碎片代币。普通投资者亦可在去中心化交易所(如Uniswap)通…

    2026年5月10日
    000
  • 在 Next.js 中循环渲染 Props 的正确方法

    在 Next.js 中循环渲染 Props 的正确方法在 Next.js 中循环渲染 Props 的正确方法在 Next.js 中循环渲染 Props 的正确方法在 Next.js 中循环渲染 Props 的正确方法

    本文旨在解决在 Next.js 应用中使用 forEach 循环渲染 props 时遇到的问题。核心在于理解 forEach 和 map 方法的区别,并掌握如何正确使用 map 方法生成 React 组件,从而实现循环渲染。通过修改原代码,将 forEach 替换为 map,可以有效地解决渲染问题,…

    2026年5月10日 用户投稿
    000
  • Golang使用gRPC拦截器处理请求示例

    使用gRPC拦截器可统一处理日志、认证等逻辑,无需修改业务代码。2. 一元拦截器通过grpc.UnaryServerInterceptor实现,用于记录请求耗时与日志。3. 流式拦截器通过grpc.StreamServerInfo处理流式RPC调用。4. 在grpc.NewServer时注册拦截器选…

    2026年5月10日
    000
  • 如何使用CSS在移动端实现小标签效果并确保安卓和苹果设备上显示一致?

    移动端CSS小标签效果实现及跨平台一致性 在移动端开发中,精确还原设计稿中的小标签效果,特别是文字与边框的完美居中,常常面临挑战,不同设备的显示差异也令人头疼。本文将分享两种CSS方法,确保您的标签在安卓和iOS设备上都能一致显示。 目标效果:边框内文字水平和垂直居中。 问题:移动端垂直居中效果不理…

    2026年5月10日
    000
  • HTML结构优化:高效移除标签内的标签

    本教程详细介绍了如何通过编程方式移除HTML文档中嵌套在“标签内的“标签,从而优化HTML结构。文章提供了纯JavaScript(适用于浏览器环境)和Node.js(结合`jsdom`库)两种实现方案,并附带示例代码和关键注意事项,帮助开发者实现更简洁、语义化的网页内容。 HTML结构…

    2026年5月10日
    000
  • 优化 Nextjs 应用性能的经过验证的技巧 ⚡️

    优化 web 应用程序的性能对于提供快速、流畅的用户体验至关重要。 使用 next.js 这个强大的 react 框架,您可以利用许多内置功能来提高应用程序的速度和效率。 以下十个关键策略可帮助您的 next.js 应用获得最佳性能: 1. 仅加载您需要的 javascript 和 css 为了避免…

    2026年5月10日
    000
  • HTML怎么添加固定背景?

    HTML怎么添加固定背景?HTML怎么添加固定背景?HTML怎么添加固定背景?HTML怎么添加固定背景?

    要实现html固定背景,需使用css的background-attachment: fixed属性。具体步骤为:1. 准备合适的背景图片,注意大小与质量;2. 编写html结构并引入css文件;3. 在css中设置background-image指定图片路径,配合background-attachm…

    2026年5月10日 用户投稿
    000

发表回复

登录后才能评论
关注微信