xpcshellでHTMLパース

タイトルどおりかと思っていたけど、よく読んでみると目的としてはHTMLのパースなのだと思う。
もし、そうであるならば、完全では無いけど出来る。

const Cc = Components.classes;
const Ci = Components.interfaces;

var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var suh = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML);
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
var ser = Cc["@mozilla.org/xmlextras/xmlserializer;1"].createInstance(Ci.nsIDOMSerializer);

function dump(obj){
    print("============================================");
    print(obj);
    for (var i in obj){
        print(i + ": " + obj[i]);
    }   
}
var xml = <html xmlns="http://www.w3.org/1999/xhtml"></html>;
// <br>とXMLとしてはinvalidなことに注目
var htmlstr = [ 
    '<html xmlns="http://www.w3.org/1999/xhtml">',
    '<head><title>test page</title</head>',
    '<body>',
    '<h1>hogehoge</h1>',
    '<br>',
    '<a href="http://example.com/hoge" id="hoge">hogehoge</a>',
    '</body>',
    '</html>'
].join("\n");

var doc = parser.parseFromString(xml.toXMLString(), "application/xml");
//doc.QueryInterface(Ci.nsIDOMXMLDocument);
var uri = ios.newURI("http://example.com/", null, null);
var fragment = suh.parseFragment(htmlstr, false, uri, doc.createElement("xml"));
doc.documentElement.appendChild(fragment);

print(ser.serializeToString(doc));
//var elm = doc.getElementById("hoge");
//elm.QueryInterface(Ci.nsIDOMHTMLAnchorElement);
//dump(doc);
//dump(elm);

実行すると

<html xmlns="http://www.w3.org/1999/xhtml">

<H1>hogehoge</H1>
<BR/>
<A id="hoge" href="http://test.com/hoge">hogehoge</A>
</html>

やっていること

  1. DOMParserXML文字列をXMLDocument化(doc)
  2. nsIScriptableUnescapeHTMLでHTML文字列をDocumentFragmentに(fragment)
  3. docfragmentをappendChild
  4. XMLSerializerdocを文字列化して出力

サンプルなので4で出力している。3のところでdocからDOM APIが使えるので後述の難点が気にならなければ使えるのではないかと思う。

難点

出力結果とhtmlstrを比べると分かると思う。

  • タグ名が大文字になる(これは元々HTML要素名が大文字だからかも)
  • head要素が抜け落ちる
  • bodyタグがなくなる
  • anchor要素のhref属性値が相対/絶対パスだと抜け落ちることがある(何故か他の属性があると大丈夫)

成果物(追記:2009-08-15)

以上を踏まえて引数のURLからHTMLコンテンツを取ってきて出力するのを作ってみた。

chmod 700 curl.js
./curl.js http://d.hatena.ne.jp/teramako/20090814/p1 | w3m -T text/html -dump

あっと、Ubuntu 9.04のxulrunner付属のものを使ってますよ。