如何解决Puppeteer 没有为带有阴影根的页面提供准确的 HTML 代码
我正在尝试下载网站 intersight.com/help/ 的 HTML 代码。但是 puppeteer 并没有像我们在页面中看到的那样返回带有 href 的 HTML 代码(示例 https://intersight.com/help/getting_started 不存在于下载的 HTML 中)。在浏览器中检查 HTML 时,我发现所有丢失的 HTML 都存在于 <an-hulk></an-hulk>
标签中。我不知道这些标签是什么意思。
const puppeteer = require('puppeteer');
const fs = require('fs');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const data = await page.goto('https://intersight.com/help/',{ waitUntil: 'domcontentloaded' });
// Tried all the below lines,neither worked
// await page.waitForSelector('.helplet-links')
// document.querySelector("#app > an-hulk").shadowRoot.querySelector("#content").shadowRoot.querySelector("#main > div > div > div > an-hulk-home").shadowRoot.querySelector("div > div > div:nth-child(1) > div:nth-child(1) > div.helplet-links > ul > li:nth-child(1) > a > span")
// await page.evaluateHandle(`document.querySelector("#app > an-hulk").shadowRoot.querySelector("#content").shadowRoot.querySelector("#main > div > div > div > an-hulk-home")`);
await page.evaluateHandle(`document.querySelector("an-hulk").shadowRoot.querySelector("#aside").shadowRoot.querySelectorAll(".item")`)
const result = await page.content()
fs.writeFile('./intersight.html',result,(err) => {
if (err) console.log(err)
else console.log('done!!')
})
// console.log(result)
await browser.close();
})();
解决方法
正如评论中提到的,您正在处理一个使用 shadow roots 的页面。试图刺穿阴影根部的传统选择器在没有帮助的情况下无法通过控制台或 Puppeteer 工作。除了使用库之外,我们的想法是通过 .shadowRoot
属性识别任何影子根元素,然后递归地深入研究它们并重复该过程,直到获得所需的数据。
此代码应按照此策略获取页面上的所有 href(我没有进行手动计数):
const puppeteer = require("puppeteer");
let browser;
(async () => {
browser = await puppeteer.launch({headless: true});
const [page] = await browser.pages();
const url = "https://intersight.com/help/";
const data = await page.goto(url,{
waitUntil: "networkidle0"
});
await page.waitForSelector("an-hulk",{visible: true});
const hrefs = await page.evaluate(() => {
const walk = root => [
...[...root.querySelectorAll("a[href]")]
.map(e => e.getAttribute("href")),...[...root.querySelectorAll("*")]
.filter(e => e.shadowRoot)
.flatMap(e => walk(e.shadowRoot))
];
return walk(document);
});
console.log(hrefs);
console.log(hrefs.length); // => 44 at the time I ran this
// Bonus example of diving manually into shadow roots...
//const html = await page.evaluate(() =>
// document
// .querySelector("#app > an-hulk")
// .shadowRoot
// .querySelector("#content")
// .shadowRoot
// .querySelector("#main an-hulk-home")
// .shadowRoot
// .querySelector(".content")
// .innerHTML
//);
//console.log(html);
})()
.catch(err => console.error(err))
.finally(async () => await browser.close());
;
请注意,侧边栏和页面的其他部分使用跨度和 div 上的事件侦听器来实现链接,因此就上述代码而言,这些都不算作 href。如果您想访问这些 URL,您可以尝试多种策略,包括点击它们和 extracting the URL after navigation。这是推测性的,因为不清楚您是否想要这样做。
关于您的代码的一些说明:
-
Puppeteer wait until page is completely loaded 是一种重要的资源。
{ waitUntil: 'domcontentloaded' }
是比{ waitUntil: 'networkidle0' }
弱的条件。使用page.waitForSelector(selector,{visible: true})
和page.waitForFunction(predicate)
很重要,可确保在开始操作元素之前已呈现元素。即使没有影子根,我也不清楚顶级"an-hulk"
在您运行evaluate
时是否可用。 - 将 console listeners 添加到您的页面以帮助调试。一次一个地尝试您的查询,并将它们分成多个阶段,看看它们哪里出错了。
-
fs.writeFile
应该是await fs.promises.writeFile
,因为您使用的是异步函数。
其他资源和类似主题:
- What is shadow root
- Puppeteer: Query nodes within shadow roots #858
- How to get text from shadow root element?
- Select element within shadow root
- puppeteer: clicking button in shadowroot
- Manipulate / set style shadowRoot using Puppeteer
- Puppeteer: get full HTML content of a webpage,like innerHTML,but including any shadow roots?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。