使用Puppeteer获取JavaScript渲染的HTML

23
我正在尝试从此 NCBI.gov 页面抓取HTML。我需要包含 #see-all URL 片段,以确保获取搜索页,而不是从错误的基因页面https://www.ncbi.nlm.nih.gov/gene/119016检索 HTML。

URL 片段不会传递到服务器,而是由页面客户端的 JavaScript 使用(在这种情况下)创建完全不同的 HTML,这是当您使用浏览器进入页面并“查看页面源代码”时获得的 HTML,也是我想要检索的 HTML。R readLines() 忽略后面带有 # 的 URL 标签

我首先尝试使用phantomJS,但它只返回了描述在此处引用错误:找不到变量 Map,似乎是由于phantomJS不支持NCBI正在使用的某些功能,从而消除了这个解决方案的可能性。

我通过使用以下JavaScript,并通过node.js进行评估,更成功地使用了Puppeteer:

const puppeteer = require('puppeteer');
(async() => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(
    'https://www.ncbi.nlm.nih.gov/gene/?term=AGAP8#see-all');
  var HTML = await page.content()
  const fs = require('fs');
  var ws = fs.createWriteStream(
    'TempInterfaceWithChrome.js'
  );
  ws.write(HTML);
  ws.end();
  var ws2 = fs.createWriteStream(
    'finishedFlag'
  );
  ws2.end();
  browser.close();
})();

但是,这返回的似乎是预渲染的 HTML。我应该如何(通过编程)获取在浏览器中得到的最终 HTML 代码?

6个回答

12

你可以尝试更改这个:

await page.goto(
  'https://www.ncbi.nlm.nih.gov/gene/?term=AGAP8#see-all');

变为这样:

  await page.goto(
    'https://www.ncbi.nlm.nih.gov/gene/?term=AGAP8#see-all', {waitUntil: 'networkidle'});

或者,您可以创建一个函数listenFor()来在页面加载时监听自定义事件:

function listenFor(type) {
  return page.evaluateOnNewDocument(type => {
    document.addEventListener(type, e => {
      window.onCustomEvent({type, detail: e.detail});
    });
  }, type);
}`

await listenFor('custom-event-ready'); // Listen for "custom-event-ready" custom event on page load.

这可能也会有用:

await page.waitForSelector('h3'); // replace h3 with your selector

10

也许尝试等待一下

await page.waitForNavigation(5);

之后

let html = await page.content();

这只会给你最初的HTML内容。 - adi ben

3
我成功地使用了以下方法来获取页面加载后生成的 HTML 内容。
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

确实需要使用innerHTML:

fs.writeFileSync( "test.html", await (await page.$("html")).evaluate( (content => content.innerHTML ) ) );

1

在我的情况下,等待网络空闲是不够的,因此我使用了dom加载事件:

await page.goto(url, {waitUntil: 'domcontentloaded', timeout: 60000} );
const data = await page.content();

0

如果你想要等待一个自定义事件,可以这样做。

const page = await browser.newPage();

/**
  * Attach an event listener to page to capture a custom event on page load/navigation.
  * @param {string} type Event name.
  * @return {!Promise}
  */
function addListener(type) {
  return page.evaluateOnNewDocument(type => {
    // here we are in the browser context
    document.addEventListener(type, e => {
      window.onCustomEvent({ type, detail: e.detail });
    });
  }, type);
}

const evt = await new Promise(async resolve => {
  // Define a window.onCustomEvent function on the page.
  await page.exposeFunction('onCustomEvent', e => {
    // here we are in the node context
    resolve(e); // resolve the outer Promise here so we can await it outside
  });

  await addListener('app-ready'); // setup listener for "app-ready" custom event on page load
  await page.goto('http://example.com');  // N.B! Do not use { waitUntil: 'networkidle0' } as that may cause a race condition
});

console.log(`${evt.type} fired`, evt.detail || '');

基于https://github.com/GoogleChrome/puppeteer/blob/master/examples/custom-event.js的示例构建


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接