Node-REDでWordpressからHexoへMigration

Posted by joeartsea on 2017-05-06

WordpressからHexoへ移行

このブログもとうとう昨年1年間放置してしまっていました。しれっとフェードアウトしようかと思ってたんですがGoogle Analyticsとか見てみると案外過去のエントリが世の役に立っているようなので、もうしばらくメンテナンスしてみます。

ずっとWordpressでやってきましたが随分前から静的ジェネレータ系に移行したいと思ってましたので今回Hexoに移行しました。Markdownでエントリ書いてGithub pagesへDeployすればOKという手軽さは"自分1人のブログ"で使うツールとして最適です。

ちなみにWordpressはHeroku+PostgreSqlでやってきましたがHexoはGithub pagesでやっていきます。

hexo-migrator-wordpressが止まる

ブログ移行の何が大変かって過去のエントリの移行ですが今回もハマりました。hexo-migrator-wordpressという移行ツール使ったんですが、何やら JS-YAML: can not read a block mapping entry; a multiline key may not be an implicit key というようなエラーで途中で止まります。

scaffoldspost.md とかのテンプレートの上部にあるYAMLが何かおかしいらしく、言われた通りに変更すると症状は変わるけど結局YAML部分全削除になって、動いたとしても全エントリのタイトルさえ取り込めない状態で意味が無いので諦めました。

Node-REDで移行Flowを作ってみた

要はWordpressからエクスポートしたXMLをParseして、YAMLの部分とMarkdown化した本文をマージすれば良いので、こういう「やればできるけどモチベーションが上がらない作業」こそNode-REDが向いてる(個人的見解)ということで以下のFlowを作ってみました。

1
[{"id":"e218d167.2e763","type":"file in","z":"387c7d80.f1bbc2","name":"","filename":"/Path/to/wordpress_export_file","format":"utf8","x":356.5,"y":116,"wires":[["18994be6.f85814"]]},{"id":"e70bdfb8.c911f","type":"inject","z":"387c7d80.f1bbc2","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":114.5,"y":32,"wires":[["e218d167.2e763"]]},{"id":"18994be6.f85814","type":"xml","z":"387c7d80.f1bbc2","name":"","attr":"","chr":"","x":132.5,"y":182,"wires":[["5c3ebf52.fcbf"]]},{"id":"5c3ebf52.fcbf","type":"change","z":"387c7d80.f1bbc2","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.rss.channel.0.item","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":291.5,"y":182,"wires":[["a0ccebc1.702dd8"]]},{"id":"e1705f3c.bbb2f","type":"template","z":"387c7d80.f1bbc2","name":"","field":"head","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"---\ntitle: {{payload.title}}\nsubtitle:\ndate: {{payload.wp:post_date.0}}\ncatalog: true\nheader-img:\ntags:\n---","x":461.5,"y":257,"wires":[["50f760b.254e1a"]]},{"id":"a0ccebc1.702dd8","type":"split","z":"387c7d80.f1bbc2","name":"","splt":"\\n","x":468.5,"y":181,"wires":[["f0e69020.e56e"]]},{"id":"50f760b.254e1a","type":"template","z":"387c7d80.f1bbc2","name":"","field":"filename","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"/Path/to/{{yyyy}}/{{mmdd}}.md","x":126.5,"y":315,"wires":[["d53310c7.3e3e2"]]},{"id":"f0e69020.e56e","type":"change","z":"387c7d80.f1bbc2","name":"","rules":[{"t":"set","p":"post_id","pt":"msg","to":"payload.wp:post_id","tot":"msg"},{"t":"set","p":"post_date","pt":"msg","to":"payload.wp:post_date.0","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":151.5,"y":262,"wires":[["13950893.0b2987"]]},{"id":"e7932009.89ebd","type":"file","z":"387c7d80.f1bbc2","name":"","filename":"","appendNewline":true,"createDir":false,"overwriteFile":"false","x":409.5,"y":378,"wires":[]},{"id":"d53310c7.3e3e2","type":"file in","z":"387c7d80.f1bbc2","name":"","filename":"","format":"utf8","x":276,"y":314,"wires":[["1538d793.74b6d8"]]},{"id":"23e71877.4c4258","type":"debug","z":"387c7d80.f1bbc2","name":"","active":true,"console":"true","complete":"post_id","x":351.5,"y":460,"wires":[]},{"id":"13950893.0b2987","type":"function","z":"387c7d80.f1bbc2","name":"","func":"msg.yyyy = msg.post_date.substring(0, 4);\nmsg.mmdd = msg.post_date.substring(5, 10);\nreturn msg;","outputs":1,"noerr":0,"x":306.5,"y":261,"wires":[["e1705f3c.bbb2f"]]},{"id":"31337f7c.a4b51","type":"template","z":"387c7d80.f1bbc2","name":"","field":"filename","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"/Path/to/{{post_id}}.md","x":258,"y":379,"wires":[["e7932009.89ebd"]]},{"id":"fd978b3f.1879c8","type":"template","z":"387c7d80.f1bbc2","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{head}}\n{{payload}}","x":122,"y":379,"wires":[["31337f7c.a4b51"]]},{"id":"1538d793.74b6d8","type":"function","z":"387c7d80.f1bbc2","name":"","func":"msg.payload = msg.payload.split('\\n');\ndelete msg.payload[0];\nmsg.payload = msg.payload.join('\\n');\nreturn msg;","outputs":1,"noerr":0,"x":415,"y":311,"wires":[["fd978b3f.1879c8"]]},{"id":"6ecf7921.eb47f8","type":"catch","z":"387c7d80.f1bbc2","name":"","scope":null,"x":134.5,"y":462,"wires":[["23e71877.4c4258"]]}]

これ最初は楽勝だと思ってたんですが以下の想定外により思ったより複雑になってしまいました。

node-red-contrib-markdownの想定外

node-red-contrib-markdownで本文をHTMLからMarkdownへ変換しようと思ってたんですが、このノードはMarkdownをHTMLへ変換することしか対応してないようです。がーん。

wpxml2mdの想定外

node-red-contrib-markdownの代替としてwpxml2mdというWordpressのエクスポートXMLからMarkdown化されたファイルを生成してくれるツールを使ってみました。ただ、このツールの仕様上、生成されるファイルは以下のようなディレクトリ構造でアウトプットされます。

1
2
3
4
5
6
7
YYYYMMDD-hhmmss/
├── pages/
│ └── YYYY/
│ └── MM-DD.md
└── posts/
└── YYYY/
└── MM-DD.md

つまり、YAMLの部分とMarkdown化した本文をマージする時に本文側のファイルを見つける処理が必要になります。

あと、細かいですが、Markdown化されたファイルの1行目がタイトルになってしまって、このままではタイトルが2重になるのでMarkdown化されたファイルの1行目を削除する処理も追加しました。

Node-REDでParserを書く手軽さ

いろいろ想定外のことがあって処理が複雑になったとはいえ、思い立ってから1時間ほどでParserが作れてしまうほどNode-REDは手軽です。Flow-Based Programmingがコーディングよりも着手する心理的障壁を下げてくれる効果もあると思います。

ちょっとしたParserを作る際は是非Node-REDで!