
在进行网页自动化或数据抓取时,Puppeteer提供了强大的能力来与页面DOM进行交互。其中,page.$eval()和page.$$eval()(以及它们的元素句柄版本elementHandle.$eval()和elementHandle.$$eval())是执行JavaScript代码并获取DOM元素信息的关键方法。理解它们的区别和正确使用方式对于高效编写Puppeteer脚本至关重要。
.$eval(selector, pageFunction)方法用于在浏览器环境中执行一个pageFunction,并将其结果返回给Node.js环境。它的核心特点是:
示例:提取单个元素的HTML内容
假设我们需要从一个ID为#words的容器中,提取第一个div元素的内部HTML。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://monkeytype.com/', { waitUntil: 'domcontentloaded' });
// 等待 #words 元素出现
const wordsSelector = await page.waitForSelector('#words');
// 使用 .$eval 提取第一个 div 的 innerHTML
const innerHtml = await wordsSelector.$eval('div', wordsDiv => wordsDiv.innerHTML);
console.log("第一个div的innerHTML:", innerHtml); // 预期输出如 <letter>...</letter>
await browser.close();
})();在这个例子中,wordsDiv参数在pageFunction中代表了#words元素内部第一个匹配div选择器的DOM元素,我们可以直接访问它的innerHTML属性。
.$$eval(selector, pageFunction)方法同样用于在浏览器环境中执行pageFunction,但它与.$eval()有显著不同:
常见误区与正确用法
许多初学者在使用.$$eval()时,会错误地认为pageFunction中的参数是单个DOM元素,并尝试直接访问其属性,例如:
// 错误示例:尝试直接访问数组的 innerHTML 属性
// const words = await wordsSelector.$$eval('div', elements => elements.innerHTML);
// 这里的 elements 是一个数组,elements.innerHTML 将是 undefined正确的做法是,在pageFunction内部,你需要遍历或映射这个DOM元素数组,对每个元素执行操作。
示例:提取所有匹配元素的HTML内容
为了提取#words容器内所有.word元素的内部HTML,我们需要在pageFunction中对传入的元素数组进行映射:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
const [page] = await browser.pages();
await page.goto('https://monkeytype.com/', { waitUntil: 'domcontentloaded' });
// 接受 Cookies
const rejectAllButton = await page.waitForSelector('.rejectAll');
if (rejectAllButton) {
await rejectAllButton.click();
}
// 等待第一个活跃的单词出现,确保页面加载完成
await page.waitForSelector('#words .word.active');
const wordsEl = await page.$('#words'); // 获取 #words 元素句柄
// 使用 $$eval 提取所有 .word 元素的 innerHTML
const wordsHtmlArray = await wordsEl.$$eval('.word', els =>
els.map(el => el.innerHTML) // 对每个元素执行 .innerHTML 操作
);
console.log("所有单词的HTML:", wordsHtmlArray); // 预期输出如 ["<letter>...</letter>", ...]
await browser.close();
})();在这个例子中,els是一个包含所有.word元素的数组。我们使用map方法遍历这个数组,对每个元素el获取其innerHTML,最终返回一个包含所有HTML字符串的数组。
最佳实践:使用.textContent
在大多数情况下,如果你只需要元素的纯文本内容,推荐使用.textContent而不是.innerHTML。.textContent会返回元素及其所有子元素的文本内容,不包含任何HTML标签,这通常更简洁且不易出错。
// 提取所有 .word 元素的纯文本内容
const wordsTextArray = await wordsEl.$$eval('.word', els =>
els.map(el => el.textContent.trim()) // 使用 .textContent 并去除首尾空白
);
console.log("所有单词的文本:", wordsTextArray); // 预期输出如 ["interest", "word", ...].$eval()和.$$eval()是构建复杂自动化脚本的基础。结合其他Puppeteer功能,我们可以实现更高级的交互,例如自动化一个打字测试网站。
下面的示例展示了如何:
const puppeteer = require("puppeteer");
let browser;
(async () => {
browser = await puppeteer.launch({headless: true}); // 启动无头浏览器
const [page] = await browser.pages();
const url = "https://monkeytype.com/";
// 启用请求拦截
await page.setRequestInterception(true);
const allowed = [
"https://monkeytype.com",
"https://www.monkeytype.com",
"https://api.monkeytype.com",
"https://fonts.google", // 允许加载字体文件
];
page.on("request", request => {
// 只允许特定域名的请求通过,其他请求一律阻止
if (allowed.some(e => request.url().startsWith(e))) {
request.continue();
}
else {
request.abort();
}
});
await page.goto(url, {waitUntil: "domcontentloaded"}); // 导航到页面
// 辅助函数,等待选择器并返回元素句柄
const $ = (...args) => page.waitForSelector(...args);
// 点击接受Cookies按钮(如果存在)
const rejectAllButton = await $(".rejectAll");
if (rejectAllButton) {
await rejectAllButton.click();
}
// 等待第一个活跃的单词出现
await $("#words .word.active");
const wordsContainer = await page.$("#words"); // 获取单词容器的句柄
try {
// 循环直到无法找到下一个活跃单词(表示测试结束)
for (;;) {
// 使用 .$eval 提取当前活跃单词的文本内容
const word = await wordsContainer.$eval(".word.active", el =>
el.textContent.trim()
);
// 模拟键盘输入单词,并在末尾加上空格
await wordsContainer.type(word + " ");
}
}
catch (err) {
// 捕获循环结束时的错误(例如找不到 .word.active 元素)
console.log("打字测试结束或发生错误:", err.message);
}
// 等待结果显示
const results = await $("#result");
// 滚动到结果区域,确保截图可见
await results.evaluate(el => el.scrollIntoView());
// 截取结果区域的屏幕截图
await results.screenshot({path: "typing-results.png"});
console.log("打字结果截图已保存为 typing-results.png");
})()
.catch(err => console.error("自动化过程中发生错误:", err))
.finally(() => browser?.close()) // 无论成功失败,最后都关闭浏览器
;注意事项:
.$eval()和.$$eval()是Puppeteer中执行页面内JavaScript评估的核心工具。正确理解它们之间的区别——.$eval()处理单个元素,.$$eval()处理元素数组——并掌握在pageFunction中如何操作这些元素,是编写健壮、高效Puppeteer脚本的关键。结合请求拦截、模拟用户输入等高级功能,你可以构建出功能强大的自动化解决方案。在实际应用中,优先使用.textContent进行文本提取,并始终注意资源管理和错误处理。
以上就是深入理解Puppeteer的元素评估方法:.$eval()与.$$eval()的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号