Help us understand the problem. What is going on with this article?

Node.jsでWordPressのRSSを(なるべく)リアルタイムで持ってくるお話

More than 1 year has passed since last update.

プロ生ちゃん Advent Calendar 2018 21日目の記事、担当は音色ねいろです。

本来はもっとプロ生ちゃん色の強い何かを作ろうとしましたが、どうもこうもアイデアも出ず記事を書く時間も見つからずで、結果的にこの記事になりました。来年のネタを早めに温めておきたい。
今回の記事では、Node.jsを使用してWordPressのFeedをなるべくリアルタイムに持ってくる内容となっています。申し訳程度にプログラミング生放送のフィードURI (https://pronama.jp/feed)を例に取っています。

きっかけ

要件(今回やること)

  • Node.jsで(なるべくリアルタイムに)更新を検知する
    • この際相手のサーバに過大な負荷をかけることは許されないため、最小でも1分間隔で取る
  • モジュール化する
    • 依存しないように取得部分と表示部分とをEventEmitterで分ける。

今回使用したライブラリ

  • data-utils
  • iconv-lite
  • xml2js
  • sync-request

実装

wpFeed.js
const EventEmitter = require('events').EventEmitter /* 1 */

require('date-utils')
const iconv = require('iconv-lite')
const xml2js = require('xml2js')
const request = require('sync-request')

let tempPostID = 0
let startupTime = '0'

module.exports = class wpFeed extends EventEmitter { /* 1 */

  constructor() {
    super()

    startupTime = new Date() /* 2 */
  }

  start() {
    const self = this

    setInterval(() => self._wordpress(), 60 * 1000) /* 3 */
  }

  _wordpress() {
    const res = request(
      'GET',
      'http://pronama.jp/feed'
    ) /* 4 */
    const buf = Buffer.from(res.getBody())
    const feedXml = iconv.decode(buf, 'UTF8') 

    xml2js.parseString(feedXml, (err, result) => { /* 5 */
      if(err !== null) {
        console.error(err)
      }

      const postData = result.rss.channel[0].item[0]
      const postID = postData['post-id'][0]._
      const pubDate = postData.pubDate
      const pubDateObject = new Date(pubDate)

      if(pubDateObject.getTime() < startupTime.getTime()) return /* 2 */
      if(postID === this.tempPostID) return /* 6 */

      this.emit('post', postData) /* 7 */
      this.tempPostID = postID /* 6 */

    })
  }
}
app.js
const wpFeed = require('./wpFeed')
const wf = new wpFeed()

wf.on('start', () => {})
wf.on('post', data => {
  console.log(`記事が更新されました!\n「${data.title[0]}」`)
})

wf.start()

※実際に使用する際は、各処理を別ファイルに分離することをおすすめします。

1 : EventEmitterを継承する

今回の記事のキモです。
イベントモジュールのEventEmitterオブジェクトを継承しています。

2 : 取得開始前の記事をスキップする

これをしないと取得開始する度に最新の記事が取得されてしまい非常に厄介です。
そのため、コンストラクタ時にstartupTimeを格納し、記事取得時の時間と比較することによって取得開始後の記事のみをエミットすることができます。

3 : setIntervalで繰り返す

下に定義したwpFeedを120秒間隔で実行します。
もっとまともな書き方があれば教えていただきたいです…!

4 : フィード情報を取得してくる

現在sync-requestが非推奨になっていますが、同期的に処理したいためこれを利用しています。
まともな書き方があれば(以下略)

5 : XMLからJSONにコンバートする

XMLだと非常に扱いにくいので、JavaScriptのために生まれてきたJSONにコンバートします。
XMLに誤りがあると変換がうまくいかない場合があるので、必ずエラー処理を噛ませておきます。

6 : 重複スキップ

直前に取得した記事IDと現在処理中の記事IDとを比較します。
比較した際、直前のIDと一致した場合は早期リターンを用います。
ただ、内容が変更された場合は感知できないようなので比較対象を内容にするべきですね。

7 : エミット

更新された記事をエミットします。
こうすると、app.js側のwpFeed.on('post', postData => {})にデータが飛び、自由自在にデータを利用することができます。

まとめ

去年の今頃からNode.js書き始めましたが、「こいつ動くんか?」といったコードも(それなりに綺麗なコードで)すんなり動いてくれてとても好きです。

今回、EventEmitterを利用しましたが、本当に様々なことに使えるので大好きです。(ep8もデータの取得にEventEmitterを使用しています)

今後やりたいこと

  • 更新されたらLEDがチカチカするIoTしたい
  • クラス生成時にURL指定、更新間隔を変更できるようにしたい
  • npmで配布したい
  • TypeScriptで書いてみたい

最後に

本当に昨日(20日)まで何を書こうか全く決まらなくて、結果今年学習した内容の記事になりました。
来年こそはもっとプロ生ちゃんにコミットしたいです……!
ここまでご覧頂きまして、本当にありがとうございました。

プロ生ちゃん Advent Calendar 2018 22日担当は120byteさんです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした