在承诺中包装噩梦/一次处理一个URL

时间:2016-02-15 21:40:57

标签: node.js promise nightmare

我正在尝试使用nightmarejs来抓取一些网站的信息。我遇到的问题是,我只想一次打开一个窗口并等待它关闭,然后再处理下一个URL。

// index.js

var urls = // an array of urls.

var l = 10; // urls.length;
while (l--) {
  // g.findById(id).then()....
  // y.findById(id).then()....

  UrlProcessing.findById(id).then(function(results) {
    console.log(results);
  });
}

现在是findByid:

//UrlProcessing.js

class UrlProcessing {


  findById(id) {
    var address = id;

    return new Promise(function (resolve, reject) {
      vo(function*(address) {
        var nightmare = new Nightmare({show: true});
        var link = yield nightmare
            .goto(address)
            .wait(2000)
            .evaluate(function() {
            return document.getElementsByTagName('html')[0].innerHTML;
            });
        yield nightmare.end();
        return yield link;

      })(address, function(err, res) {
        if(err) reject(err);

        resolve(res);
      });
    });
  }
  module.exports = UrlProcessing;
}

有关如何实现这一目标的任何建议?我想在while循环中执行每个findById

2 个答案:

答案 0 :(得分:3)

在不修改findById的情况下,您可以使用reduce模拟类似行为或类似瀑布的行为:

var urls = ['http://www.yahoo.com', 'http://example.com', 'http://w3c.org'];
urls.reduce(function(accumulator, url) {
  return accumulator.then(function(results) {
    return findById(url)
      .then(function(result) {
        results.push(result);
        return results;
      });
  });
}, Promise.resolve([])).then(function(results){
  //do what you need to do with the results
});

为了完整起见,并且因为我必须进行一些修饰,findById方法进行了我的(轻微)修改:

function findById(address) {
  return new Promise(function(resolve, reject) {
    vo(function * (address) {
      var nightmare = new Nightmare({
        show: true
      });
      var link = yield nightmare
        .goto(address)
        .wait(2000)
        .evaluate(function() {
          return document.getElementsByTagName('html')[0].innerHTML;
        });
      yield nightmare.end();
      return link;
    })(address, function(err, res) {
      if (err) reject(err);
      resolve(res);
    });
  });
}

...所有这一切,我不确定这种方法是否最好。为什么在单独的梦魇实例中一次只想要一个?我意识到这并不完全适合你的原始实现,但这可能是你想要考虑的事情 - 你可以改变findById来接受一个数组而不是一个URL,并且(可选)使用相同的Nightmare实例。致电findById

var urls = ['http://www.yahoo.com', 'http://example.com', 'http://w3c.org'];
findById(urls)
 .then(function(results) {
    //do what you need to do with the results
  });

...和findById本身:

function findById(addresses) {
  return new Promise(function(resolve, reject) {
    vo(function * (addresses) {
      var nightmare = new Nightmare({
        show: true
      });
      var results = [];
      for (var i = 0; i < addresses.length; i++) {
        results.push(yield nightmare
          .goto(addresses[i])
          .wait(2000)
          .evaluate(function() {
            return document.getElementsByTagName('html')[0].innerHTML;
          }));
      }
      yield nightmare.end();
      return results;

    })(addresses, function(err, res) {
      if (err) reject(err);
      resolve(res);
    });
  });
}

当然,如果您仍然希望每次都有新的Nightmare实例,则可以在.end()循环内移动构造函数调用和for调用。

答案 1 :(得分:1)

你所做的一切几乎都是正确的,现在你需要做的就是顺序化承诺,即链接它们,你可以看看this answer

只需更改代码即可使用reduce

// index.js

urls.reduce( function(promise, url){
  return promise.then(function(){
    return url.findById(id);
  }).then(function(results){
    console.log(results);
  });
}, Promise.resolve())
  .then(function(){
    console.log('All done');
  });

在更简洁的ES6表格中,它将是:

urls.reduce( (p, url) => p.then(() => url.findById(id)).then(r => console.log(r)), Promise.resolve())
  .then(() => console.log('All done') );