Skip to content

Automating report generation using Puppeteer

DEPRECATION NOTICE Information provided in this article is deprecated and no longer valid, because of the deal-breaking issues with the Puppeteer. Please refer to “Automating report generation using Selenium Webdriver” for a working solution.

If you need to automate the generation of chart images or PDF, utilities with a self contained browser are your best options. There are few options out there as headless browsers.

We created this example based on Puppeteer as this is a new option supported by Google that uses latest Chrome.

Puppetter is a npm package that contains Chrome on its distribution. When you run Puppeteer,
Chrome opens and you can run a given page in it.

Check more about Puppeteer on the links below:

  • Install NodeJS

  • Create a folder

  • Create a package.json file with the content shown below:

    {
    "name": "amCharts 4",
    "description": "Automate chart export",
    "engines": {
    "node": ">=6.0.0"
    },
    "dependencies": {
    "puppeteer": "^1.5.0"
    }
    }
  • Open a terminal and run npm i on the same folder to install the dependencies

Section titled “Generating a single chart report from external link”

You can export charts that exist on a current page.

Add a file called external.js for now. You can rename it as you wish. Add the code below
in it:

const puppeteer = require('puppeteer');
(async () => {
let url = 'https://s.codepen.io/team/amcharts/fullpage/eKoVEP';
// Open the browser
const browser = await puppeteer.launch({
// Run the browser headless or not
headless: true
});
// Open a tab
const page = await browser.newPage();
// Set the referer. That is only needed if the server needs it for security reasons
await page.setExtraHTTPHeaders({
'referer': url
});
await page.setRequestInterception(true);
page.on('request', request => {
request.continue();
});
// Defines where the file should be saved
await page._client.send('Page.setDownloadBehavior', {
behavior: 'allow',
// Using a folder at the root to save the images
downloadPath: '/export'
});
// Define the URL where the chart is
await page.goto(url);
// Run scripts in the page
await page.evaluate(() => {
// Make the evaluate callback wait until the export is done
return new Promise((resolve, reject) => {
// Call the export on the chart
chart.exporting.export('pdf').finally(() => {
// Wait for the download to get done
setTimeout(() => {
resolve();
}, 2000);
});
});
});
// Close the browser
await browser.close();
})();

Now run node external.js.

Generating a single chart report from the same package

Section titled “Generating a single chart report from the same package”

If you prefer to create charts without requirement external pre-existing pages, you can have them built into this solution. We will setup a server using HapiJS by first adding the following dependencies in the package.json file:

{
"name": "amCharts 4",
"description": "Automate chart export",
"engines": {
"node": ">=6.0.0"
},
"dependencies": {
"puppeteer": "^1.5.0",
"hapi": "17.5.1",
"inert": "5.1.0"
}
}

Run npm i to install the new dependencies.

Add server.js file and the following code into it:

const Hapi = require('hapi');
const Inert = require('inert');
const exportChart = require('./internal');
const server = new Hapi.Server({
host: 'localhost',
port: 3000
});
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return h.file('./public/index.html');
}
});
server.route({
method: 'GET',
path: '/{fileName*}',
handler: (request, h) => {
return h.file('./public/' + request.params.fileName);
}
});
const init = async () => {
await server.register(Inert);
await server.start();
console.log(`Server running at: ${server.info.uri}`);
}
server.events.on('start', () => {
// Start the export feature
exportChart(server);
console.log('Server started and the export module is loaded');
});
server.events.on('stop', () => {
console.log('Server stopped');
});
init();

At this point you should some of the files below. Please add the missing folders and files as shown and then the correspondent content:

/
├── package.json
├── server.js
├── internal.js
├── external.js
└── public/
├── index.html
├── styles/
| └── app.css
└── scripts/
└── chart.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>amCharts 4</title>
<link rel="stylesheet" href="/styles/app.css">
</head>
<body>
<div id="chartdiv"></div>
<script src="//www.amcharts.com/lib/4/core.js"></script>
<script src="//www.amcharts.com/lib/4/charts.js"></script>
<script src="//www.amcharts.com/lib/4/themes/animated.js"></script>
<script src="/scripts/chart.js"></script>
</body>
</html>
@import url("https://fonts.googleapis.com/css?family=Archivo+Narrow");
body {
font-family: "Archivo Narrow", Arial;
}
#chartdiv {
width: 100%;
height: 500px;
}
am4core.useTheme(am4themes_animated);
var chart = am4core.create("chartdiv", am4charts.XYChart);
chart.data = [{
category: "One",
value1: 1,
value2: 5,
value3: 3,
value4: 3
}, {
category: "Two",
value1: 2,
value2: 5,
value3: 3,
value4: 4
}, {
category: "Three",
value1: 3,
value2: 5,
value3: 4,
value4: 4
}, {
category: "Four",
value1: 4,
value2: 5,
value3: 6,
value4: 4
}, {
category: "Five",
value1: 3,
value2: 5,
value3: 4,
value4: 4
}, {
category: "Six",
value1: 8,
value2: 7,
value3: 10,
value4: 4
}, {
category: "Seven",
value1: 10,
value2: 8,
value3: 6,
value4: 4
}];
chart.legend = new am4charts.Legend();
chart.colors.step = 2;
var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "category";
categoryAxis.renderer.grid.template.location = 0;
var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
valueAxis.renderer.minWidth = 35;
var series1 = chart.series.push(new am4charts.ColumnSeries());
series1.columns.template.width = am4core.percent(80);
series1.columns.template.tooltipText = "{name}: {valueY.value}";
series1.name = "Series 1";
series1.dataFields.categoryX = "category";
series1.dataFields.valueY = "value1";
series1.stacked = true;
var series2 = chart.series.push(new am4charts.ColumnSeries());
series2.columns.template.width = am4core.percent(80);
series2.columns.template.tooltipText = "{name}: {valueY.value}";
series2.name = "Series 2";
series2.dataFields.categoryX = "category";
series2.dataFields.valueY = "value2";
series2.stacked = true;
var series3 = chart.series.push(new am4charts.ColumnSeries());
series3.columns.template.width = am4core.percent(80);
series3.columns.template.tooltipText = "{name}: {valueY.value}";
series3.name = "Series 3";
series3.dataFields.categoryX = "category";
series3.dataFields.valueY = "value3";
series3.stacked = true;
var series4 = chart.series.push(new am4charts.ColumnSeries());
series4.columns.template.width = am4core.percent(80);
series4.columns.template.tooltipText = "{name}: {valueY.value}";
series4.name = "Series 4";
series4.dataFields.categoryX = "category";
series4.dataFields.valueY = "value4";
series4.stacked = true;
chart.scrollbarX = new am4core.Scrollbar();
chart.exporting.menu = new am4core.ExportMenu();
const puppeteer = require('puppeteer');
module.exports = async function (server) {
let url = 'http://localhost:3000';
// Open the browser
const browser = await puppeteer.launch({
// Run the browser headless or not
headless: true
});
// Open a tab
const page = await browser.newPage();
// Set the referer. That is only needed if the server needs it for security reasons
await page.setExtraHTTPHeaders({
'referer': url
});
await page.setRequestInterception(true);
page.on('request', request => {
request.continue();
});
// Defines where the file should be saved
await page._client.send('Page.setDownloadBehavior', {
behavior: 'allow',
// Using a folder at the root to save the images
downloadPath: '/export'
});
// Define the URL where the chart is
await page.goto(url);
// Run scripts in the page
await page.evaluate(() => {
// Make the evaluate callback wait until the export is done
return new Promise((resolve, reject) => {
// Call the export on the chart
chart.exporting.export('pdf').finally(() => {
// Wait for the download to get done
setTimeout(() => {
resolve();
}, 2000);
});
});
});
// Wait for the browser to close to stop the server
browser.on('disconnected', async () => {
// Stop the server
await server.stop({
timeout: 3000
});
});
// Close the browser
await browser.close();
};

Now run node server.js to start the server and to generate the chart.

Where to customize the export functionality

Section titled “Where to customize the export functionality”

You can customize the code calling the export functionality inside the `page.evaluate()` method as shown below:

// Run scripts in the page
await page.evaluate(() => {
// Make the evaluate callback wait until the export is done
return new Promise((resolve, reject) => {
// Call the export on the chart
chart.exporting.export('pdf').finally(() => {
// Wait for the download to get done
setTimeout(() => {
resolve();
}, 2000);
});
});
});

Please check more about exporting here.

Download the whole example here and run npm i to load the dependencies.