Node.js で html の表の中身を CSV 形式に加工する

# はじめに
Node.js で html の表の内容を CSV 形式に加工し、ファイルシステムに保存するスクリプトを作成しました。

当初は、スクレイピングツールのように動的に Web サイトから HTML を取得する想定でしたが、
認証機構を突破したり、javascript で動的に出力されるコンテンツの取得がめんどうだったのと
対象の Web ページが限られていたことから Chrome で要素をエクスポートし、それを加工する方針としました。

# 当初の目論見
職場で、グループウェアで管理している社内帳票のドキュメント名と管理者、更新日時を
CSV 形式のファイルに落としたい状況となりました。

グループウェアは、Desknet's neo を利用しており、技術スタックは HTML5 + jquery ベースです。

当初の目論見では、URL を指定しあらかじめ決められたルールに従って対象画面のタグを解析し
CSV 形式に適宜加工するスクリプトを想定していました。

# 挫折、そして妥協

しかし、Desknet's の認証機構を突破し、javascript イベントを発火させて目的のコンテンツを表示させるのは
使い捨てのスクリプトとしては骨が折れました。

そこで、HTML の取得は手動ですることにしました。
幸い、Chrome の「要素の検証」機能により、javascript による動的コンテンツが出力する HTML を参照することができます。
それをバッファにコピーして、テキストエディタに貼り付ければ、望みの生 HTML が手に入ります。ダサいですがとりま妥協。

元データは以下のような形式です。

```html:source.html
<table>
<tbody class="co-tbody-list ui-draggable" aria-disabled="false">
  <tr class="doc-listmod ">
    <td class="co-thd-handle"><span class="co-draghandle">:</span></td>
    <td class="co-chk"><input type="checkbox" name="id" value="5527"><input type="hidden" class="jco-id" value="5527"></td>
    <td class="doc-list-name jco-listview-widauto" style="width: 282px;"><a href="#cmd=docmsetrefer&amp;id=5527&amp;folder=134" class="jco-dragname" title="&nbsp;(総-XXX）捺印簿">&nbsp;(総-XXX）捺印簿</a></td>
    <td class="doc-list-entry"><span title="田中　理央">田中　理央</span></td>
    <td class="doc-th-ymdhi-his" style="width: 143.5px;"><a href="#cmd=docmsetrevindex&amp;id=5527&amp;folder=134">2013/01/02 12:21</a></td>
  </tr>

  <tr class="doc-listmod ">
    <td class="co-thd-handle"><span class="co-draghandle">:</span></td>
    <td class="co-chk"><input type="checkbox" name="id" value="4614"><input type="hidden" class="jco-id" value="4614"></td>
    <td class="doc-list-name jco-listview-widauto"><a href="#cmd=docmsetrefer&amp;id=4614&amp;folder=134" class="jco-dragname" title="(総-XXX）社員情報（新規・変更）登録届">(総-XXX）社員情報（新規・変更）登録届</a></td>
    <td class="doc-list-entry"><span title="山田　太一">山田　太一</span></td>
    <td class="doc-th-ymdhi-his"><a href="#cmd=docmsetrevindex&amp;id=4614&amp;folder=134">2016/02/03 16:42</a></td>
  </tr>
</tbody>
</table>
```

# 最終型
結果的に以下のコードに落ち着きました。

```js:getList.js
var client = require('cheerio');
var fs = require('fs');
var file = process.argv[2];

fs.readFile(file, function (err, contents) {
  $ = client.load(contents.toString());
  var result = [];
  $('tr').each(function(i, tr){
    var row = {};
    $(tr).children().each(function(j, td){
      if($(td).hasClass('doc-list-name')){
          row.docname = $(td).text();
      } else if($(td).hasClass('doc-list-entry')){
          row.owner = $(td).text();
      } else if($(td).hasClass('doc-th-ymdhi-his')){
          row.ymdhi = $(td).text();
      }
    });
    if (Object.keys(row).length != 0){
      result.push(row);
    }
  });
  result.forEach( function (h) {
    console.log(`"${h.docname}","${h.owner}","${h.ymdhi}"`);
  });
});

```
取得した HTML を解析し、加工するのは cheerio モジュールを使用しました。
cheerio は jquery ライクなセレクターの機能が利用でき、コードをシンプルに記述することができます。

前述の HTML に対する出力結果は以下のとおりです。

```CSV:
" (総-XXX）捺印簿","田中　理央","2013/01/02 12:21"
"(総-XXX）社員情報（新規・変更）登録届","山田　太一","2016/02/03 16:42"
```

# 終わりに

一昔前、テキスト加工といえば Ruby や Perl, Python に bash が定番でした。
しかしながら、マークアップ言語の加工は、XPath や xml ライブラリを使いこなす必要があり、煩雑になりがち。
Node.js のおかげで、コマンドライン上で jquery の強力なパース機能をつかえるのは嬉しいです。

私のプログラミング上の母語は Ruby なのですが、
javascript の無名関数は、Ruby のブロックとよく似た概念で、私の思考と親和性が高いです。
今後は積極的に node.js で実装していきたいと思います。