Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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...

like image 812
MaxiNet Avatar asked Jan 18 '15 15:01

MaxiNet


1 Answers

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.

like image 153
Whymarrh Avatar answered Nov 20 '22 05:11

Whymarrh