ヘッドレスChromeとPuppeteerを使用してJavascriptテストを実行しようとしていますが、ページから結果を抽出できません。 this answer 、it page.evaluate()
を使用する必要があるように見えます。そのセクションには、必要なもののように見える例さえあります。
_const bodyHandle = await page.$('body');
const html = await page.evaluate(body => body.innerHTML, bodyHandle);
await bodyHandle.dispose();
_
完全な例として、Stack Overflowのユーザープロファイルから名前を抽出するスクリプトに変換しようとしました。プロジェクトではNode 6を使用しているため、await
式を.then()
を使用するように変換しました。
_const puppeteer = require('puppeteer');
puppeteer.launch().then(function(browser) {
browser.newPage().then(function(page) {
page.goto('https://stackoverflow.com/users/4794').then(function() {
page.$('h2.user-card-name').then(function(heading_handle) {
page.evaluate(function(heading) {
return heading.innerText;
}, heading_handle).then(function(result) {
console.info(result);
browser.close();
}, function(error) {
console.error(error);
browser.close();
});
});
});
});
});
_
それを実行すると、次のエラーが表示されます。
_$ 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)
_
問題は、page.evaluate()
への入力パラメーターのシリアル化にあるようです。文字列と数字を渡すことはできますが、要素ハンドルは渡せません。例は間違っていますか、またはNode 6の問題ですか?DOMノードのテキストを抽出するにはどうすればよいですか?
抽出の複雑さに応じて、この問題の3つの解決策を見つけました。最も単純なオプションは、私が気付いていなかった関連関数です page.$eval()
。基本的に、私がやろうとしていたことを行います: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();
});
});
});
});
まったく同じ結果が得られます。ここで、エラー処理を追加する必要があり、インデントレベルを下げる必要があるかもしれません。
await/async
および $eval
、構文は次のようになります。
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);
}
それが役に立てば幸い。
Page。$ evalを使用します
const text = await page.$eval('h2.user-card-name', el => el.innerText );
console.log(text);