在 puppeteer 中,如何获取 innerText 找到的元素的 nextSibling 的内容?
In puppeteer, how I get content of nextSibling of element found by innerText?
我关注html:
<article data-tid="product-detail"> <!-- I page.$eval on this element. -->
<h1 itemprop="name">Product name</h1> <!-- This I can query by itemprop -->
<h2>Some other topic</h2>
<p>I don't want this text.</p>
<h2>Unique topic</h2> <!-- I can find this innerText === "Unique topic". -->
<p>Text I want real' bad.</p> <!-- I want this innerText. -->
<h2>Some other topic</h2>
<p>I don't want this text.</p>
</article>
如何获得“我想要真实的糟糕文本”。从页面,知道“独特的话题”?
我 运行 从节点脚本操纵木偶。
这是我目前所拥有的:
async function puppeteerProductDataExtractor($product) {
// This works like charm.
const productName = $product.querySelector('[itemprop=name]')?.innerText;
// Now I have to find the right h2 and get it's next element sibling.
const h2 = $product.querySelectorAll('h2');
// 1. If I try to get innerText of all h2, it fails with getProperty function not defined.
console.log(
await Promise.all([...h2].map(async $el => await (await element.getProperty('innerText')).jsonValue()))
);
// 2. This returns empty array
console.log([...h2].filter($el => $el.innerText.startsWith('Unique topic')));
// This prints JSHandle@array - innerText is not a string.
console.log([...h2].map($el => $el.innerText));
// This also prints JSHandle@array which is just insane.
console.log([...h2].map($el => Object.keys($el)));
// This fails with "property is not a function" error.
console.log([...h2].map(el => el.property('innerText')));
// So does this.
console.log([...h2].map(el => el.getProperty('innerText')));
}
page.on('console', consoleObj => console.log('xxxx', consoleObj.text()));
const product = await page.$eval('article[data-tid=product-detail]', puppeteerProductDataExtractor);
第一次尝试来自这里:
其他一切都只是沮丧的盲目射击。必须承认我很困惑。有些东西应该根据文档工作,但它只是失败了。像 JSHandle should have the property
function,但是当我调用它(不是函数)时它失败了。
我什至没有到达 nextSibling
部分。
我尝试了很多代码,但大部分都失败了,不想用它来污染问题。感觉这应该非常简单,我只是遗漏了一些东西。希望初衷清楚。
我确信有一个简单的解决方案,但反复试验似乎不是解决问题的方法。
经过深挖,原来我的初衷是对的。 filter()
不起作用不是因为 innerText 将是 JSHandle 的实例(正如它所显示的那样),而是因为大写首字母是由 CSS 完成的(在比较之前必须小写以统一字符串)。有点惭愧...抱歉并感谢@ggorlen 的帮助。
/* WE'RE INSIDE $eval FUNCTION */
// This returns JSHandle@array which is just weird...
console.log([...h2].map(el => el.innerText));
// But this returns the joined string correctly. Huh...
console.log([...h2].map(el => el.innerText).join(';'));
// So this eventually works
console.log([...h2]
.filter(el => el.textContent.toLowerCase().startsWith('unique topic'))[0]?
.nextElementSibling.textContent);
您可以尝试 nextElementSibling
rather than nextSibling
,它可以 return 空白文本节点:
const text = document
.querySelector("h2")
.nextElementSibling
.textContent
;
console.log(text);
<h2>Unique topic</h2>
<p>Text I want real' bad.</p>
虽然您没有共享您的标记,但看起来您可能有多个 <h2>
/<p>
组合,并且您想从每个组合中提取文本。这可能有助于您入门:
const text = [...document.querySelectorAll("h2")]
.map(e => e.nextElementSibling.textContent)
;
console.log(text);
<h2>Unique topic 1</h2>
<p>Text I want real' bad. 1</p>
<h2>Unique topic 2</h2>
<p>Text I want real' bad. 2</p>
<h2>Unique topic 3</h2>
<p>Text I want real' bad. 3</p>
如果不明显,上面的代码必须运行在$eval
、$$eval
或evaluate
中。例如,您可以使用:
const puppeteer = require("puppeteer");
let browser;
(async () => {
const html = `
<h2>Unique topic 1</h2>
<p>Text I want real' bad. 1</p>
<h2>Unique topic 2</h2>
<p>Text I want real' bad. 2</p>
<h2>Unique topic 3</h2>
<p>Text I want real' bad. 3</p>
`;
browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.setContent(html);
const contents = await page.$$eval(
"h2",
els => els.map(e => e.nextElementSibling.textContent)
);
console.log(contents);
})()
.catch(err => console.error(err))
.finally(async () => await browser.close())
;
你的线路
await (await element.getProperty('innerText')).jsonValue()
是纯粹的 Node Puppeteer,但您正试图 运行 在浏览器控制台中执行此操作。这是一个常见的错误——elementHandles 只在 Puppeteer 中工作。 提供了一个显示 evaluate
方法的底部示例,该方法使用仅浏览器代码。
为了调试浏览器代码(在 evaluate
、$eval
、$$eval
等的回调中执行的内容),我建议将侦听器附加到控制台,如 或 运行 以便您可以看到错误消息。
另一个技巧是在浏览器中手动计算出您的选择器,然后只有在它们工作后才将它们添加到 Puppeteer 的 evaluate
。 evaluate
是通用的,因此所有 DOM 操作都可以使用 shorthand Puppeteer page
和 elementHandle 便捷方法,如 .click()
、.getProperty()
、.$eval
、.$x
等可以直接在evaluate
.
中完成
我关注html:
<article data-tid="product-detail"> <!-- I page.$eval on this element. -->
<h1 itemprop="name">Product name</h1> <!-- This I can query by itemprop -->
<h2>Some other topic</h2>
<p>I don't want this text.</p>
<h2>Unique topic</h2> <!-- I can find this innerText === "Unique topic". -->
<p>Text I want real' bad.</p> <!-- I want this innerText. -->
<h2>Some other topic</h2>
<p>I don't want this text.</p>
</article>
如何获得“我想要真实的糟糕文本”。从页面,知道“独特的话题”?
我 运行 从节点脚本操纵木偶。
这是我目前所拥有的:
async function puppeteerProductDataExtractor($product) {
// This works like charm.
const productName = $product.querySelector('[itemprop=name]')?.innerText;
// Now I have to find the right h2 and get it's next element sibling.
const h2 = $product.querySelectorAll('h2');
// 1. If I try to get innerText of all h2, it fails with getProperty function not defined.
console.log(
await Promise.all([...h2].map(async $el => await (await element.getProperty('innerText')).jsonValue()))
);
// 2. This returns empty array
console.log([...h2].filter($el => $el.innerText.startsWith('Unique topic')));
// This prints JSHandle@array - innerText is not a string.
console.log([...h2].map($el => $el.innerText));
// This also prints JSHandle@array which is just insane.
console.log([...h2].map($el => Object.keys($el)));
// This fails with "property is not a function" error.
console.log([...h2].map(el => el.property('innerText')));
// So does this.
console.log([...h2].map(el => el.getProperty('innerText')));
}
page.on('console', consoleObj => console.log('xxxx', consoleObj.text()));
const product = await page.$eval('article[data-tid=product-detail]', puppeteerProductDataExtractor);
第一次尝试来自这里:
其他一切都只是沮丧的盲目射击。必须承认我很困惑。有些东西应该根据文档工作,但它只是失败了。像 JSHandle should have the property
function,但是当我调用它(不是函数)时它失败了。
我什至没有到达 nextSibling
部分。
我尝试了很多代码,但大部分都失败了,不想用它来污染问题。感觉这应该非常简单,我只是遗漏了一些东西。希望初衷清楚。
我确信有一个简单的解决方案,但反复试验似乎不是解决问题的方法。
经过深挖,原来我的初衷是对的。 filter()
不起作用不是因为 innerText 将是 JSHandle 的实例(正如它所显示的那样),而是因为大写首字母是由 CSS 完成的(在比较之前必须小写以统一字符串)。有点惭愧...抱歉并感谢@ggorlen 的帮助。
/* WE'RE INSIDE $eval FUNCTION */
// This returns JSHandle@array which is just weird...
console.log([...h2].map(el => el.innerText));
// But this returns the joined string correctly. Huh...
console.log([...h2].map(el => el.innerText).join(';'));
// So this eventually works
console.log([...h2]
.filter(el => el.textContent.toLowerCase().startsWith('unique topic'))[0]?
.nextElementSibling.textContent);
您可以尝试 nextElementSibling
rather than nextSibling
,它可以 return 空白文本节点:
const text = document
.querySelector("h2")
.nextElementSibling
.textContent
;
console.log(text);
<h2>Unique topic</h2>
<p>Text I want real' bad.</p>
虽然您没有共享您的标记,但看起来您可能有多个 <h2>
/<p>
组合,并且您想从每个组合中提取文本。这可能有助于您入门:
const text = [...document.querySelectorAll("h2")]
.map(e => e.nextElementSibling.textContent)
;
console.log(text);
<h2>Unique topic 1</h2>
<p>Text I want real' bad. 1</p>
<h2>Unique topic 2</h2>
<p>Text I want real' bad. 2</p>
<h2>Unique topic 3</h2>
<p>Text I want real' bad. 3</p>
如果不明显,上面的代码必须运行在$eval
、$$eval
或evaluate
中。例如,您可以使用:
const puppeteer = require("puppeteer");
let browser;
(async () => {
const html = `
<h2>Unique topic 1</h2>
<p>Text I want real' bad. 1</p>
<h2>Unique topic 2</h2>
<p>Text I want real' bad. 2</p>
<h2>Unique topic 3</h2>
<p>Text I want real' bad. 3</p>
`;
browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.setContent(html);
const contents = await page.$$eval(
"h2",
els => els.map(e => e.nextElementSibling.textContent)
);
console.log(contents);
})()
.catch(err => console.error(err))
.finally(async () => await browser.close())
;
你的线路
await (await element.getProperty('innerText')).jsonValue()
是纯粹的 Node Puppeteer,但您正试图 运行 在浏览器控制台中执行此操作。这是一个常见的错误——elementHandles 只在 Puppeteer 中工作。 evaluate
方法的底部示例,该方法使用仅浏览器代码。
为了调试浏览器代码(在 evaluate
、$eval
、$$eval
等的回调中执行的内容),我建议将侦听器附加到控制台,如
另一个技巧是在浏览器中手动计算出您的选择器,然后只有在它们工作后才将它们添加到 Puppeteer 的 evaluate
。 evaluate
是通用的,因此所有 DOM 操作都可以使用 shorthand Puppeteer page
和 elementHandle 便捷方法,如 .click()
、.getProperty()
、.$eval
、.$x
等可以直接在evaluate
.