Webスクレイピングをしていて,「こういう構造にマッチしたときのここの文字列を取り出したい」というのはまあよくある話.
こういったXMLからデータを取り出す用途においてはXPathを使うのが一般的なのではないだろうか.HTMLであれば,tidy
コマンドを使うことでHTMLをXMLに変換することができるから,スクレイピングでも使うことができる.
ただしXPathには分かりにくく書きにくいという欠点がある.正直に言うと自分が書いたXPathが一発で動いた試しなどほぼない.たいていの人は根からHTMLを見てなくて,葉から見ているとおもう.「こういうタグのときにここの文字列が欲しい」というイメージはあるのだから,直接ノードの構造を示してパターンマッチさせることはできないのだろうか.そしてxpath
コマンドは構造化された出力をしないのでパイプで繋ぐのが大変面倒だ.
そこで今回は,XMLに対してパターンマッチを行い,テキストノードを取り出すソフトウェアを書いてみて使い勝手を見てみることにした.
リポジトリ
実装言語はCommon Lisp.パターンマッチライブラリとXMLライブラリの両者があり,なおかつ言語に慣れているため選んだ.
実行には処理系マネージャであるRoswellが必要.
例
Steamのユーザページからステータスを取得してみる.Steam Community :: WindymeltをcurlでGETしてきてtidy -asxml < windy.html 2>/dev/null > windy.xml
をかけたという想定である.
ユーザのステータスは以下のようなタグになっている:
<div class="profile_in_game_header">現在オンラインです。</div>
これを表現するパターンファイルは次のような形になる:
<?xml version="1.0" encoding="UTF-8"?> <pattern> <div class="profile_in_game_header"><string var="status" /></div> </pattern>
取り出したい箇所に<string var="status" />
という空タグを入れている.
これでxmatch.ros
を動かす.
$ ./xmatch.ros -e steam.xml windy.xml WARNING: found VARS: ("Currently Online")
ステータスを読み込むことができた.JSONを出力させるようにするともっと便利だと思う.
仕組み
CXMLというライブラリを使ってソースファイルとパターンファイルのXMLからS式を生成し,パターンマッチライブラリに放り込んでいる.マッチするかどうかリストを走査していくので,コストは高そう.賢いことをすると時間を削減できるかもしれない.いまのところ5秒くらいかかってしまう.わざわざS式に変換する手間で時間をロスしているのかもしれない(未測定).
終わり
もっと良さそうな実装ができそうだけど,Proof of Conceptということで今回は満足した.そのうち改良してみたい.