LoginSignup
12
12

More than 5 years have passed since last update.

Node.js+SPARQLでRDFデータベースから「日本史人物の家系図」を表示する

Last updated at Posted at 2017-03-04

目的

 Wikipediaから情報をスクレイピングする際の代替手段として、"DBpedia Japanese"があります。
 このデータベースへのアクセスには、SQLライクな言語"SPARQL(スパークル)"を用いなければならず、記法について学習が必要でした。

 本記事の目的は、SPARQLを用いたRDFデータベースからの情報取得について手法をまとめ、自分への備忘録作成および情報共有をすることです。

結論

 指定した歴史上の人物の、「子、親、兄弟」を、それぞれ検索表示してくれるNode.jsアプリケーションを作成しました。
 動作は以下の通り。

内容

コード

getFamily.js
function getFamily(person) {
  var uri      = "http://ja.dbpedia.org/sparql";
  var children = [];
  var parents  = [];
  var siblings = [];

  console.log("YUKI.N > " + person + " の親族を調査中...");

  async.series([
    function(callback) {
      // 子供を検索
      client.fetch(uri, getParam(person, "child"), function(err, $, res, body){
        children = getJson(body);
        callback(err, children);
      });
    },
    function(callback) {
      // 親を検索
      client.fetch(uri, getParam(person, "parent"), function(err, $, res, body){
        parents = getJson(body)
        callback(err, parents);
      });
    },
    function(callback) {
      // 兄弟を検索
      siblings = compareChildren(parents);
      callback(null, siblings);
    }
  ], function(err, results) {
    if (err) throw err;
    results.forEach(function(v, i){
      switch (i) {
        case 0: console.log("YUKI.N > 子は、"); break;
        case 1: console.log("YUKI.N > 親は、"); break;
        case 2: console.log("YUKI.N > 兄弟は、"); break;
      }
      display(v);
    });
  });

  // ----------------------------
  // SPARQLクエリを生成する
  function getParam(name, cp) {
    var obj = {};
    var q;
    var f = "application/json";

    if (cp == "child") {
      // nameの子供を検索するSPARQLを返す
      q  = "SELECT DISTINCT ?name ?abstract WHERE {{ ?w dbpedia-owl:parent <http://ja.dbpedia.org/resource/"+name+"> . ";
      q += "} UNION { <http://ja.dbpedia.org/resource/"+name+"> prop-ja:子 ?w . } ";
    } else if (cp == "parent") {
      // nameの親を検索するSPARQLを返す
      q = "SELECT DISTINCT ?name ?abstract WHERE { <http://ja.dbpedia.org/resource/"+name+"> dbpedia-owl:parent ?w . ";
    }
    q += "?w rdfs:label ?name . ";
    q += "?w dbpedia-owl:abstract ?abstract . }";
    obj.query  = q;
    obj.format = f;
    return obj;
  }

  // SPARQLendpointからの応答である4バイト文字JSONを変換する
  function getJson(body) {
    return JSON.parse(decodeUnicode(body)).results.bindings;

    function decodeUnicode(unicode) {
      return unicode.replace(/(\\u)([0-9A-F]{4})/g, function(match,p1,p2){
        return String.fromCharCode(parseInt(p2, 16));
      });
    }
  }

  // 子供の数を比較して多いほうを返す
  function compareChildren(p) {
    var result = [];

    p.forEach(function(v) {
      var n = v.name.value;
      var tmp = getJson(client.fetchSync(uri, getParam(n, "child")).body);
      if (tmp.length > result.length) {
        result  = tmp; // 子供を代入
        parents = v;   // 親を再代入
      }
    });
    return result;
  }

  function display(arr) {
    arr.forEach(function(v){
      console.log(v.name.value);
    });
  }
}

【ポイント】SPARQLについて

 SPARQLの記法について記載します。
 コードのgetParam関数では、人物名を引数としてSPARQLクエリを生成する役割を持っています。
 例えば、"織田信長"が与えられた場合、「"織田信長"の子」を表すSPARQLクエリは以下になります。
 このクエリを、"DBpedia Japanese"のSPARQLエンドポイントに対し、URLパラメータに加えてGETすれば、結果を得られます。
 このような応答です。

SELECT DISTINCT ?name
WHERE {
  {
    ?w dbpedia-owl:parent <http://ja.dbpedia.org/resource/織田信長> . --1.
  } UNION {
    <http://ja.dbpedia.org/resource/織田信長> prop-ja: ?w . --2.
  }
  ?w rdfs:label ?name . --3.
}

 コード中の番号はそれぞれ以下の処理を意味しています。
 1.と2.は重複した検索を行っているように見えますが、このようにしないと検索結果に漏れができます。詳細は割愛します。

  1. 「織田信長」を親とする人物(信長の子)を検索し"?w"に格納
  2. 「織田信長」の子である人物を検索し"?w"に格納、UNIONで1.の結果とマージする
  3. "?w"の名前を"?name"に格納

展望

  1. コンソール上の表示だけではなく、"OrgChart.js"などを用いて、図示および芋づる式の家系調査(ある人の子供の子供の子供の...と辿っていける)が行えると、物凄く有用であると思う。

所見

  • 私は日本史が大好き(趣味:お城巡り)なので、このようなツールがあればいいなと以前から思っていた。実現でき、とても嬉しい。
  • この処理をWikipediaへのスクレイピングで実現しようとすると、アクセス回数がかなり多くなり、実行速度低下などが予想される。こちらの案を実現できて良かった。
  • 基本情報技術者試験を17年春季に受験予定なので、SQLの勉強方法に悩まされていた。SPARQLの学習を通してSQLの文法を学ぶことができ良かった。

参考

  1. "Wikipedia からスクレイピングして… とか言ってる人におすすめしたい,DBPedia からの情報抽出": Qiita @pika_shi
  2. "直感RDF!! その1-RDFとは。": Qiita @maoringo
  3. "cheerio-httpcli": GitHub @ktty1220
12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12