Skip to content

Capture A Screenshot

Michael Kenney edited this page Sep 29, 2018 · 2 revisions

Testing the script

To test the script below in a docker environment, save it to a file named main.go in an otherwise empty directory and run this command from the same location:

docker run -it --rm \
	-v $(pwd):/go/src/test \
	-v $(pwd):/tmp/test \
	-e LOG_LEVEL=info \
	mkenney/chromium-headless:latest \
		sh -cexu "cd /go/src/test \
			&& (dep init || (dep ensure && dep ensure -update)) \
			&& go build -o /go/bin/app \
			&& /go/bin/app"

The screenshot will be written to a file named example.jpg in that location.

Screenshot script

For the screenshot to work as expected, we have to wait for the page to load. Requesting a screenshot will generate an image of whatever Chrome has rendered at the time of the request, usually a blank or partially rendered screen if the page hasn't been given time to load.

This script uses an event listener to wait until the Page.loadEventFired event fires before rendering the screenshot.

package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"

	chrome "github.com/mkenney/go-chrome/tot"
	"github.com/mkenney/go-chrome/tot/emulation"
	"github.com/mkenney/go-chrome/tot/page"
)

func main() {
	var err error

	// Define a chrome instance with remote debugging enabled.
	browser := chrome.New(
		// See https://developers.google.com/web/updates/2017/04/headless-chrome#cli
		// for details about startup flags
		&chrome.Flags{
			"addr":               "localhost",
			"disable-extensions": nil,
			"disable-gpu":        nil,
			"headless":           nil,
			"hide-scrollbars":    nil,
			"no-first-run":       nil,
			"no-sandbox":         nil,
			"port":               9222,
			"remote-debugging-address": "0.0.0.0",
			"remote-debugging-port":    9222,
		},
		"/usr/bin/google-chrome", // Path to Chromeium binary
		"/tmp",      // Set the Chromium working directory
		"/dev/null", // Ignore internal Chromium output, set to empty string for os.Stdout
		"/dev/null", // Ignore internal Chromium errors, set to empty string for os.Stderr
	)

	// Start the chrome process.
	if err := browser.Launch(); nil != err {
		panic(err)
	}

	// Open a tab and navigate to the URL you want to screenshot.
	tab, err := browser.NewTab("https://www.google.com")
	if nil != err {
		panic(err)
	}

	// Enable Page events for this tab.
	if enableResult := <-tab.Page().Enable(); nil != enableResult.Err {
		panic(enableResult.Err)
	}

	// Create a channel to receive the screenshot data generated by the
	// event handler.
	results := make(chan *page.CaptureScreenshotResult)

	// Create an event handler that will execute when the page load event
	// fires with a closure that will capture the screenshot and write
	// the result to the results channel. This can also be done manually
	// via:
	//	eventHandler := socket.NewEventHandler(
	//		"Page.loadEventFired",
	//		func(response *socket.Response) {
	//			...
	//			results <- screenshotResult
	//		},
	//	)
	//	tab.AddEventHandler(eventHandler)
	tab.Page().OnLoadEventFired(
		// This function will generate a screenshot and write the data
		// to the results channel.
		func(event *page.LoadEventFiredEvent) {

			// Set the device emulation parameters.
			overrideResult := <-tab.Emulation().SetDeviceMetricsOverride(
				&emulation.SetDeviceMetricsOverrideParams{
					Width:  1440,
					Height: 1440,
					ScreenOrientation: &emulation.ScreenOrientation{
						Type:  emulation.OrientationType.PortraitPrimary,
						Angle: 90,
					},
				},
			)
			if nil != overrideResult.Err {
				panic(overrideResult.Err)
			}

			// Capture a screenshot of the current state of the current
			// page.
			screenshotResult := <-tab.Page().CaptureScreenshot(
				&page.CaptureScreenshotParams{
					Format:  page.Format.Jpeg,
					Quality: 50,
				},
			)
			if nil != screenshotResult.Err {
				panic(screenshotResult.Err)
			}

			results <- screenshotResult
		},
	)

	// Wait for the handler to fire
	result := <-results
	if nil != result.Err {
		panic(result.Err)
	}

	// Decode the base64 encoded image data
	data, err := base64.StdEncoding.DecodeString(result.Data)
	if nil != err {
		panic(err)
	}

	// Write the generated image to a file
	err = ioutil.WriteFile("/tmp/test/example.jpg", data, 0644)
	if nil != err {
		panic(err)
	}

	fmt.Println("Finished rendering example.jpg")
}