7

I built a simple node script using nightmare.js to scrape websites

var Nightmare = require('nightmare');
var vo = require('vo');

vo(run)(function(err, result) {
    if (err) throw err;
});

function *run() {
    var x = Date.now();
    var nightmare = Nightmare();
    var html = yield nightmare
    .goto('http://google.com')
    .evaluate(function() {
        return document.getElementsByTagName('html')[0].innerHTML;
    });

    console.log("done in " + (Date.now()-x) + "ms");
    console.log("result", html);

    yield nightmare.end();
}

I want to run this in an environment using an older version of node, which does not support ES6 features. There are no examples on the github page on how to do this without the "yield" keyword.

I did find an example of usage without the ES6 syntax here : Webscraping with nightmare

I wrote it like this :

var night = new Nightmare()
.goto('http://www.google.com')
.evaluate(function () {
  return document.getElementsByTagName('html')[0].innerHTML;
},function (html) {
   console.log("result", html);
  }
)
.run(function (err, nightmare) {
  if (err) return console.log(err);
  console.log('Done!');
});

It does not crash, but the result logging function is never called.

With the yield syntax, getting the returned value from "evaluate" is pretty straightforward, but without it, I did not find any way to do it.

UPDATE Wrote this thanks to the accepted answer and its comments. It uses 'Q' and works in node versions previous to 0.12:

var Nightmare = require('nightmare');

var Promise = require('q').Promise;

var x = Date.now();
var nightmare = Nightmare();
Promise.resolve(nightmare
  .goto('http://google.com')
  .evaluate(function() {
      return document.getElementsByTagName('html')[0].innerHTML;
})).then(function(html) {
    console.log("done in " + (Date.now()-x) + "ms");
    console.log("result", html);
    return nightmare.end();
}).then(function(result) {

}, function(err) {
   console.error(err); // notice that `throw`ing in here doesn't work
});
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Rayjax
  • 7,494
  • 11
  • 56
  • 82
  • 1
    Using [babel-node](https://babeljs.io/docs/usage/cli/#babel-node) might be a simpler solution, depending on your use case. It's basically a wrapper around that transpile the ES6 code into ES5 before running it. – Maël Nison Sep 16 '15 at 09:40

1 Answers1

12

The docs are horrible, but it seems that Nightmare is based on thenables. I didn't find much information on the callback interface either, but that would lead to an indentation pyramid anyway.

So your best bet is to use promises, just choose any library that roughly follows the ES6 standard (they all are usable in non-ES6 environments as well).

You can easily transform your linear generator code into a promise chain, just replace every yield by a then call:

var Nightmare = require('nightmare');
var Promise = require('…');

var x = Date.now();
var nightmare = Nightmare();
Promise.resolve(nightmare
  .goto('http://google.com')
  .evaluate(function() {
      return document.getElementsByTagName('html')[0].innerHTML;
})).then(function(html) {
    console.log("done in " + (Date.now()-x) + "ms");
    console.log("result", html);
    return nightmare.end();
}).then(function(result) {
    …
}, function(err) {
   console.error(err); // notice that `throw`ing in here doesn't work
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Which library of the list did you use in this precise example ? I tried with "q" without success – Rayjax Sep 16 '15 at 09:04
  • Ah, `Q` has legacy calling conventions (it was one of the earliest libraries). You'd need to do `Q.Promise` instead of `Promise` and `Q` instead of `Promise.resolve`. – Bergi Sep 16 '15 at 09:10