使用 Puppeteer 渲染 Google 个图表

Render Google Charts with Puppeteer

我正在尝试找出结合 Puppeteer 和 GoogleCharts 库以呈现条形图并导出图表的 PNG 图像的正确方法。

主要受 Puppeteer 文档启发的基本布局似乎与此类似,用于创建一个包含 canvas 元素的新页面。

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    await page.setContent(`
            <!DOCTYPE html>
                <html>
                    <body>
                        <canvas id="chart" width="580" height="400"></canvas>
                    </body>
                </html>
            `);
    await browser.close();
})();

我发现这个 npm 包可以与 Google 图表一起使用:https://www.npmjs.com/package/google-chartschart.draw 方法似乎接受一个 DOM 元素,它将在其中呈现图表。

根据这个包的文档,用法看起来很简单。

const pie_1_chart = new GoogleCharts.api.visualization.PieChart(document.getElementById('chart1'));
pie_1_chart.draw(data);

如何执行 draw 方法,以便它在 Puppeteer 页面的 #canvas DOM 元素内呈现图表?

此外,如果您能给我一个将 page/canvas 导出为 PNG 图像的正确方法的示例,我们将不胜感激。

注意:我浏览了很多,找到了一个现有的 library/package 可以实现我想要实现的目标,但我能找到的最接近的是一个 TypeScript 包对于 Chart.JS:https://github.com/SeanSobey/ChartjsNodePuppeteer。我不熟悉 TypeScript/ES6,所以我不知道如何调整此库以使其与 Google 图表库一起使用,而不是 Chart.JS。

谢谢

我设置了一个 puppeteer 代码,它加载 Google 图表示例页面的 Iframe,然后截取 elementHandle #chart_div。 这比加载另一个库要快得多,也简单得多。

接下来您可以使用 div 元素创建 HTML DOM 并使用它设置 Google 图表并截取 div 元素。

    const page = await browser.newPage()
    page.setDefaultNavigationTimeout(0);

    const goto = await page.goto('https://developers-dot-devsite-v2-prod.appspot.com/chart/interactive/docs/gallery/barchart_9a8ce4c79b8da3fa2e73bee14869e01b6f7fb5150dc7540172d60b5680259b48.frame')
    const wait = await page.waitForSelector('#chart_div > div', {'visible' : true, 'timeout' : 0})
    const elem = await page.$('#chart_div')
    const save = await elem.screenshot({path : 'googlechart.png' })

我遇到了同样的问题并将其烘焙到一个名为 Google Charts Node 的开源库中。

这是一个用法示例:

const GoogleChartsNode = require('google-charts-node');

// Define your chart drawing function
function drawChart() {
  const data = google.visualization.arrayToDataTable([
    ['City', '2010 Population',],
    ['New York City, NY', 8175000],
    ['Los Angeles, CA', 3792000],
    ['Chicago, IL', 2695000],
    ['Houston, TX', 2099000],
    ['Philadelphia, PA', 1526000]
  ]);

  const options = {
    title: 'Population of Largest U.S. Cities',
    chartArea: {width: '50%'},
    hAxis: {
      title: 'Total Population',
      minValue: 0
    },
    vAxis: {
      title: 'City'
    }
  };

  const chart = new google.visualization.BarChart(container);
  chart.draw(data, options);
}

// Render the chart to image
const image = await GoogleChartsNode.render(drawChart, {
  width: 400,
  height: 300,
});

render 方法 returns 包含此图像的缓冲区:

它在 NPM 上可用 google-charts-node

关于使用 Puppeteer 渲染的一般问题,我发现它相当简单。我尝试尽可能使用图表提供的方法 getImageURI,但并非所有图表都支持它。在这种情况下,我使用 Puppeteer 截屏。

这是基本逻辑(也可以查看完整的 source code):

async function render() {
  // Puppeteer setup
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // Add the chart
  await page.setContent(`...Insert your Google Charts code here...`);

  // Use getImageURI if available (not all charts support)
  const imageBase64 = await page.evaluate(() => {
    if (!window.chart || typeof window.chart.getImageURI === 'undefined') {
      return null;
    }
    return window.chart.getImageURI();
  });

  let buf;
  if (imageBase64) {
    buf = Buffer.from(imageBase64.slice('data:image/png;base64,'.length), 'base64');
  } else {
    // getImageURI was not available - take a screenshot using puppeteer
    const elt = await page.$('#chart_div');
    buf = await elt.screenshot();
  }

  await browser.close();
  return buf;
}

我已经使用下面的代码行在 node.js 应用程序中设置了 puppeteer。在 pdf 文件中显示图表工作正常。

对我来说 defaultViewport: null 和 args: ['--window-size=1920,1080'] 的组合让我全屏显示适合屏幕大小的视图,图表显示适当的宽度应用中显示:

const browser = await puppeteer.launch({
      headless: true,
      defaultViewport: null,
      args: [
        '--window-size=1366,768',
        '--no-sandbox',
        '--disable-setuid-sandbox',
      ],
});
const page = await browser.newPage();
await page.setDefaultNavigationTimeout(0);
await page.goto(reportUrl, {
    waitUntil: ['load', 'domcontentloaded', 'networkidle0'],
});

这是我使用 PDF 文件中的图表的代码,解决了获取 PDF 文件中图表的完整宽度的问题,类似于应用程序视图

defaultViewport 的选项:null 和 args:['--window-size=1920,1080'] 在应用程序中对我有用,并提供完整视图以创建图表的 pdf

const browser = await puppeteer.launch({
      headless: true,
      defaultViewport: null,
      args: [
        '--window-size=1366,768'
      ],
});
const page = await browser.newPage();
await page.setDefaultNavigationTimeout(0);
await page.goto(reportUrl, {
    waitUntil: ['load', 'domcontentloaded', 'networkidle0'],
});