PhantomJS WebServer 28 July 2013

The PhantomJS WebServer module lets you create self contained web applications that are easy to deploy to heroku using the PhantomJS build pack.

I’ll be talking about this at Oxford geek nights on the 7th of August - come along if you’re in the area.

(tl;dr - deployed example here & more involved app here)

Let’s start with a base PhantomJS script - this loads the Oxfordshire lanyrd page and outputs the names of any upcoming events:

var page = new WebPage();

page.open("http://lanyrd.com/places/oxfordshire/", function(){
  var events = page.evaluate(function(){
    return $('.vevent .summary').map(function(e){
      return '* ' + this.innerText
    }).toArray().join('\n');
  });

  console.log('Upcoming Events in Oxfordshire:');
  console.log(events);

  phantom.exit();
});

This script can be run with phantomjs example.js and it will print the names of all upcoming events in the terminal - something like this:

Upcoming Events in Oxfordshire:
* Oxford Geek Night 32
* WitneyMeets
* XML Summer School 2013
* Sterling Geo Intergraph ERDAS UK UGM
* All Your Base Conference 2013
* jQuery UK 2014
* World Humanist Congress 2014

…super cool. Have a look at the quick start guide on the PhantomJS wiki to find out how this works and what other things are possible.

Using the webserver module

To expose this script with the webserver module, you have to add a few things:

// import the webserver module, and create a server
var server = require('webserver').create();

// start a server on port 8080 and register a request listener
server.listen(8080, function(request, response) {

  var page = new WebPage();

  page.open("http://lanyrd.com/places/oxfordshire/", function(){
    var events = page.evaluate(function(){
      return $('.vevent .summary').map(function(e){
        return '* ' + this.innerText
      }).toArray().join('\n');
    });

    // Rather than console logging, write the data back as a
    // response to the user
    //
    // console.log('Upcoming Events in Oxfordshire:');
    // console.log(events);

    response.statusCode = 200;
    response.write('Upcoming Events in Oxfordshire:\n');
    response.write(events);
    response.close();

    // We want to keep phantom open for more requests, so we
    // don't exit the process. Instead we close the page to
    // free the associated memory heap
    //
    // phantom.exit();

    page.close();

  });
});

This can be run in the same way as the previous script - phantomjs example.js - then when you visit localhost:8080, you should see the list of events in your browser.

localhost:8080 - list of events from lanyrd

With phantomjs, you’re not limited to sending plain text back to the client - you can render images of the webpage and send that back (either by reading the file back with the File System Module, or using base 64 to send back an embeddable data-uri).

Deploying

There is a PhantomJS Buildpack for heroku which makes deploying lovely.

To get your app ready for deployment you have to do a few things:

Set the port based on environment variable PORT

var port = require('system').env.PORT || 8080; // default back to 8080
server.listen(port, function(request, response) {

Add a file named Procfile containing the command to spin it up:

web: phantomjs example.js

Commit your files to git then create a heroku app with the build pack

heroku create --stack cedar --buildpack http://github.com/stomita/heroku-buildpack-phantomjs.git
Creating quiet-lowlands-5118... done, stack is cedar BUILDPACK_URL=http://github.com/stomita/heroku-buildpack-phantomjs.git http://quiet-lowlands-5118.herokuapp.com/ | [email protected]:quiet-lowlands-5118.git Git remote heroku added

Push your code up to heroku

git push heroku master
Counting objects: 10, done. Delta compression using up to 4 threads. Compressing objects: 100% (7/7), done. Writing objects: 100% (10/10), 1.34 KiB, done. Total 10 (delta 2), reused 0 (delta 0) -----> Fetching custom git buildpack... done -----> PhantomJS app detected -----> Fetching PhantomJS 1.9.0 binaries at http://stomita-buildpack-phantomjs.s3.amazonaws.com/buildpack-phantomjs-1.9.0.tar.gz -----> Extracting PhantomJS 1.9.0 binaries to /tmp/build_2idj4c8tadrpx/vendor/phantomjs -----> Discovering process types Procfile declares types -> web Default types for PhantomJS -> console -----> Compiled slug size: 15.5MB -----> Launching... done, v5 http://quiet-lowlands-5118.herokuapp.com deployed to Heroku To [email protected]:quiet-lowlands-5118.git * [new branch] master -> master

The example app should now be available on the reported url (in this case: http://quiet-lowlands-5118.herokuapp.com).

View Deployed Example Site GitHub Source


A more involved example

I’ve put together a more complex version of this style of app - it allows you to specify any webpage, renders a screenshot and returns some information about the page (a list of links).

It also serves a static page with a form to submit the requests to the app. It’s deployed to phantomjs-webserver-example.herokuapp.com and the source code is on github.

I’ve tried to make it an easy project to modify for your own use - so fork away and have a hack!

screenshot of example code

View Demo GitHub Source

A few issues / gotchas