Puppeteer在万豪网站弹窗中选择多元素的点击异常问题
万豪官网客房/成人数量设置问题修复方案
问题原因
- 选择器过度依赖位置:万豪的弹窗DOM会随房间数量变化更新,用
:nth-of-type这类按位置定位的选择器,新增房间后极易选错按钮,导致点成人加号时反而增加了房间数量。 - 未等待DOM更新就操作:点击房间加号后,弹窗HTML会重新渲染,之前获取的元素句柄会失效,直接调用就会触发
TypeError: Cannot read properties of undefined (reading 'click')错误。
修正后的代码
async function fill_room_and_guest(page, constants) { // 打开客房/客人选择弹窗 await page.click('.search__rates.t-font-s[type="button"]'); // 等待弹窗加载完成,用包含"Rooms"文本的元素判断,比动态类名更可靠 await page.waitForXPath('//div[contains(text(), "Rooms")]'); // 将房间从默认1间增加到目标数量 const targetRooms = constants.NUM_ROOMS; for (let i = 1; i < targetRooms; i++) { // 每次点击前重新获取房间加号按钮,避免DOM更新后元素失效 const roomPlusBtn = await page.waitForSelector('[class^="StyledRowDiv"]:has([class*="Room"]) [class^="StyledIncrementDiv"] .icon-plus'); await roomPlusBtn.click(); // 给DOM留更新时间,避免操作过快 await page.waitForTimeout(500); } // 设置成人数量:默认每间房1位,总成人数减房间数即为需额外添加的次数 const extraAdults = constants.NUM_ADULTS - constants.NUM_ROOMS; if (extraAdults > 0) { // 往第一间房添加成人(需调整到其他房间可修改选择器) for (let j = 0; j < extraAdults; j++) { const adultPlusBtn = await page.waitForSelector('[class^="StyledRowDiv"]:nth-child(2) [class^="StyledIncrementDiv"] .icon-plus'); await adultPlusBtn.click(); await page.waitForTimeout(500); } } // 关闭弹窗(可根据后续流程决定是否保留) await page.click('[class^="StyledPopupDiv"] [class^="StyledButtonDiv"] button'); }
核心优化点
- 用内容关联的选择器:通过
:has([class*="Room"])筛选房间相关行,或用XPath定位包含“Adults”文本的行,避免位置依赖导致的误选。 - 每次操作重新获取元素:点击后DOM会更新,之前的元素句柄会失效,用
waitForSelector重新获取可确保元素存在再操作。 - 理清成人数量逻辑:万豪要求每间房至少1位成人,总成人数减去房间数即为需额外点击加号的次数,直接在某一间房完成操作即可。
- 添加短暂等待:
waitForTimeout保证DOM有足够时间更新,避免因操作过快导致元素未加载。
额外提醒
- 优先使用
waitForSelector而非$$获取元素数组:前者会自动等待元素出现,超时会抛出明确错误,更利于排查问题。 - 动态类名场景下XPath更可靠:比如定位成人加号的XPath为
//div[contains(text(), "Adults")]/../following-sibling::div//*[contains(@class, "icon-plus")],靠文本定位不会因类名变化失效。
内容的提问来源于stack exchange,提问作者Nelson Mak
相关产品推荐
相关产品推荐

