web-dev-qa-db-ja.com

PuppeteerとヘッドレスでDOMノードテキストを取得するChrome

ヘッドレス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ノードのテキストを抽出するにはどうすればよいですか?

10
Don Kirkby

抽出の複雑さに応じて、この問題の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();
            });
        });
    });
});

まったく同じ結果が得られます。ここで、エラー処理を追加する必要があり、インデントレベルを下げる必要があるかもしれません。

9
Don Kirkby

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)
3
RobLoach

以下を使用して成功しました。

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);
}

それが役に立てば幸い。

1
Darren Hall

Page。$ evalを使用します

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

1
vnguyen