Webページ(HTML文書)からの情報抽出は、古くからある話題です。そして、「頑張れば何とかなるけど楽な方法はないよね」というあたりに落ち着く話題でもあります。「頑張るのは辛い ←→ 楽して出来ることはたかが知れてる」というトレードオフの関係なのです。このトレードオフの境界線をズラして、「楽に出来るけど、けっこうなところまでイケる」としたいな、と思います。
XSLTをものすごく単純化してみる
汎用プログラミング言語でHTMLをパーズしていじれば何でもできますが、それは楽ではないですね。HTML文書からの変換処理に向いた言語といえばXSLTです。XSLTは、基本的には XML to XML の変換を扱うので、入力HTML文書をXML(XHTML)化する前処理(またはオプション)が必要です。いったんXML文書ツリーができてしまえば、XSLTの変換能力は豊富で強力です。
問題は、XSLTが難しいことです。習得してしまえば無敵の武器かも知れませんが、学習は容易ではありません。「アレを勉強するくらいなら汎用プログラミング言語でやるよ」と思う人も多いでしょう。
XSLTでは、入力とのパターンマッチをXPathで行い、出力データとXSLTの変数/命令/関数などをXML名前空間で区別しながら混在させて書きます。入力のパターン、処理自体の記述、出力のデータを一緒に書くところが、便利である反面ややこしくなっているところです。
入力はHTMLだとして、入力データとのパターンマッチはCSSセレクターを使ってはどうでしょう。CSSセレクターはXPathほどの能力は持ちませんが、非常に多くの人が使っていてお馴染みなものです。学習の負担もXPathよりはずっと小さいでしょう。
そして、出力はJSONに限定したらどうでしょう。JSONの複合データは、ブラケット('[' と ']')で囲まれた配列か、ブレイス('{' と '}')で囲まれたオブジェクトだけです。XMLのような様々なタグや属性はないので、出力データの記述は極端に単純化されます。
命令や関数は使いません。パターンマッチした結果を組み合わせて出力するだけです。それ以上の処理は、結果であるJSONデータに対して行えばいいと割り切ります。JSONに対する処理なら、どんなプログラミング言語でも出来ます。
XSLTの概念と機能を、「XPath → CSSセレクター」「出力XML → 出力JSON」「命令/関数 → なし」と単純化したらイイんじゃないか? と、実際にやってみました。単純化するので低機能になりますが、トレードオフの境界線をズラす効果はあるように思えます。「楽に出来るけど、けっこうなところまでイケる」みたい。
簡単な実例
今日は詳しいことを述べませんが、いくつかの簡単な実例をあげます。(処理系は実際に動いていて、割と複雑な変換もできます。)
最初に出現する見出し(h1からh4のどれか)の内容テキスト:↓
<h1, h2, h3, h4>$:text</>
すべての画像のURL(src属性値)のリスト:↓
*<img>$src</>
すべての見出し(h1からh6のどれか)の、タグと内容HTMLのタプルからなるリスト:↓
*<h1, h2, h3, h4, h5, h6>$:tag, $:html</>
すべての「div要素の子である見出し(h1からh4のどれか)」の、タグと内容HTMLをプロパティとするオブジェクトからなるリスト:↓
*<div> ><h1, h2, h3, h4>"tag": $:tag, "content": $:html</></>
最初に出現したテーブルの(テキストとしての)セルデータを、入れ子にしたリスト:↓
<table> *<tr> *<th, td>$:text</> </> </>
すべての要素のタグと属性を出現順に列挙したリスト:↓
*<*>$:tag, $:attrs</>