Expressで複数サイトのRSSを取得するプログラムを作る

node.jsのExpressで複数サイトのRSSを取得するプログラムを作ってみる。今回は複数サイトのRSSを非同期で読み込み、全てのRSSの取得が完了したときに画面に出力するという仕様で作成する。

express-generatorでプログラムの雛形を作る

まずはexpress-generatorをインストールし、Expressを使用したプログラムの雛形を作る。

npmでexpress-generatorをグローバルインストールする。

npm install express-generator -g

プログラムの雛形を作る。今回作成するプログラムのコードではテンプレートエンジンは使用しないので、テンプレートエンジンは何を選択しても良い。今回は適当にejsを選択する。

express --view=ejs myapp

依存関係のモジュールをインストールする。

cd myapp
npm install

expressを起動する。

DEBUG=myapp:* npm start

ブラウザからhttp://localhost:3000/にアクセスし、「Welcome to Express」と表示されることを確認する。

表示が確認できたらCtrl + cでExpressを停止させる。

pm2でExpressをデーモン化する

pm2を使ってExpressをデーモン化する。デーモン化するメリットはたくさんあるが、最も恩恵を感じるのは、プログラムのコードを修正した場合にpm2がファイルの変更を検知して自動でexpressを再起動してくれることである。

npmからpm2をグローバルインストールする。

npm install pm2 -g

pm2でexpressを起動する。--watchを付けることでファイルの変更を監視し、変更があった場合はpm2が自動でexpressを再起動する。

pm2 start bin/www --watch

ログを表示してページの状態が分かるようにする。

pm2 log

RSSを取得してパースする

RSSの取得とパースにrss-parserモジュールが必要なのでインストールする。

npm install rss-parser --save

routes/index.jsに処理を記述する。全体としては以下のようなコードになる。このコードは動作確認済み。routes/index.jsにコピペして実行すると、reddit.comとwired.comのRSSの記事タイトルと記事URLを取得して画面に出力する。

var express = require('express');
var router = express.Router();

//モジュールの読み込み
var rssParser = require('rss-parser');

/* GET home page. */
router.get('/', function(req, res, next) {

  //reddit.comとwired.comのRSS
  var urls = [
    "https://www.reddit.com/.rss",
    "https://www.wired.com/feed/rss"
  ];

  Promise.all(urls.map(function(url) {
    return new Promise(function(resolve, reject){
      var parser = new rssParser();
      var feed = parser.parseURL(url);
      //処理の終わり(解決)を通知する
      resolve(feed);
    });
  })).then(function(feeds) {
  
    //取得したRSSデータの整形
    var result = "";
    feeds.forEach(function(feed) {
       feed.items.forEach(function(item) {
         result += item.title + ':' + item.link + ' ';
       });
    });
    
    //画面に出力する
    res.send(result);
  });

});

module.exports = router;

このコードを理解するためのポイントは以下の3点である。

  • 全てのサイトのRSSの取得が完了した段階で画面に出力しなければいけないので非同期処理を書く必要がある。そのため、Promise.all()を使用する必要がある。
  • Promise.all()に与える引数はPromiseインスタンスを格納した配列でなければならない。従って、変数urlsにmapを使用してurlsの要素数分のPromiseインスタンスをPromise.all()に渡す。
  • Promise.all()は与えられた全てのPromiseインスタンスがresolve()を実行したタイミングで同期が完了したとみなし、.then(function(){});を実行する。

参考
Express - Node.js Web アプリケーション・フレームワーク
Express のアプリケーション生成プログラム