2

I'm writing a bit of coffeescript with phantomjs to take screenshots of multiple urls. Every time I try running it though, I get an error message, Can't open <filename>. What gives? Here's my code:

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

  page.viewportSize =
    width: 1024
    height: 760

  urls = phantom.args
  i = 1
  for url in urls
    do (url) ->
      output = "screenshot-#{i}.png"
      page.open url, (status) ->
        if status isnt 'success'
          console.log "Error opening url \"#{page.reason_url}\": #{page.reason}"
          phantom.exit(1)
        else
          console.log "Page opened.."
          window.setTimeout (->
            page.clipRect =
              top: 0
              left: 0
              width: 1024
              height: 760
            page.render(output)
          ), 200
      i += 1

  phantom.exit()

I tried commenting bits out and it seems that the part that's failing is the page.open(url). Oddly the error message says the file itself can't be opened.

DaniG2k
  • 4,772
  • 36
  • 77
  • Have you tried appending "http://google.com" as the first url? Maybe the error message is valid. – jcollum Feb 12 '14 at 17:56
  • Yes I tried inputting a string directly and it hasn't seemed to work :( – DaniG2k Feb 12 '14 at 21:16
  • try hardcoding? like `urls = ['http://google.com']` – jcollum Feb 12 '14 at 21:38
  • That doesn't seem to work either. I've boiled it down to the bare minimum with just a simple `page.open 'http://www.google.com', (status)-> console.log "--->#{status}"` but I get the same error `Can't open 'test.js.coffee'` I'm starting to think there's omething broken with phantomjs's open method. – DaniG2k Feb 13 '14 at 09:18
  • I think it boils down to this piece of code not working: `page.open('http://github.com/', (status)-> console.log("Status: #{status}") page.render('github.png') )` – DaniG2k Feb 13 '14 at 09:58
  • Have a look at this question http://stackoverflow.com/questions/12957239/using-the-webpage-phantom-module-in-node-js that may help. Personally, I'm using Casper with Phantom for page testing, that might be a better way to go. – jcollum Feb 13 '14 at 15:17

1 Answers1

0

(As a quick aside: I think there is a cut-and-paste error in line 14. I believe you want #{url} in place of #{page.reason_url}.)

I see two issues here, or one issue manifest in a couple of ways.

The page.open method is asynchronous -- that's why it accepts a callback function (your (status)->... stuff).

That's causing two types of problem for your code:

  1. Your phantom.exit() call on line 28 is outside of the scope of the for loop (that starts on line 9) and of the callback method (that starts on line 12).

    Because the open call is asynchronous, when urls is ['http://google.com'], the for loop will call page.open('http://google.com/') and then jump to phantom.exit() BEFORE the page has finished loading or the callback is invoked.

    You need to defer your phantom.exit() call until the page.render(output) method is complete. Moving phantom.exit() right after -- and at the same of indentation as -- page.render(output) will make your program work for a single URL.

  2. Even with phantom.exit inside the callback, you'll still have a problem when more than one URL is passed to your program. Again, because page.open is asynchronous, you're effectively calling page.open several times in quick succession (and then exiting before all of those pages have loaded).

    Moreover, like a regular, single, web-browser tab, Phantom's page object can't really open multiple pages at a time.

To fix your program need to ensure that (a) one URL has been loaded and rendered before moving on to the next one, and (b) that you don't call phantom.exit() until all of the URLs have been rendered.

There are a few ways to go about this (google for "nodejs asynchronous for loop" or something like that), but depending upon your needs, it may be easier to just render one page per invocation of the program.

Here's a version of your program, updated for PhantomJS 2 and set up to render exactly one URL:

system = require('system')
page = require('webpage').create()

page.viewportSize =
  width: 1024
  height: 760

url = system.args[2]
output = "screenshot-#{Date.now()}.png"
page.open url, (status) ->
  if status isnt 'success'
    console.error "Error opening url \"#{url}\": #{page.reason}"
    phantom.exit(1)
  else
    console.log "Page opened..."
    window.setTimeout (->
      page.clipRect =
        top: 0
        left: 0
        width: 1024
        height: 760
      page.render(output)
      console.log "Page rendered..."
      phantom.exit()
    ), 200

Since Phantom no longer parses CoffeeScript directly, to run this you'll need to first "compile" to JavaScript, like so:

coffee -c foo.coffee
phantomjs foo.js "http://www.google.com/"

This seems to work properly for me.

Lela Jennings
  • 88
  • 1
  • 8