Pages

Oct 19, 2012

node.jsでXMLをパースしたい

わざわざサーバサイドでパース?

node.jsを使ってファイルのアップロード処理を実装する記事を以前に書いたのですが、
アップロードするファイルがXMLファイルであり、
そのファイルをパースしなければいけないことになりました。
(某m教授からの要望であったりします)

そこで、色々と調べてみたところ、
node.js + jsdom + request という文字を多く見かけました。

ただし、jsdomでは、ひとつのイベントにリスナを付けすぎるとエラーが起きるとか、
メモリリークが起きるだとか、ちょっとしたDOM操作には向かないのかな、、、
と思うような記述をちらほら見かけました。
(実際に入れてみたりしたんだけど、色々してみたんだけどエラーが解決できず、、、

他の代替手段を無いかと探してみたところ
node.js + cherrio  でウェブスクレイピングという記事を見つけました。
試したところ軽い感じのDOM操作に向いている感じだったので健忘録としてメモ。

概略

cherrio.jsとは?(githubより

• JSDOM's built-in parser is too strict:
• JSDOM is too slow
• JSDOM feels too heavy

-> jsdomが重くて書式が厳格に決められているから使えないサイトが多くなってきた
 もっと手軽で簡単なDOM操作をしたい

みたいなことらしいです。

ちょっとしたDOM操作を行うときに向いている模様。

使い方

流れとしては、Xmlファイルをアップロードされて
それをパースするまでの流れを書いてきます。(各タグの中の情報を取り込むのが目的)

まずは、npmを使ってcheerio.jsをインストール
npm install cheerio

クライアントサイド(アップロードフォーム)
<form action="/qupload" method="post" enctype="multipart/form-data">
    <input type="file"name="upfile"/>
    <input type="submit"value="送信"/>
</form>

サーバサイド
app.js
var Upload = require("./routes/Upload");
app.post("/upload", Upload.uploadPost);
upload.js
/* モジュールの読み込み */
var sequelize = require("sequelize");
var async = require("async");
var exec = require('child_process').exec;
var fs = require('fs');

exports.uploadPost = function (req, res){
    var tmp_path = req.files.upfile.path;
    var path = __dirname + '/../public/uploaded' + req.path + "/" + req.files.upfile.name;
    var cmd = "rm -rf " + __dirname + "/../" + tmp_path + "/../tmp/";

    async.waterfall([ 
     // ファイルのアップロード処理
        function first(callback) {
     fs.rename(tmp_path, path, function(err) {
         if (err){
      throw err;
         }
         fs.unlink(tmp_path, function() {
      if (err){
          throw err;
      }
         });
     });
     setTimeout(function() {
         callback(null, '1st');
            }, 100);
        }, 
        // ファイルをパース
        function second(str, callback) {
            parse(path);
     setTimeout(function() {
         callback(null, str + ' 2nd');
     }, 100);
        },
        // 一時ファイルの削除
        function last(str) {
            exec(cmd, {timeout:1000}, function (error, stdout, stderr){
         if (error == null){
      console.log("compleate" + stdout);
         }
         else {
                    console.log("tmp error:" + error);
         }
            });
            res.render("selectmanage", {});
        }
    ]);
}
/* cheerio の読み込み */
var cheerio = require("cheerio");

/* XMLファイルの問題ファイルをパース */
var parse = function (path, filename){
        // fileを読み込み
 var tmp = fs.readFileSync(path, "utf-8");

 $ = cheerio.load(tmp, {
  ignoreWhitespace: true,
  xmlMode: true
 });
     // DOMにアクセス出来る
 console.log("input >> " + $("input").text());
 console.log("output >> " + $("output").text());
  console.log("answer >> " + $("answer").text());
}

githubな文章によると、
 Familiar syntax: Cheerio implements a subset of core jQuery. Cheerio removes all the DOM inconsistencies and browser cruft from the jQuery library, revealing its truly gorgeous API.

jQueryチックなDOM操作をnode.jsから出来るということで、
使いやすいと個人的に感じました。

ただ、Sjift-jis系の文字コードを扱う為にはもう一工夫必要そうなので
次のエントリぐらいにはそういうのをかけるようにしたいです。

※ 厳密にはjQueryオブジェクトではない?
nodeでjsdomのかわりにcheerioを使ってwebスクレイパー書いてみた

参考

1 comment:

  1. とても魅力的な記事でした!!
    また遊びに来ます!!
    ありがとうございます。

    ReplyDelete