Node12+Puppeteer2.2.1如何获取Shadow DOM元素的ElementHandles并遍历?
解决Puppeteer 2.2.1中遍历Shadow Root内DOM元素并获取ElementHandles的问题
在Node 12和Puppeteer 2.2.1(均无法升级)的环境下,需要动态查找Shadow Root内的DOM元素,并将对应的ElementHandles传递给类的其他函数用于爬取。遇到的问题包括:
page.$()/page.$$()无法检测带Shadow Root的元素page.$$eval()/page.evaluate()无法将ElementHandles返回到主函数- 使用遍历Shadow Root的代码后,
evaluateHandle()返回的JSHandle不可迭代,导致for循环无法正常运行
核心思路是:evaluateHandle()返回的JSHandle包裹了元素数组,我们可以先获取数组的长度,再通过索引逐个取出每个元素对应的ElementHandle,以此实现遍历操作。
修改后的完整代码如下:
#!/usr/bin/env node 'use strict'; const puppeteer = require('puppeteer'); class Generator { async main() { const url = 'https://www.example.com'; const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url, {waitUntil: 'networkidle2'}); await this.scrape(page); await browser.close(); } async process(page, element) { await page.evaluate(el => { el.click(); }, element); this.doOtherThingsWithElement(element); } doOtherThingsWithElement(element) { // 添加自定义元素处理逻辑 console.log('处理元素:', element); } async scrape(page) { const elementsHandle = await page.evaluateHandle(() => { const walk = root => [ ...[...root.querySelectorAll('[class*=shadowed-child] button')], ...[...root.querySelectorAll('*')] .filter(e => e.shadowRoot) .flatMap(e => walk(e.shadowRoot)) ]; return walk(document); }); // 获取元素数组长度 const length = await page.evaluate(arr => arr.length, elementsHandle); // 通过索引遍历并转换为ElementHandle for (let i = 0; i < length; i++) { const element = await elementsHandle.getProperty(i); const elementHandle = element.asElement(); if (elementHandle) { await this.process(page, elementHandle); await element.dispose(); // 释放单个元素资源 } } await elementsHandle.dispose(); // 释放外层JSHandle资源 } } new Generator().main().catch(err => console.error(err));
关键修改说明:
- 给
main方法添加async关键字,修复原代码的语法错误 - 对
evaluateHandle返回的JSHandle,通过getProperty(i)按索引获取单个元素的句柄,再用asElement()转换为可用的ElementHandle - 遍历过程中调用
dispose()释放不再使用的句柄,避免内存泄漏 - 增加浏览器关闭逻辑,防止资源残留
内容的提问来源于stack exchange,提问作者rabudde
相关产品推荐
相关产品推荐

