Skip to content

Instantly share code, notes, and snippets.

@ajxchapman
Created July 11, 2017 13:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajxchapman/d328b9ad995d83c68040a2e6c967f83f to your computer and use it in GitHub Desktop.
Save ajxchapman/d328b9ad995d83c68040a2e6c967f83f to your computer and use it in GitHub Desktop.
Node script to screenshot web pages using chrome debugging protocol
// npm install chrome-remote-interface minimist
const CDP = require('chrome-remote-interface');
const argv = require('minimist')(process.argv.slice(2));
const file = require('fs');
const spawn = require('child_process').spawn;
const net = require('net');
const crypto = require('crypto');
const url = argv.url || 'https://www.google.com';
const id = argv.id || crypto.createHash('sha256').update(url).digest("hex");
const viewportWidth = argv.viewportWidth || 1440;
const viewportHeight = argv.viewportHeight || 900;
const delay = argv.delay || 200;
const debugport = argv.port || 9222
// Reference: https://www.npmjs.com/package/wait-for-port
function wait_for_port(host, port, cb) {
var retriesRemaining = 10;
var retryInterval = 200;
var timer = null, socket = null;
function clearTimerAndDestroySocket() {
clearTimeout(timer);
timer = null;
if (socket) socket.destroy();
socket = null;
}
function tryToConnect() {
clearTimerAndDestroySocket();
if (--retriesRemaining < 0) return cb(new Error('out of retries'));
socket = net.createConnection(port, host, function(err) {
clearTimerAndDestroySocket();
if (retriesRemaining > 0) cb(null);
});
timer = setTimeout(tryToConnect, retryInterval);
socket.on('error', function(err) {
clearTimerAndDestroySocket();
setTimeout(tryToConnect, retryInterval);
});
}
tryToConnect();
};
console.log("Launching chrome");
// WARN: Using no-sandbox to run nicely inside docker for now
const chrome_args = ["--headless", "--hide-scrollbars", `--remote-debugging-port=${debugport}`, "--disable-gpu", "--no-sandbox"];
console.log(chrome_args);
const proc = spawn("google-chrome", chrome_args);
console.log(`Connecting to debug port on 127.0.0.1:${debugport}`);
wait_for_port("127.0.0.1", debugport, (err) => {
if (err) {
console.log("Unable to connect to chrome debug port, exiting");
process.exit(2);
}
// Reference: https://medium.com/@dschnr/using-headless-chrome-as-an-automated-screenshot-tool-4b07dffba79a
CDP(function(client) {
const {Emulation, Page, Security} = client;
Page.loadEventFired(() => {
console.log("Page loaded");
setTimeout(() => {
console.log("Taking screenshot");
Page.captureScreenshot({format : 'png'}, (err, screenshot) => {
if (!err) {
const buffer = new Buffer(screenshot.data, 'base64');
file.writeFileSync(`${id}_screenshot.png`, buffer, 'base64');
}
client.close();
process.exit(0);
});
}, delay);
});
// Reference: https://github.com/cyrus-and/chrome-remote-interface/wiki/Bypass-certificate-errors-(%22Your-connection-is-not-private%22)
Security.certificateError(({eventId}) => {
Security.handleCertificateError({
eventId,
action: 'continue'
});
});
const deviceMetrics = {
width: viewportWidth,
height: viewportHeight,
deviceScaleFactor: 0,
mobile: false,
fitWindow: false,
};
Promise.all([
Page.enable(),
]).then(() => {
console.log("Configuring browser");
return Promise.all([
Emulation.setDeviceMetricsOverride(deviceMetrics),
Emulation.setVisibleSize({width: viewportWidth, height: viewportHeight}),
Security.enable(),
Security.setOverrideCertificateErrors({override: true})
]);
}).then(() => {
console.log("Navigating to page ", url);
return Page.navigate({url});
})
setTimeout(() => {
console.log("Timeout loading page, exiting");
client.close();
process.exit(3);
}, delay + 5000);
}).on('error', err => {
console.error('Cannot connect to browser:', err);
process.exit(4);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment