読者です 読者をやめる 読者になる 読者になる

S.F. Page

Programming,Music,etc...

はてなブログAtomPub APIの非公式node.jsラッパーの改造。はてなブログ記事の一括更新。

動機

去年だったかおととしだったか、ドメインをenoie.netからsfpgmr.netに変更した。はてなブログの古い記事には古いドメインのリンクがかなりあって、リンク切れとなってしまった。 よく読まれている記事は手メンテで修正したが、量的に結構あり、すべてを手メンテで修正するのは難しいので放置状態であった。

これをなんとかしたくなった。

最初はてなブログのエクスポート・インポートを使って行おうと思ったが、これは以下の理由で使えない。

  • HTMLでエクスポートされるため、元の記事のフォーマット(markdown形式)が失われてしまう。

  • エクスポートしたデータをインポートすると、記事は更新されず、新規に追加される。
    記事を全部削除してインポートすればよいかもしれないが、恐くてそれはできない。

それで、はてなBlogには記事の投稿・更新用API(AtomPub)があるので、これを使って更新しようと考えた。nodeならすでにこれのラッパーがあるのではないかと探したらあった。

github.com

このライブラリはAtomPubをラップしたものである。AtomPubはXMLベースのAPIであるが、このライブラリではxml2jsを使用して、API呼び出し・処理結果をJSONで行うようになっている。ただ返されるJSONデータはXMLを変換したものであるので、変換の際にXMLとJSON間の仕様の差を埋めるための独自の仕様(xml2jsの)が存在する。それは

  • テキストノードは_プロパティに格納される。
  • アトリビュートは$プロパティに格納される。

というものである。これを知らないと処理する際にちょっと困ってしまう。実際困った。。

で、このライブラリを使ってnodeでコードを書いてみることにした。

しかしこのライブラリには1つ問題があった。「ブログエントリの一覧取得」APIのラッパー関数Blog.list()に引数{page:'xxxxxxx'}を渡すと動作しない。原因は以下のコードにあった。

https://github.com/bouzuya/node-hatena-blog-api/blob/master/src/blog.coffee#L176

index: (options, callback) ->
    method = 'get'
    unless callback?
      callback = options 
      options = null
    pathWithoutQuery = "/#{@_username}/#{@_blogId}/atom/entry"
    page = options?.page

callbackが未定義の場合に、optionsをcallbackに入れるようにしている。
ただこのままだと「optionsが未指定でcallbackが指定されている」ケースだと問題ないが、「optionsが指定でcallbackが未指定」のケースでは問題が発生してしまう。 なのでこのライブラリをforkして修正することにした。

修正から改造

このライブラリはcoffeeで作られている。coffeeはほとんど知らないので、ES6に変換することにした。
変換には以下のツールを使用した。

github.com

上のツールのREADME.mdを読むと、「ES6」によりcoffeeは役割を終えた。」なんてことが書いてある。変換したコードはものすごくES6していた。coffeeの特徴的な仕様はけっこうES6に取り込まれたからかなと思う。そういうことなのでcoffeeは役割を終えたということらしい。

ただimportだけはnodeで使う場合何とかしないといけないので、rollupでCommonJS形式に変換することにした。

そして変換したソースコードを修正した。修正ついでに、いろいろな改造を施した。

  • メソッド名をわかりやすく(できる限り自明に)した。
  • コールバックを廃止して、Promiseのみにした。
  • 変数チェックをそこそこ厳密に行うようにし、不正なAPIコールをできる限り行わないようにした。
  • README.mdでほとんどのことがわかるようにした。

結果は以下のレポジトリにまとめた。詳細についてはREADME.mdを参照してほしい。

github.com

AtomPub APIで気になるところ

2つ気になる点がある。

  1. APIドキュメントではブログエントリの一覧取得は一度に7件までとなっているが、実際APIをコールすると10件返ってくる。
  2. 連続投稿を行う際、記事と記事の間隔を1000ms程度開けないと正常に投稿されない。
    連続ポストのサンプルを書いているときに気がついた。見た目ちゃんとポストされているように見えるし、管理画面でも編集・削除できるけど、実際のブログには表示されないという不具合が発生する。

それで

これを使って、記事を一括更新した。バックアップはちゃんととってからね。やっぱり恐いから。。 とりあえず、古いドメインのリンクは一掃することができた。

あとcoffeeは廃れつつあるのだということを知って、ちょっとびっくりした。

mizchi.hatenablog.com

ES6が使えるようになってきて、AltJSの存在意義はかなり薄れてしまった感があるが、どうなんだろう。