MaxiNet
MaxiNet

Reputation: 1028

PhantomJS is not executing JS

I try to make a screenshot of www.fallswoodsmith.com with PhantomJS. My code is:

var page = require('webpage').create();
page.viewportSize = { width: 1024, height: 768 };
page.clipRect = {top: 0, left: 0, width: 1024, height: 768};

page.open('http://www.fallswoodsmith.com', function () {
    page.render('cache/www.fallswoodsmith.com123567266_1024_768.png', {format: 'png', quality: '10'});
    phantom.exit();
});

This page is JS only, so without JS you get no content. For some reason PhantomJS is not executing this JS. I've also tried to set a timeout of 5 secs for the page.render() and phantom.exit(), but this did not change something. If I do a console.log(page.content) before the page.render() I get the full HTML of the page - just without changes that JS does.

Why does PhantomJS not execute the page's JS?

UPDATE 1: I've added the following debug stuff:

page.onConsoleMessage = function(msg, lineNum, sourceId) {
    console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};

page.onError = function(msg, trace) {

    var msgStack = ['ERROR: ' + msg];

    if (trace && trace.length) {
        msgStack.push('TRACE:');
        trace.forEach(function(t) {
            msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
        });
    }

    console.error(msgStack.join('\n'));

};

page.onResourceError = function(resourceError) {
    console.log('Unable to load resource (#' + resourceError.id + 'URL:' + resourceError.url + ')');
    console.log('Error code: ' + resourceError.errorCode + '. Description: ' + resourceError.errorString);
};

page.onResourceTimeout = function(request) {
    console.log('Response (#' + request.id + '): ' + JSON.stringify(request));
};

No console.log() output in my console...

Upvotes: 5

Views: 7687

Answers (2)

Zon
Zon

Reputation: 19918

Since version 2.1 phantomjs has polyfill included into distribution's javascript engine. Try their latest version.

Upvotes: 0

Whymarrh
Whymarrh

Reputation: 13585

If I may politely ask, who made that site? I would very much recommend not building sites in such a way that they are 100% dependent on JavaScript. Turning off JavaScript and "loading" that site (www.fallswoodsmith.com) results in nothing. zip. nada. zilch. </rant>

The answer you are looking for

Running the screenshot script you have above, I get the following output:

TypeError: 'undefined' is not a function (evaluating 'joinURL.bind(null, staticServerUrl)')

  http://static.parastorage.com/services/santa-versions/1.150.0/main-r.js:353 in wixRenderSite

To fix that issue, you can polyfill Function.prototype.bind (which is missing from PhantomJS 1.x, as per this issue) after the web page object is created but before a URL is loaded (i.e. onInitialized).

The result:

var page = require('webpage').create();

page.onInitialized = function () {
    page.evaluate(function () {
        var isFunction = function (obj) {
            return typeof obj == 'function' || false;
        };
        var slice = Array.prototype.slice;
        Function.prototype.bind = function bind(obj) {
            var args = slice.call(arguments, 1);
            var self = this;
            var F = function () {};
            var bounded = function() {
                return self.apply(
                    this instanceof F ? this : (obj || {}),
                    args.concat(slice.call(arguments))
                );
            };
            F.prototype = this.prototype || {};
            bounded.prototype = new F();
            return bounded;
        };
    });
};

page.open('http://www.fallswoodsmith.com', function () {
    setTimeout(function screenshot() {
        page.render('WORKS.png', {
            format: 'png',
            quality: '10',
        });
        phantom.exit();
    }, 10 * 1000);
});

Why wait 10 seconds before taking a screenshot? Well, since the site is completely reliant on JS, there is no obvious event (that I can think of) to wait for that indicates the loading of the page. Your mileage may vary. Increase or decrease the timeout as you see fit.

Note: the output filename above is WORKS.png.

PhantomJS versions

The above example was tested and works with PhantomJS 1.9.7. The script seems to work with PhantomJS 1.9.8 as well, but 1.9.8 has this issue (Unsafe JavaScript attempt to access frame in 1.9.8) that, while fixed, is not part of any release and causes the following error-looking output:

Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL file://28011634.js. Domains, protocols and ports must match.

Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL file://28011634.js. Domains, protocols and ports must match.

Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL file://28011634.js. Domains, protocols and ports must match.

Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL file://28011634.js. Domains, protocols and ports must match.

Viewport size

By default, the rendered image will be a full page screenshot. To fix the viewport size, you can add back in the following at the top of the script:

page.viewportSize = {
    width: 1024,
    height: 768
};
page.clipRect = {
    top: 0,
    left: 0,
    width: 1024,
    height: 768
};

Polyfilling .bind

The polyfill found on MDN, doesn't seem to work without a bit of modification, but that, combined with the underscore.js source code and this answer resulted in the above.

Upvotes: 8

Related Questions