Node.js 使用Puppeter和headless Chrome获取DOM节点文本

Node.js 使用Puppeter和headless Chrome获取DOM节点文本,node.js,google-chrome-headless,puppeteer,Node.js,Google Chrome Headless,Puppeteer,我试图使用headless Chrome和Puppeter来运行Javascript测试,但我无法从页面中提取结果。基于,看起来我应该使用。这一部分甚至有一个例子,看起来像我需要的 const bodyHandle = await page.$('body'); const html = await page.evaluate(body => body.innerHTML, bodyHandle); await bodyHandle.dispose(); 作为一个完整的示例,我尝试将其转

我试图使用headless Chrome和Puppeter来运行Javascript测试,但我无法从页面中提取结果。基于,看起来我应该使用。这一部分甚至有一个例子,看起来像我需要的

const bodyHandle = await page.$('body');
const html = await page.evaluate(body => body.innerHTML, bodyHandle);
await bodyHandle.dispose();
作为一个完整的示例,我尝试将其转换为一个脚本,该脚本将在堆栈溢出时从我的用户配置文件中提取我的名字。我们的项目正在使用节点6,因此我将
wait
表达式转换为使用
.then()

当我运行该命令时,会出现以下错误:

$ node get_user.js 
TypeError: Converting circular structure to JSON
    at Object.stringify (native)
    at args.map.x (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/helper.js:30:43)
    at Array.map (native)
    at Function.evaluationString (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/helper.js:30:29)
    at Frame.<anonymous> (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:376:31)
    at next (native)
    at step (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:355:24)
    at Promise (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:373:12)
    at fn (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:351:10)
    at Frame._rawEvaluate (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:375:3)
$node get\u user.js
TypeError:将循环结构转换为JSON
at Object.stringify(本机)
在args.map.x(/mnt/data/don/git/Kive/node_modules/puppeter/node6/helper.js:30:43)
at Array.map(本机)
在Function.evaluationString(/mnt/data/don/git/Kive/node_modules/puppeter/node6/helper.js:30:29)
在画面上。(/mnt/data/don/git/Kive/node_modules/puppeter/node6/FrameManager.js:376:31)
在下一个(本地)
在步骤(/mnt/data/don/git/Kive/node_modules/puppeter/node6/FrameManager.js:355:24)
在承诺中(/mnt/data/don/git/Kive/node_modules/puppeter/node6/FrameManager.js:373:12)
在fn(/mnt/data/don/git/Kive/node_modules/puppeter/node6/FrameManager.js:351:10)
在帧处(/mnt/data/don/git/Kive/node\u modules/puppeter/node6/FrameManager.js:375:3)

问题似乎在于将输入参数序列化为
page.evaluate()
。我可以传入字符串和数字,但不能传入元素句柄。该示例是错误的,还是节点6有问题?如何提取DOM节点的文本?

根据提取的复杂程度,我找到了三种解决方案。最简单的选项是一个我没有注意到的相关函数:。它基本上完成了我想做的:将
page.$()
page.evaluate()结合起来。下面是一个有效的示例:

const puppeteer = require('puppeteer');

puppeteer.launch().then(function(browser) {
    browser.newPage().then(function(page) {
        page.goto('https://stackoverflow.com/users/4794').then(function() {
            page.$eval('h2.user-card-name', function(heading) {
                return heading.innerText;
            }).then(function(result) {
                console.info(result);
                browser.close();
            });
        });
    });
});
这给了我预期的结果:

$ node get_user.js 
Don Kirkby top 2% overall
我想提取一些更复杂的内容,但我最终意识到求值函数是在页面的上下文中运行的。这意味着您可以使用页面中加载的任何工具,然后来回发送字符串和数字。在本例中,我在字符串中使用jQuery来提取我想要的内容:

const puppeteer = require('puppeteer');

puppeteer.launch().then(function(browser) {
    browser.newPage().then(function(page) {
        page.goto('https://stackoverflow.com/users/4794').then(function() {
            page.evaluate("$('h2.user-card-name').text()").then(function(result) {
                console.info(result);
                browser.close();
            });
        });
    });
});
这会给我一个结果,其中空格保持不变:

$ node get_user.js 

                            Don Kirkby

                                top 2% overall
在我的真实脚本中,我想提取几个节点的文本,因此我需要一个函数,而不是一个简单的字符串:

const puppeteer = require('puppeteer');

puppeteer.launch().then(function(browser) {
    browser.newPage().then(function(page) {
        page.goto('https://stackoverflow.com/users/4794').then(function() {
            page.evaluate(function() {
                return $('h2.user-card-name').text();
            }).then(function(result) {
                console.info(result);
                browser.close();
            });
        });
    });
});
这给出了完全相同的结果。现在我需要添加错误处理,并可能减少缩进级别。

使用and,语法如下所示:

await page.goto('https://stackoverflow.com/users/4794')
const nameElement = await context.page.$eval('h2.user-card-name', el => el.text())
console.log(nameElement)
const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto(url);
  await page.waitFor(2000);
  let html_content = await page.evaluate(el => el.innerHTML, await page.$('.element-class-name'));
  console.log(html_content);
} catch (err) {
  console.log(err);
}

我成功地使用了以下工具:

await page.goto('https://stackoverflow.com/users/4794')
const nameElement = await context.page.$eval('h2.user-card-name', el => el.text())
console.log(nameElement)
const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto(url);
  await page.waitFor(2000);
  let html_content = await page.evaluate(el => el.innerHTML, await page.$('.element-class-name'));
  console.log(html_content);
} catch (err) {
  console.log(err);
}
希望有帮助。

我使用页面。$eval

const text = await page.$eval('h2.user-card-name', el => el.innerText );
console.log(text);


最近才将ElementHandle作为参数传递到
页面。evaluate
中。你的代码将在下一个版本的《木偶演员》中使用。我想我的时间安排不好。感谢您的解释,@Joel.Thank,但我尝试在Node 6中不使用async和Wait来实现这一点。在这种情况下,只需将其放入babel中即可。我认为返回的DOM元素不会包含
text()
方法;您可能希望使用InnerText这里的
上下文发生了什么事?
上下文通常在创建Puppeter实例时提供给您。然而,API可能在编写之后发生了变化。