Puppeteer如何在SPA表单API填充完输入框后点击按钮?
问题描述
当前为SPA页面,表单输入框的值由JS生成;在某特殊搜索字段输入代码后,其余输入框会通过API获取数据自动填充;需在所有输入框数据填充完成后点击创建按钮。尝试使用page2.waitForNavigation({waitUntil: 'networkidle0'})但无效,目前通过timeout(延迟2000ms)实现,想寻求无需timeout的可靠方案。此前尝试检查输入框值时,因API数据未返回过早获取到空值,无法判断填充完成。
原尝试代码
await page2.focus("body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.content > div:nth-child(1) > div.content > div:nth-child(2) > div:nth-child(1) > div > div.content > input"); await page2.keyboard.type('14129513'); //after type - make req/res for load other fields page2.waitForNavigation({waitUntil: 'networkidle0'}) //dont work const daliButton = await page2.waitForSelector('body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.buttons > div.button.waves-effect.action') await daliButton.evaluate(b => b.click());
当前timeout方案代码
await page2.focus("body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.content > div:nth-child(1) > div.content > div:nth-child(2) > div:nth-child(1) > div > div.content > input"); await page2.keyboard.type('14129513'); function delay(time) { return new Promise(function(resolve) { setTimeout(resolve, time) }); } await delay(2000); const daliButton = await page2.waitForSelector('body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.buttons > div.button.waves-effect.action') await daliButton.evaluate(b => b.click());
解决方案
方案1:等待所有目标输入框填充完成(精准判断DOM值)
利用Puppeteer的waitForFunction监听所有需要填充的输入框,直到它们的value都不为空。这种方法直接验证最终的填充结果,避免依赖网络状态或API细节。
await page2.focus("body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.content > div:nth-child(1) > div.content > div:nth-child(2) > div:nth-child(1) > div > div.content > input"); await page2.keyboard.type('14129513'); // 替换为实际需要检查的所有输入框选择器 const requiredInputs = [ "input[data-name='field1']", "input[data-name='field2']", ".form-group:nth-child(3) input", // 新增更多需要验证的输入框 ]; // 等待所有输入框都有非空值 await page2.waitForFunction((selectors) => { return selectors.every(selector => { const input = document.querySelector(selector); return input && input.value.trim() !== ''; }); }, {}, requiredInputs); // 点击创建按钮 const daliButton = await page2.waitForSelector('body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.buttons > div.button.waves-effect.action'); await daliButton.click();
注意:必须将requiredInputs替换为页面中实际需要填充的所有输入框的选择器,确保覆盖所有依赖API填充的字段。
方案2:监听目标API请求完成
如果知道触发输入框填充的具体API接口,可以直接监听该请求的响应完成,确保数据已返回后再执行后续操作。
await page2.focus("body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.content > div:nth-child(1) > div.content > div:nth-child(2) > div:nth-child(1) > div > div.content > input"); // 监听目标API请求完成(替换为实际的API路径) const apiResponse = page2.waitForResponse(res => res.url().includes('/api/fetch-form-data') && res.ok() ); await page2.keyboard.type('14129513'); // 等待API响应返回 await apiResponse; // 可选:短延迟确保DOM渲染完成(远小于2000ms,比如300ms) await page2.waitForTimeout(300); // 点击创建按钮 const daliButton = await page2.waitForSelector('body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.buttons > div.button.waves-effect.action'); await daliButton.click();
注意:如果有多个API请求触发填充,可以使用Promise.all等待所有请求完成:
const responsesPromise = Promise.all([ page2.waitForResponse(res => res.url().includes('/api/field1-data')), page2.waitForResponse(res => res.url().includes('/api/field2-data')) ]); await responsesPromise;
方案3:等待创建按钮状态变化
如果创建按钮在表单填充完成后会从禁用状态变为可用状态(比如移除disabled属性或禁用类),可以直接等待按钮变为可用,这是最直观的判断方式。
await page2.focus("body > div.viewport > div.overlay.dialog-overlay > div > div.tabs-container > div.tabs-wrapper > ul > li.tab-content.active > div > div.content > div:nth-child(1) > div.content > div:nth-child(2) > div:nth-child(1) > div > div.content > input"); await page2.keyboard.type('14129513'); // 等待按钮变为可用(根据实际禁用标识调整选择器) await page2.waitForSelector('div.button.waves-effect.action:not([disabled])', { visible: true }); // 点击创建按钮 const daliButton = await page2.$('div.button.waves-effect.action'); await daliButton.click();
注意:如果按钮禁用时是通过类名(比如disabled)控制,选择器改为div.button.waves-effect.action:not(.disabled)。
内容的提问来源于stack exchange,提问作者Aleksandr Goroshko
相关产品推荐
相关产品推荐

