Generating Website Screenshots with PHP

25th January 2016 | Tags:

In this post, I’m going to intro­duce a PHP pack­age I recently released for gen­er­at­ing screen­shots of web pages. It’s pretty straight­for­ward to get up-​and-​running, as I’ll demonstrate.

Why Gen­er­ate Screenshots?

There are a num­ber of rea­sons you may wish to gen­er­ate screen­shots. My moti­va­tion behind the pack­age orig­i­nally was to build a sim­ple “direc­tory” of web­sites, using screen­shots to illus­trate the sites.

Screen­shots are also use­ful in test­ing; you could even run a script to peri­od­i­cally gen­er­ate a screen­shot of one of the sites you main­tain in order to check that every­thing is ren­der­ing okay.

What are the Options?

Before I intro­duce the pack­age, it’s worth men­tion­ing that there are a num­ber of third-​party ser­vices which make it even eas­ier to gen­er­ats screen­shots such as Brow­shot, URL2PNG, Shrink the Web and Url­box — but it’s worth not­ing that these are all paid ser­vices. If you’d rather do it your­self, or do it on-​the-​cheap, you might like to try out my package.

How does it Work?

The pack­age uses Phan­tomJS to gen­er­ate screen­shots. Phan­tomJS is basi­cally a script­able, head­less web browser. Essen­tially what it does is launch a “vir­tual” web browser, nav­i­gate to the page, wait a short while for it to down­load and ren­der and then takes a snap­shot, sav­ing it to an image file. It’s worth bear­ing in mind that part about wait­ing for the page to down­load and ren­der, as that wait time is some­thing you may find that you need to tweak. But that’s get­ting ahead of our­selves, rather — so let’s look at how to get up-​and-​running with the package.

Get­ting Started

The pack­age is avail­able on Pack­ag­ist, and also on Github.

Instal­la­tion is via Composer:

composer require lukaswhite/screenshotter

There is one thing to men­tion before you can use it; the pack­age includes a bin direc­tory which con­tains the Phan­tomJS exe­cuta­bles; you must, there­fore, ensure that its con­tents are exe­cutable. For example:

chmod -R +x vendor/lukaswhite/screenshotter/src/bin

Depend­ing on your set-​up and per­mis­sions, you may find that this is taken care of for you by Com­poser; specif­i­cally, the post-install command.

Basic Usage

The first thing you’ll need to do is cre­ate an instance of the Screenshotter class:

$screenshotter = new \Lukaswhite\Screenshotter\Screenshotter(
  $ouputPath, 
  $cachePath
);

The $outputPath indi­cates where the result­ing screen­shots will go; make sure it’s write­able. $cachePath is used to gen­er­ate small text files which are used to run the ser­vice; your best bet here is just to pro­vide a tem­po­rary directory.

So, for example:

$screenshotter = new \Lukaswhite\Screenshotter\Screenshotter(
  '/var/www/example.com/app/storage/screenshots/',
  '/var/tmp/'
);

Here’s a Lar­avel example:

$screenshotter = new \Lukaswhite\Screenshotter\Screenshotter(
  storage_path('screenshots'),
  '/var/tmp/'
);

Once you’ve cre­ated an instance, you can use it as follows:

$screenshot = $screenshotter->capture(
  $url, 
  $filename, 
  $options = []
);

Let’s look at those parameters:

  • $url is the URL of the web­site /​web page you want to take a screen­shot of.
  • $filename is the file­name to save the screen­shot to.
  • $options is an optional array of addi­tional options:
    • $width; if you don’t pro­vide this, it’s set to 1024px
    • $height; if you don’t pro­vide this, it’s set to 768px
    • $clipW; the width to clip (optional)
    • $clipH; the height to clip (optional)

Most of those should be pretty self explana­tory, but let’s look at $clipW and $clipH in slightly more detail.

As stated above, by by default the screen­shot will be based on a web browser “win­dow” set to 1024 by 768 pix­els. This, of course, is con­fig­urable. How­ever the height of the result­ing image could be any­thing; it’s entirely depen­dent on the length of the page. If you want to restrict the screen­shot to a par­tic­u­lar size, you’ll need to set $clipW and $clipH. For exam­ple, to ensure that the screesnhot is exactly 1024 by 768 pixels;

$screenshot = $screenshotter->capture(
  'http://www.lukaswhite.com',
  'lukaswhitedotcom.png',
  [
    'clipW' => 1024,
    'clipH' => 768
  ]
);

To make it 640 x 480 pixels:

$screenshot = $screenshotter->capture(
  'http://www.lukaswhite.com',
  'lukaswhitedotcom.png',
  [
    'width' => 640,
    'height' => 480,
    'clipW' => 640,
    'clipH' => 480
  ]
);

…and so on.

That’s all there is to it. How­ever, it’s worth look­ing at some addi­tional options.

Addi­tional Configuration

The wait time

You’ll recall that I men­tioned the pack­age “waits” a short time while the page has a chance to ren­der. The time it takes to do so can vary enor­mously, of course; par­tic­u­larly if it uses a lot of front-​end script­ing. By default it waits for one sec­ond — specif­i­cally, 1000 milliseconds.

To change that, you can use the fol­low­ing method on the Screenshotter class:

setWait( $value )

The value should be in mil­lisec­onds; do, for example:

$screenshotter->setWait( 3000 ); // wait for three seconds

You may find that you need to play around with this a while to get it right.

Set­ting the SSL Protocol

Phan­tomjs some­times has dif­fi­culty con­nect­ing to HTTPS sites. Chances are if that’s the case, the process will appear to suceed but the screen­shot will be blank.

In many cases this is because by default Phan­tomJS uses SSLv3 to con­nect, which is often either unsup­ported or — thanks to the POO­DLE attack — has been disabled.

To get around this, you can use the setSSLProtocol() method to explic­itly tell Phan­tomjs which SSL pro­to­col to use; the fol­low­ing val­ues are valid:

  • sslv3
  • sslv2
  • tlsv1
  • any

In most cases, the sim­plest approach is sim­ply to set it to all, i.e.:

$screenshotter->setSSLProtocol( 'any' );

Ignor­ing SSL Errors

Cer­tain SSL errors can also cause dif­fi­culty tak­ing screen­shots; most notably, expired or self-​signed cer­tifi­cates. You can tell Phan­tomJS to ignore SSL errors with the following:

$screenshotter->ignoreSSLErrors();
// or
$screenshotter->ignoreSSLErrors( TRUE );

To tell it not to ignore SSL errors — which is the default behav­iour — sim­ply set it to FALSE:

$screenshotter->ignoreSSLErrors( FALSE );

A Note on Exceptions

If the screen­shot process fails, it’ll throw an Excep­tion. Most likely this will be an instance of:

Symfony\Component\Process\Exception\ProcessTimedOutException

This type of excep­tion (a time­out) can hap­pen for a vari­ety of rea­sons, and not just a time­out; because Phan­tomJS is an exter­nal process, it’s not always easy to know what failed, so check your para­me­ters. You can also increase the timeout:

$screenshotter->setTimeout(60); // Set timeout to 60 seconds, instead of the defaut 10

Prob­lems?

If you have any prob­lems using the pack­age, please use the Issues Queue on Github.

Wrap­ping Up

I looked at a cou­ple of pos­si­ble uses for the pack­age, but if you use it for any­thing else then please do let me know in the comments!

Comments

No comments yet.

Links and images are allowed, but please note that rel="nofollow" will be automactically appended to any links.