Node-REDのfeedparserを改造しフローからURLを指定できるようにする
Node-REDを使って遊んでいましたが1か月もするとやった事を忘れてしまいます。やはりメモしておかないと・・・
ノードfeedparserはatomやRSSなどのフィードを定期的に取得し使いやすいjsonオブジェクトにしてくれます。ですがフィードURLは固定の一つだけになってしまうのが不便です。巡回間隔もコントロールできません。そこで、入力端子を追加してフィードURLを受け取るように改造してみます。
Node-REDで自分のノードを作る練習にもなります。
Node-REDはjavascriptのプログラミングツールであり、Raspberry PiのOS Raspbianには最初から入っています。ネットワーク通信を簡単に書くことができます。
関連記事:Google HomeとRaspberry PiのNode-REDで赤外線リモコンのコントロールをする
Node-REDにはフィードを取得するノードnode-red-node-feedparserが組み込まれています。
しかし、フィードURLは初期設定した一つだけ。複数のフィードを取得したい場合はfeedparserをURLの数だけ並べなくてはなりません。URLの追加や変更には一旦flowを停止する必要があります。
お手軽IoTにはとても便利そうなノードなのですが、本格的なフィード取得や管理には対応できません。
ライブラリを探してもフィードURLを入力できるfeedparserノードは無いようです。
Node-REDの中身はNode.jsです。node-red-node-feedparserはNode-RED独自の機能ではなくNode.jsのfeedparserをNode-REDで呼び出しやすくラップしたようなものです。
…と思ったのですが、ゼロから作るのはいろいろしんどい。そんなに難しくないのはわかるのですが仕様の解釈をすり合わせて行くのは気力がいります。
node-red-node-feedparserのソースを見るとfeedparserをタイマーで呼び出す短いコードだったので、これをテンプレートとしてそのまま使って改造する事にしました。
subversionをインストールします。
LICENSEとREADME.mdファイルはプログラムと関係がないので、いじる必要があるファイルは残りの3つです。
32-feedparse.jsはjavascriptファイルですからプログラムの本体です。
32-feedparse.htmlはhtmlファイルです。編集画面の表示やフロー上でのノードの見せ方を設定するようです。
package.jsonはjsonデータファイルです。ノードのバージョン、ソースの場所や依存するライブラリに関する情報を記述しているようです。
今回は"myfeedparser"としてみます。
ダウンロードしたソースディレクトリはそのままにしてmyfeedparserというディレクトリ名でコピーします。
詳細は以後の修正に含めます。
元ファイル(Ver.0.1.12)との差分は次のようになります。
インターバルタイマーのロジックを削除します。インターバルはフローで実現することになります。URLはメッセージmsg.urlの値が読み込まれます。
変更としては少しなのですが、インターバルタイマーの削除でブロックの括弧の位置が微妙に変わりとてもわかりずらいです。変更後のソースを置いておきます。
Node-REDを停止し改造したファイルのあるディレクトリへ移動しリンクします。
入力端子がありますね。msg.urlに値が設定されていればそのURLを使います。
コードの修正する場合はリンクを切断します。Node-REDを停止し作成したノードのディレクトリで
32-feedparse.jsの変更はこれで良いのかまだ自信がありません。基本的には問題ないと思っています。
今回は必要最低限の修正しかしていません。フォークして公開するにはもう少し体裁を整えないと…
javascriptで非同期通信を書くとバグに悩みます。今回のインターバルタイマーの削除も閉じ括弧の位置を間違えて大変悩みました。Node-REDのノード化をすると非同期部分の見通しと切り分けがやりやすくなるはずです。
ノードfeedparserはatomやRSSなどのフィードを定期的に取得し使いやすいjsonオブジェクトにしてくれます。ですがフィードURLは固定の一つだけになってしまうのが不便です。巡回間隔もコントロールできません。そこで、入力端子を追加してフィードURLを受け取るように改造してみます。
Node-REDで自分のノードを作る練習にもなります。
Node-REDはjavascriptのプログラミングツールであり、Raspberry PiのOS Raspbianには最初から入っています。ネットワーク通信を簡単に書くことができます。
関連記事:Google HomeとRaspberry PiのNode-REDで赤外線リモコンのコントロールをする
Node-REDにはフィードを取得するノードnode-red-node-feedparserが組み込まれています。
node-red-node-feedparserパーサーと言う通りフィードの形式に依らずほぼ同じjsonデータとして扱えるようにしてくれます。インターバルタイマー機能もあるため1時間に一回など定期的にフィードを取得する事もできます。IoT的な使い方にはとても便利そうです。
A Node-RED node to get RSS Atom feeds.
しかし、フィードURLは初期設定した一つだけ。複数のフィードを取得したい場合はfeedparserをURLの数だけ並べなくてはなりません。URLの追加や変更には一旦flowを停止する必要があります。
お手軽IoTにはとても便利そうなノードなのですが、本格的なフィード取得や管理には対応できません。
ライブラリを探してもフィードURLを入力できるfeedparserノードは無いようです。
Node-REDの中身はNode.jsです。node-red-node-feedparserはNode-RED独自の機能ではなくNode.jsのfeedparserをNode-REDで呼び出しやすくラップしたようなものです。
feedparserならば自分で使いやすいノードを作ってしまえば良いのだ!
Feedparser - Robust RSS, Atom, and RDF feed parsing in Node.js
…と思ったのですが、ゼロから作るのはいろいろしんどい。そんなに難しくないのはわかるのですが仕様の解釈をすり合わせて行くのは気力がいります。
node-red-node-feedparserのソースを見るとfeedparserをタイマーで呼び出す短いコードだったので、これをテンプレートとしてそのまま使って改造する事にしました。
node-red-node-feedparser ソースコードのコピー
早速 node-red-node-feedparser のソースコードを取得します。ソースはGitHubのnode-red-nodesの中にあります。node-red-nodes/social/feedparser/node-red-nodes全体は大きすぎるのでfeedparserディレクトリだけ取得したいです。ですがgitではレポジトリのサブディレクトリだけの取得はできないようです。subversionの機能を使うとサブディレクトリだけ取得できるそうです。
subversionをインストールします。
sudo apt install subversionソースコードはホームディレクトリ下のDocumentsフォルダにコピーする事にします。
cd ~/Documents svn export https://github.com/node-red/node-red-nodes/trunk/social/feedparserこれでfeedparserディレクトリ以下にソースコードのコピーができました。
ソースコードを改造する
feedparserディレクトリの中身を見てみます。一つのディレクトリと5つのファイルがあります。- localesディレクトリ
- 32-feedparse.html
- 32-feedparse.js
- package.json
- LICENSE
- README.md
LICENSEとREADME.mdファイルはプログラムと関係がないので、いじる必要があるファイルは残りの3つです。
32-feedparse.jsはjavascriptファイルですからプログラムの本体です。
32-feedparse.htmlはhtmlファイルです。編集画面の表示やフロー上でのノードの見せ方を設定するようです。
package.jsonはjsonデータファイルです。ノードのバージョン、ソースの場所や依存するライブラリに関する情報を記述しているようです。
名前を変える
最初にノードの名前を変えます。feedparserのソースコードをコピーした状態なので、既にあるfeedparserと区別できるようにしなくてはなりません。今回は"myfeedparser"としてみます。
ダウンロードしたソースディレクトリはそのままにしてmyfeedparserというディレクトリ名でコピーします。
cd ~/Documents cp -r feedparser myfeedparserコピーしたディレクトリ内のファイルでノード名に相当する所を"myfeedparser"に変えていきます。
詳細は以後の修正に含めます。
ファイルの修正
package.json
package.jsonを編集します。名称、バージョン番号やノード名を変更します。名前は"node-red-contrib"で始まるようにするようです。元ファイル(Ver.0.1.12)との差分は次のようになります。
--- ./feedparser/package.json 2018-03-30 22:50:51.000000000 +0900 +++ ./myfeedparser/package.json 2018-04-09 13:09:35.025390745 +0900 @@ -1,6 +1,6 @@ { - "name": "node-red-node-feedparser", - "version": "0.1.12", + "name": "node-red-contrib-myfeedparser", + "version": "0.0.1", "description": "A Node-RED node to get RSS Atom feeds.", "dependencies": { "feedparser": "^2.2.9", @@ -8,7 +8,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/node-red/node-red-nodes/tree/master/social/feedparser" + "url": "" }, "license": "Apache-2.0", "keywords": [ @@ -18,7 +18,7 @@ ], "node-red": { "nodes": { - "feedparse": "32-feedparse.js" + "myfeedparse": "32-feedparse.js" } } }
32-feedparse.html
ノード名の変更、インターバルの入力ボックスの削除、ノード入力の追加などを行います。元ファイル(Ver.0.1.12)との差分は次のようになります。--- ./feedparser/32-feedparse.html 2018-03-30 22:50:51.000000000 +0900 +++ ./myfeedparser/32-feedparse.html 2018-04-09 14:49:49.466857267 +0900 @@ -1,20 +1,16 @@ -<script type="text/x-red" data-template-name="feedparse"> +<script type="text/x-red" data-template-name="myfeedparse"> <div class="form-row"> - <label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="feedparse.label.feedurl"></span></label> + <label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="myfeedparse.label.feedurl"></span></label> <input type="text" id="node-input-url"> </div> <div class="form-row"> - <label for="node-input-interval"><i class="fa fa-repeat"></i> <span data-i18n="feedparse.label.refresh"></span></label> - <input type="text" id="node-input-interval" style="width:60px"> <span data-i18n="feedparse.label.minutes"></span> - </div> - <div class="form-row"> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label> <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name"> </div> </script> -<script type="text/x-red" data-help-name="feedparse"> +<script type="text/x-red" data-help-name="myfeedparse"> <p>Monitors an RSS/atom feed for new entries.</p> <p>The <code>msg.topic</code> contains the original article link. The <code>msg.payload</code> contains the description, and <code>msg.article</code> contains the complete article object, @@ -23,19 +19,18 @@ </script> <script type="text/javascript"> - RED.nodes.registerType('feedparse',{ + RED.nodes.registerType('myfeedparse',{ category: 'advanced-input', color:"#C0DEED", defaults: { name: {value:""}, - url: {value:"", required:true}, - interval: { value:15, required:true, validate:function(v) {return (!isNaN(parseInt(v)) && (parseInt(v) <= 35790))} } + url: {value:""}, }, - inputs:0, + inputs:1, outputs:1, icon: "feed.png", label: function() { - return this.name||this.url||this._("feedparse.feedparse"); + return this.name||this.url||this._("myfeedparse.myfeedparse"); }, labelStyle: function() { return this.name?"node_label_italic":"";
localesファイル
localesディレクトリ下のen-USおよびjaディレクトリにあるファイルを修正します。それぞれテキストエディタで開いて、"feedparser"と書かれた部分を"myfeedpaser”と変更します。32-feedparse.js
入力からのURLを受け取るようにします。インターバルタイマーのロジックを削除します。インターバルはフローで実現することになります。URLはメッセージmsg.urlの値が読み込まれます。
変更としては少しなのですが、インターバルタイマーの削除でブロックの括弧の位置が微妙に変わりとてもわかりずらいです。変更後のソースを置いておきます。
module.exports = function (RED) { "use strict"; var FeedParser = require("feedparser"); var request = require("request"); var url = require('url'); function MyFeedParseNode(n) { RED.nodes.createNode(this, n); this.url = n.url; var node = this; this.on("input", function (msg) { if (msg.url) { this.url = msg.url; } //input URL this.seen = {}; var parsedUrl = url.parse(this.url); if (!(parsedUrl.host || (parsedUrl.hostname && parsedUrl.port)) && !parsedUrl.isUnix) { this.error(RED._("feedparse.errors.invalidurl")); } else { var getFeed = function () { var req = request(node.url, { timeout: 10000, pool: false }); //req.setMaxListeners(50); req.setHeader('user-agent', 'Mozilla/5.0 (Node-RED)'); req.setHeader('accept', 'text/html,application/xhtml+xml'); var feedparser = new FeedParser(); req.on('error', function (err) { node.error(err + ':' + node.url); req.abort(); }); req.on('response', function (res) { if (res.statusCode != 200) { node.warn(RED._("feedparse.errors.badstatuscode") + " " + res.statusCode); } else { res.pipe(feedparser); } }); feedparser.on('error', function (error) { node.error(error); }); feedparser.on('readable', function () { var stream = this, article; while (article = stream.read()) { // jshint ignore:line if (!(article.guid in node.seen) || (node.seen[article.guid] !== 0 && node.seen[article.guid] != article.date.getTime())) { node.seen[article.guid] = article.date ? article.date.getTime() : 0; //var msg = { // topic: article.origlink || article.link, // payload: article.description, // article: article //}; msg.topic = article.origlink || article.link; msg.payload = article.description; msg.article = article; node.send(msg); } } }); feedparser.on('meta', function (meta) { }); feedparser.on('end', function () { req.abort(); }); }; getFeed(); } }); this.on("close", function () { }); } RED.nodes.registerType("myfeedparse", MyFeedParseNode); }【2018/04/14 修正: msgが初期化されflowのデータを引き継がなかったのを修正しました】
ノードモジュールのテスト
改造したノードモジュールを動かしてみます。公開できる完成度ではないので改造したファイルがあるディレクトリをリンクして動くようにします。Node-REDを停止し改造したファイルのあるディレクトリへ移動しリンクします。
node-red-stop cd ~/Documents/myfeedparser sudo npm linkエラーなくリンクができたならNode-REDを動かします。
node-red-start正しくモジュールを認識するとmyfeedparseが表示されるでしょう。
入力端子がありますね。msg.urlに値が設定されていればそのURLを使います。
コードの修正する場合はリンクを切断します。Node-REDを停止し作成したノードのディレクトリで
sudo npm unlinkとするとリンクが解除されます。修正後に再びリンクします。
まとめ
Node-REDのfeedparserを改造してフロー入力のメッセージからフィードURLを指定できるようにしました。既存のノードを改造する事でNode-REDで独自のノードを作るときの勉強になりました。32-feedparse.jsの変更はこれで良いのかまだ自信がありません。基本的には問題ないと思っています。
今回は必要最低限の修正しかしていません。フォークして公開するにはもう少し体裁を整えないと…
javascriptで非同期通信を書くとバグに悩みます。今回のインターバルタイマーの削除も閉じ括弧の位置を間違えて大変悩みました。Node-REDのノード化をすると非同期部分の見通しと切り分けがやりやすくなるはずです。
コメント
コメントを投稿