从Node.JS中的两个网站请求RSS提要

时间:2013-11-24 17:01:46

标签: javascript node.js rss request

我有一台Node.JS服务器,它请求来自两个Web服务器的数据:bbc.co.uksky.com。然后解析RSS提要,用户看到两个列表:来自BBC和天空。

这是代码。

var feed = require('feed-read');
var http = require('http');
var async = require('async');
var request = require('request');

var LIMIT = 10;
var UNABLE_TO_CONNECT = "Unable to connect.";
var BBC_URL = 'http://feeds.bbci.co.uk/news/rss.xml';
var SKY_URL = 'http://news.sky.com/feeds/rss/home.xml';

var server = http.createServer(onRequest);
server.listen(9000);

function onRequest(req, res) {
    res.writeHead(200, {
        'Content-Type' : 'text/html; charset=utf-8'
    });

    async.parallel([ function(callback) {
        feed(BBC_URL, onRssFetched);
        // TODO: where to call callback()?
    }, function(callback) {
        feed(SKY_URL, onRssFetched);
        // TODO: where to call callback()?
    } ], function done(err, results) {
        console.log("Done");
        if (err) {
            throw err;
        }
    });
}

function onRssFetched(err, articles) {
    console.log("RSS fetched");
    var html = [];
    if (err) {
        html.push("<p>", UNABLE_TO_CONNECT = "</p>");
    } else {
        html.push("<ol>");
        var i = 0;
        articles.forEach(function(entry) {
            if (i == LIMIT) {
                return;
            }
            html.push("<li><a href='" + entry.link + "'>" + entry.title
                    + "</a></li>");
            i++;
        });
    }
    console.log(html.join(""));
}

现在我不知道如何将结果添加到网页中。如果我在调用callback()方法后立即致电feedcallback()将会执行,而不会等到feed完成其工作。另一方面,我无法将callback传递给feed。也许这种方法是错误的,我还需要一些其他模块来进行RSS解析。

4 个答案:

答案 0 :(得分:12)

@Maksim我知道你的原始问题包括 async 模块,但提出了另一种选择:

为什么不 每篇文章到客户端,而不是等待所有 RSS提要在发送回复之前返回...?

使用 async.parallel 告诉节点:

  

等到我们收到回复 所有 这些新闻服务
  只有
然后 (将文章组合成) 对客户的回应 ...“

当您等待所有响应(来自RSS新闻服务)时,这会占用每个连接客户端的内存... 浪费

所以我没有诉诸 async 就写了我的答案。
并且,而不是等待年龄(而 async 将所有Feed合并为一个), 一旦 第一个 rss Feed返回,客户就会看到新闻!

var feed = require('feed-read'),  // require the feed-read module
    http = require("http"),
    urls = [
        "http://feeds.bbci.co.uk/news/rss.xml",
        "http://news.sky.com/feeds/rss/home.xml",
        "http://www.techmeme.com/feed.xml"
    ]; // Example RSS Feeds

http.createServer(function (req, res) { 
    // send basic http headers to client
    res.writeHead(200, {
        "Content-Type": "text/html",
        "Transfer-Encoding": "chunked"
    });

    // setup simple html page:
    res.write("<html>\n<head>\n<title>RSS Feeds</title>\n</head>\n<body>");

    // loop through our list of RSS feed urls
    for (var j = 0; j < urls.length; j++) {

        // fetch rss feed for the url:
        feed(urls[j], function(err, articles) {

            // loop through the list of articles returned
            for (var i = 0; i < articles.length; i++) {

                // stream article title (and what ever else you want) to client
                res.write("<h3>"+articles[i].title +"</h3>"); 

                // check we have reached the end of our list of articles & urls
                if( i === articles.length-1 && j === urls.length-1) {
                    res.end("</body>\n</html>"); // end http response
                } // else still have rss urls to check
            } //  end inner for loop
        }); // end call to feed (feed-read) method
    } // end urls for loop
}).listen(9000);

主要优势

  • 与您的应用相关联的人会看到新闻/结果更快(几乎立即!)
  • 您的应用使用 更少内存
  • 添加新的RSS新闻源时,您无需编辑/更新任何代码
  

有关此解决方案的更多细节/说明
  见:https://github.com/nelsonic/node-parse-rss

答案 1 :(得分:1)

不,你不需要另一个图书馆。但您需要做的是将callback移交给feed函数而不是onRssFetched。这样,使用async.parallel变量将单个RSS Feed移交给result调用中的最终回调。

在这个变量中,您可以同时访问这两个RSS源,并且可以使用它们执行任何操作。

所以,基本上你的逻辑需要是:

async.parallel({
  bbc: function (callback) {
    feed(BBC_URL, callback);
  },
  sky: function (callback) {
    feed(SKY_URL, callback);
  }
}, function (err, result) {
  if (err) {
    // Somewhere, something went wrong…
  }

  var rssBbc = result.bbc,
      rssSky = result.sky;

  // Merge the two feeds or deliver them to the client or do
  // whatever you want to do with them.
});

就是这样: - )。

答案 2 :(得分:0)

为了放大@nelsonic的答案(足够让我觉得这保证了它自己的答案),feed-parse已经异步处理了。从本质上讲,它仍然在http.request上运行。如果你查看代码,你会看到你甚至可以直接传递一个URL数组,它会遍历它们,但是它使用了更多的“async.eachSeries”方法,其中下一个调用只发生在前一个调用之后完成,看起来不是你想要的。

如果您真的想在处理之前等待调用先完成,那么最好异步缓冲数据,然后在所有URL完成后使用下划线_.after()运行。

但很可能,你真正想要做的事情(除非你只是想找一个尝试异步的例子)是@ nelsonic的答案。

答案 3 :(得分:0)

理想情况下,我会流式传输rss数据,而不是在内存中聚合。 @nelsonic解释了解决这个问题的正确方法。

但是,如果我们要让您的代码运行,请考虑以下代码:

var util = require('util');
var http = require('http');
var async = require('async');
var feed = require('feed-read');
var request = require('request');

var LIMIT = 10;
var UNABLE_TO_CONNECT = 'Unable to connect.';
var BBC_URL = 'http://feeds.bbci.co.uk/news/rss.xml';
var SKY_URL = 'http://news.sky.com/feeds/rss/home.xml';

var server = http.createServer(onRequest);
server.listen(9000);

function onRequest(req, res) {
  util.log('Request recieved!');
  res.writeHead(200, {
    'Content-Type': 'text/html; charset=utf-8'
  });

  async.parallel({
    bbc: function (callback) {
      feed(BBC_URL, function (err, articles) {
        var html = onRssFetched(err, articles);
        callback(err, html);
      });
    },
    sky: function (callback) {
      feed(SKY_URL, function (err, articles) {
        var html = onRssFetched(err, articles);
        callback(err, html);
      });
    }
  }, done);

  function done(err, results) {
    util.log('Received results: ' + Object.keys(results).join(','));
    if (!err && results) {
      var entry, html;
      for (entry in results) {
        html = results[entry];
        res.write(html.join(''));
      }
      util.log('Send complete!');
      res.end();
    } else {
      console.log(err || 'no data in results');
      res.end('Unable to process your request');
    }
  }
}

function onRssFetched(err, articles) {
  // limit number of articles;
  articles = articles.slice(0, LIMIT);

  var html = [];
  if (err) {
    html.push('<p>', UNABLE_TO_CONNECT = '</p>');
  } else {
    html.push('<ol>');
    articles.forEach(function (entry) {
      html.push('<li><a href="' + entry.link + '">' + entry.title + '</a></li>');
    });

    html.push('</ol>');
  }
  return html;
}

// -- Test Code ---------------------------------------------------------
if (require.main === module) {
  (function () {
    var req, res = {
        writeHead: console.log,
        write: console.log,
        end: console.log
      };
    // onRequest(req, res);
  })();
}

如果您遇到任何问题,请告诉我。