Where are screenshots from phantom.js saved?

2019-04-27 01:27发布

问题:

Just starting out with Phantom.js after installing via Homebrew on my mac.

I'm trying out the examples to save screenshots of websites via https://github.com/ariya/phantomjs/wiki/Quick-Start

var page = require('webpage').create();
page.open('http://google.com', function () {
    page.render('google.png');
    phantom.exit();
});

But I don't see the images anywhere. Will they be in the same directory as the .js file?

回答1:

PhantomJS usually renders the images to the same directory as the script that you're running. So yes, it should be in the same directory as the JavaScript file that you're running using PhantomJS.

EDIT

It appears that that particular example is flawed. The problem is that page.render(...); takes some time to render the page, but you're calling phantom.exit() before it has finished rendering. I was able to get the expected output by doing this:

var page = require('webpage').create();
page.open('http://google.com', function () {
    page.render('google.png');
    setTimeout(function() { phantom.exit(); }, 5000) // wait five seconds and then exit;
});

Unfortunately this isn't ideal, so I was able to come up with something that's a hair better. I say a "hair", because I'm basically polling to see when the page has finished rendering:

var done = false; //flag that tells us if we're done rendering

var page = require('webpage').create();
page.open('http://google.com', function (status) {
    //If the page loaded successfully...
    if(status === "success") {
        //Render the page
        page.render('google.png');
        console.log("Site rendered...");

        //Set the flag to true
        done = true;
    }
});

//Start polling every 100ms to see if we are done
var intervalId = setInterval(function() {
    if(done) {
        //If we are done, let's say so and exit.
        console.log("Done.");
        phantom.exit();
    } else {
        //If we're not done we're just going to say that we're polling
        console.log("Polling...");
    }
}, 100);

The code above works because the callback isn't immediately executed. So the polling code will start up and start to poll. Then when the callback is executed, we check to see the status of the page (we only want to render if we were able to load the page successfully). Then we render the page and set the flag that our polling code is checking on, to true. So the next time the polling code runs, the flag is true and so we exit.

This looks to be a problem with the way PhantomJS is running the webpage#render(...) call. I suspected that it was a non-blocking call, but according to the author in this issue, it is a blocking call. If I had to hazard a guess, perhaps the act of rendering is a blocking call, but the code that does the rendering might be handing off the data to another thread, which handles persisting the data to disk (so this part might be a non-blocking call). Unfortunately, this call is probably still executing when execution comes back to the main script and executes phantom.exit(), which means that the aforementioned asynchronous code never gets a chance to finish what it's doing.

I was able to find a post on the PhantomJS forums that deals with what you're describing. I can't see any issue that has been filed, so if you'd like you can go ahead and post one.



回答2:

I have the very same issue as the author of this post, and none of the code examples worked for me. Kind of disorienting to have the second example in Phantom.js documentation not work. Installed using Home Brew on Snow Leopard.

I found a working example

var page = require("webpage").create();
var homePage = "http://www.google.com/";
page.settings.javascriptEnabled = false;
page.settings.loadImages = false;
page.open(homePage);
page.onLoadFinished = function(status) {
  var url = page.url;
  console.log("Status:  " + status);
  console.log("Loaded:  " + url);
  page.render("google.png");
  phantom.exit();
};


回答3:

Just a quick help for people who come here looking for the directory where PhantomJS or CasperJS's screenshots are saved: it is in the scripts directory by default. However, you do have control.

If you want to control where they are saved you can just alter the filename like so:

page.render('screenshots/google.jpg'); // saves to scriptLocation/screenshots/
page.render('/opt/google.jpg'); // saves to /screenshots (in the root)

Or if you use CasperJS you can use:

casper.capture('/opt/google.jpg',
        undefined,
        { // imgOptions
            format: 'jpg',
            quality: 25
        }); 

Hope this saves someone the trouble!



回答4:

I'm not sure if something has changed, but the example on http://phantomjs.org/screen-capture.html worked fine for me:

var page = require('webpage').create();
page.open('http://github.com/', function() {
  page.render('github.png');
  phantom.exit();
});

I am running phantomjs-2.1.1-windows.

However, what led me to this thread was initially the image file was never getting created on my disk just like the original question. I figured it was some kind of security issues so I logged into a VM and started by putting everything in the same directory. I was using a batch file to kick off phantomjs.exe with my .js file passed in as a parameter. With all of the files in the same directory on my VM, it worked great. Through trial and error, I found that my batch file had to be in the same directory as my .js file. Now all is well on my host machine as well.

So in summary...it was probably a security related problem for me. No need for adding any kind of timeout.

My answer to the original question would be IF you have everything set up correctly, the file will be in the same directory as the .js file that phantomjs.exe runs. I tried specifying a fully qualified path for the image file but that didn't seem to work.



回答5:

The root cause is that page.render() may not be ready to render the image even during the onLoadFinished() event. You may need to wait upwards of several seconds before page.render() can succeed. The only reliable way I found to render an image in PhantomJS is to repeatedly invoke page.render() until the method returns true, indicating it successfully rendered the image.

Solution:

var page = require("webpage").create();
var homePage = "http://www.google.com/";

page.onLoadFinished = function(status) {
  var rendered, started = Date.now(), TIMEOUT = 30 * 1000; // 30 seconds
  while(!((rendered = page.render('google.png')) || Date.now() - started > TIMEOUT));
  if (!rendered) console.log("ERROR: Timed out waiting to render image.")
  phantom.exit();
};

page.open(homePage);


回答6:

Steal from rasterize.js example, it works more reliable than the accepted answer for me.

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

page.open('http://localhost/TestForTest/', function (status) {
    console.log("starting...");
    console.log("Status: " + status);

    if (status === "success") {
        window.setTimeout(function () {
            page.render('myExample.png');
            phantom.exit();
        }, 200);
    } else {
        console.log("failed for some reason.");
    }
});


回答7:

Plenty of good suggestions here. The one thing I'd like to add:

I was running a phantomjs docker image, and the default user was "phantomjs", not root. I was therefore trying to write to a location that I didn't have permission on (it was the pwd on the docker host)...

> docker run -i -t -v $(pwd):/pwd --rm wernight/phantomjs touch /pwd/foo.txt
touch: cannot touch '/pwd/foo.txt': Permission denied

The code(s) above all run without error, but if they don't have permission to write to the destination then they will silently ignore the request...

So for example, taking @vivin-paliath's code (the current accepted answer):

var done = false; //flag that tells us if we're done rendering

var page = require('webpage').create();
page.open('http://google.com', function (status) {
    //If the page loaded successfully...
    if(status === "success") {
        //Render the page
        page.render('google.png');
        console.log("Site rendered...");

        //Set the flag to true
        done = true;
    }
});

//Start polling every 100ms to see if we are done
var intervalId = setInterval(function() {
    if(done) {
        //If we are done, let's say so and exit.
        console.log("Done.");
        phantom.exit();
    } else {
        //If we're not done we're just going to say that we're polling
        console.log("Polling...");
    }
}, 100);

And running it as the default user produces:

docker run -i -t -v $(pwd):/pwd -w /pwd --rm wernight/phantomjs phantomjs google.js 
Polling...
Polling...
Polling...
Polling...
Polling...
Polling...
Polling...
Polling...
Polling...
Polling...
Polling...
Polling...
Site rendered...
Done.

But no google.png and no error. Simply adding -u root to the docker command solves this, and I get the google.png in my CWD.

For completeness, the final command:

docker run -u root -i -t -v $(pwd):/pwd -w /pwd --rm wernight/phantomjs phantomjs google.js