dblp.xml から expat をつかって共著者を抽出
最近、dblp データのスナップショットが、web で公開されているのを知りました (copyrightは、dblp.dtd内にあります)。これを使って、自分と距離が近い研究者のグラフが表示したいなぁと思います。今日は、expat を使って、対象の著者の共著者リストを取り出すとこまでの覚書です。
(追記:ここに、ソースファイルを掲載しました。よろしければどうぞ)
まずは、xml のパーサ選びからはじめます。言語はとりあえずc/c++ってことにしています。xerces, expat, rxp などがあるようです。2009年7月現在でdblp.xmlのサイズが約600MBあります。全体の構造を保持したりするのはちょっと大変ですね。それから、DOMも必要ないし、パフォーマンス重視ということで、expat を採用しました。
expat を google で検索すると、簡単なチュートリアルが見つかると思います。基本はそちらで確認できます。とっても参考になりました。今回は、少しつまずいた箇所についての覚書を残しておきたいと思います。
次の要素は実体参照を含む dblp.xml の一部ですが、ここら辺の扱いで迷いました。最初の注意としては、要素authorを読んでいるとき、その内容に実体参照がある場合は、G, & uuml;, nther Goumlrzの三回連続で XML_SetCharacterDataHandler で設定した外部関数が呼ばれます。基本なのかもしれませんが、XML_SetCharacterDataHandlerで指定される関数の第二引数に G& uuml;nther Goumlrz がそのまま代入されると思って実装していました。ふむむ。
<article mdate="2003-01-31" key="tr/ibm/IWBS66"> <author>Günther Goumlrz</author> <title>Verarbeitung natürlicher Sprache</title> <journal>IWBS Report</journal> <volume>66</volume> <year>1989</year> <publisher>IBM Germany Science Center, Institute for Knowledge Based Systems</publisher> </article>
次は、実体参照を解決するやり方です。例えば、& uuml; → ü は、dblp.dtdで実体が定義されています。dblp.dtdは、dblp.xml と同様にwebで公開されています。これをプログラムと同じディレクトリにコピーしちゃいます。そして、次のようにパーサを定義した後で実体参照を操作する関数を指定します。あ、あと、コンパイル時には、引数として-XML_DTDを指定しておいて下さい。
/* constructing parser */ XML_Parser parser = XML_ParserCreate("ISO-8859-1"); /* parsing external DTD */ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); XML_SetBase(parser, "dblp.dtd"); XML_SetExternalEntityRefHandler(parser, externalEntityRefHandler);
それから、externalEntityRefHander は、例えば次のように書けます。これは、expat のソース内の test ディレクトリにあるコードを参考にしています。
int externalEntityRefHandler(XML_Parser parser, const XML_Char* content, const XML_Char* base, const XML_Char* systemId, const XML_Char* publicId) { XML_Parser extparser = XML_ExternalEntityParserCreate( parser, content, "ISO-8859-1"); FILE* fp = fopen(base, "r"); if (fp == 0) { fprintf(stderr, "open: %s", base); return 0; } int ret = 1; size_t BUFSIZE = 4096; for (;;) { char *buf = (char*) XML_GetBuffer(extparser, BUFSIZE); if (!buf) { ret = 0; break; } size_t nread = fread(buf, sizeof(char), BUFSIZE, fp); if (ferror(fp)) { ret = 0; break; } if (!XML_ParseBuffer(extparser, nread, feof(fp))) { ret = 0; break; } if (feof(fp)) { break; } } fclose(fp); XML_ParserFree(extparser); return ret; }
最後に、大学院時代の恩師をキーとして共著者を引いてみました。自分のだと実体参照がある共著者がいなかったためです。また、自分の環境(MacBookPro 2.26GHz)では、下のリストを出力するのに約30秒かかりました。思っていたより速かったです。まだ、ちょっと問題はあります(Peterさんが2回カウントされてしまう)が、今日はここまでにします。
... Antonio Hernández-Barrera ... Jirí Matousek ... Peter Brass Peter Braß ...