[Objective-C]DBpediaにSPARQLクエリを入力してJSON出力する


SPARQL Advent Calendar 2015の11日目です。よろしくです。

ここでは、DBpedia JapaneseのエンドポイントにSPARQLクエリを投げて情報を取得します。

具体的にはObjective-Cを使ってJSON形式のデータを取得するにはどうすればいいんだろう、という疑問を解消するべくしてこの記事は書かれています。

DBpediaとは

まずDBpediaとは、簡単にオンライン大百科辞典の代表であるWikipediaのインフォボックスの情報を取得するプログラムを持ったWebサイトのことです。

公式ホームページから引用するとこのように書かれています。

DBpediaはWikipediaから情報を抽出してLOD (Linked Open Data)として公開するコミュニティプロジェクトです.本家のDBpediaは主にWikipedia英語版を対象としています.DBpedia Japanese の目的は,Wikipedia日本語版を対象としたDBpediaを提供することです.

入力例

では早速クエリ例を示していきます。これはSPARQLエンドポイントにクエリを入力して実行させることで入力に対する出力された情報が得られます。
こんな感じの見た目。

クエリ内容

上記のQuery Text内に以下のようなクエリを入力してRun Queryする。

prefix dbp: <http://ja.dbpedia.org/resource/>
prefix dbp-owl: <http://dbpedia.org/ontology/>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>

SELECT *
WHERE {
    dbp:結月ゆかり dbp-owl:wikiPageWikiLink ?thing1.
    ?thing1 rdfs:label ?label.
}

このクエリでやっていることは、おおまかに「結月ゆかり」という主語に対して述語wikiPageWikiLinkを用いることで得られる目的語?thing1を取得する。
そして、次の行の主語?thing1に対してrdfs:labelを用いることで人間が読むことのできるリソース、つまり文字列として目的語である?labelに格納する、という感じ。

出力例(HTML)

実際に出力を見たほうがわかりやすいです。ちなみにこのクエリをHTML形式で出力させるとこんな感じ。thing1に取得したリソースがずらーっと表示されて、labelにはその文字列が表示されている。
単にそのリソースを表しているキーワードを取得したいってときに役立ちそうです。

それでObjective-Cのコードに埋め込むとこのようなかんじ。ここではJSON形式でデータ出力するようにしています。

ViewController.m
    //送信するリクエストURLをつくる
    __block NSString *topic = @"結月ゆかり";
    NSString *loadSparqlQueries = [NSString stringWithFormat:@"default-graph-uri=http://ja.dbpedia.org&query=prefix dbp: <http://ja.dbpedia.org/resource/> prefix dbp-owl: <http://dbpedia.org/ontology/> prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE { dbp:%@ dbp-owl:wikiPageWikiLink ?thing1. ?thing1 rdfs:label ?label.}&format=json&timeout=0&debug=on", topic];
    NSString *encodedLoadSparqlQueries = [loadSparqlQueries stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSString *urlString = [NSString stringWithFormat:@"http://ja.dbpedia.org/sparql?%@",encodedLoadSparqlQueries];
    NSLog(@"urlString : %@", urlString);
    NSURL *url = [NSURL URLWithString:urlString];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    NSLog(@"%@", request);

    // リクエストを送信する
    NSURLResponse *response = nil;
    NSError *error = nil;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

    NSMutableData *responseData = [[NSMutableData alloc] init];
    [responseData appendData:data];

    NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    NSError *e = nil;
    NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&e];

    // リクエスト結果を表示する。
    NSLog(@"request finished!!");
    NSLog(@"error = %@", error);

    NSArray *allValuesArray = [jsonDict valueForKeyPath:@"results.bindings.label.value"];

    NSLog(@"First Topic1 : %@", topic);

    [allValuesArray enumerateObjectsUsingBlock:^(NSString *valueString, NSUInteger idx, BOOL *stop) {
        //数字:値 がずらーっと出力される
        NSLog(@"%lu: %@", (unsigned long)idx, valueString);}];

出力されるデータ

長ったらしくなってしまいますが、以下のように取り出されました。

出力例(JSON)

{ "head": { "link": [], "vars": ["thing1", "label"] },
"results": { "distinct": false, "ordered": true, "bindings": [
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/12\u670822\u65E5" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "12\u670822\u65E5" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/2011\u5E74" } , "label": { "type": "literal", "xml:lang": "ja", "value": "2011\u5E74" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Microsoft_Windows_XP" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "Microsoft Windows XP" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Microsoft_Windows_Vista" }    , "label": { "type": "literal", "xml:lang": "ja", "value": "Microsoft Windows Vista" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Microsoft_Windows_7" }    , "label": { "type": "literal", "xml:lang": "ja", "value": "Microsoft Windows 7" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:\u97F3\u697D\u30BD\u30D5\u30C8\u30A6\u30A7\u30A2" }  , "label": { "type": "literal", "xml:lang": "ja", "value": "\u97F3\u697D\u30BD\u30D5\u30C8\u30A6\u30A7\u30A2" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u30E4\u30DE\u30CF" } , "label": { "type": "literal", "xml:lang": "ja", "value": "\u30E4\u30DE\u30CF" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u97F3\u58F0\u5408\u6210" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "\u97F3\u58F0\u5408\u6210" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/ITunes_Store" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "ITunes Store" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/VOCALOID" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "VOCALOID" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u5973\u6E80\u5225\u7A7A\u6E2F" } , "label": { "type": "literal", "xml:lang": "ja", "value": "\u5973\u6E80\u5225\u7A7A\u6E2F" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u6587\u5009\u5341" } , "label": { "type": "literal", "xml:lang": "ja", "value": "\u6587\u5009\u5341" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/AH-Software" }    , "label": { "type": "literal", "xml:lang": "ja", "value": "AH-Software" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/Category:VOCALOID" }  , "label": { "type": "literal", "xml:lang": "ja", "value": "VOCALOID" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u30A8\u30B0\u30B8\u30C3\u30C8\u30C1\u30E5\u30FC\u30F3\u30BA" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "\u30A8\u30B0\u30B8\u30C3\u30C8\u30C1\u30E5\u30FC\u30F3\u30BA" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u77F3\u9ED2\u5343\u5C0B" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "\u77F3\u9ED2\u5343\u5C0B" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u30C9\u30EF\u30F3\u30B4\u30FB\u30DF\u30E5\u30FC\u30B8\u30C3\u30AF\u30A8\u30F3\u30BF\u30C6\u30A4\u30F3\u30E1\u30F3\u30C8" }   , "label": { "type": "literal", "xml:lang": "ja", "value": "\u30C9\u30EF\u30F3\u30B4\u30FB\u30DF\u30E5\u30FC\u30B8\u30C3\u30AF\u30A8\u30F3\u30BF\u30C6\u30A4\u30F3\u30E1\u30F3\u30C8" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u30E4\u30DE\u30CF\u30DF\u30E5\u30FC\u30B8\u30C3\u30AF\u30B3\u30DF\u30E5\u30CB\u30B1\u30FC\u30B7\u30E7\u30F3\u30BA" } , "label": { "type": "literal", "xml:lang": "ja", "value": "\u30E4\u30DE\u30CF\u30DF\u30E5\u30FC\u30B8\u30C3\u30AF\u30B3\u30DF\u30E5\u30CB\u30B1\u30FC\u30B7\u30E7\u30F3\u30BA" }},
{ "thing1": { "type": "uri", "value": "http://ja.dbpedia.org/resource/\u30A8\u30A4\u30D9\u30C3\u30AF\u30B9\u30FB\u30DF\u30E5\u30FC\u30B8\u30C3\u30AF\u30FB\u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30F4" } , "label": { "type": "literal", "xml:lang": "ja", "value": "\u30A8\u30A4\u30D9\u30C3\u30AF\u30B9\u30FB\u30DF\u30E5\u30FC\u30B8\u30C3\u30AF\u30FB\u30AF\u30EA\u30A8\u30A4\u30C6\u30A3\u30F4" }} ] } }

NSLogで出力したとき

2015-12-11 01:30:00.277 test[1396:75926] First Topic1 : 結月ゆかり
2015-12-11 01:30:00.277 test[1396:75926] 0: 12月22日
2015-12-11 01:30:00.277 test[1396:75926] 1: 2011年
2015-12-11 01:30:00.277 test[1396:75926] 2: Microsoft Windows XP
2015-12-11 01:30:00.277 test[1396:75926] 3: Microsoft Windows Vista
2015-12-11 01:30:00.277 test[1396:75926] 4: Microsoft Windows 7
2015-12-11 01:30:00.277 test[1396:75926] 5: 音楽ソフトウェア
2015-12-11 01:30:00.278 test[1396:75926] 6: ヤマハ
2015-12-11 01:30:00.278 test[1396:75926] 7: 音声合成
2015-12-11 01:30:00.278 test[1396:75926] 8: ITunes Store
2015-12-11 01:30:00.278 test[1396:75926] 9: VOCALOID
2015-12-11 01:30:00.278 test[1396:75926] 10: 女満別空港
2015-12-11 01:30:00.278 test[1396:75926] 11: 文倉十
2015-12-11 01:30:00.279 test[1396:75926] 12: AH-Software
2015-12-11 01:30:00.279 test[1396:75926] 13: VOCALOID
2015-12-11 01:30:00.279 test[1396:75926] 14: エグジットチューンズ
2015-12-11 01:30:00.280 test[1396:75926] 15: 石黒千尋
2015-12-11 01:30:00.280 test[1396:75926] 16: ドワンゴ・ミュージックエンタテインメント
2015-12-11 01:30:00.280 test[1396:75926] 17: ヤマハミュージックコミュニケーションズ
2015-12-11 01:30:00.281 test[1396:75926] 18: エイベックス・ミュージック・クリエイティヴ

さいごに

DBpediaからJSONを取得できるので、パースしてそのデータをもとにしたアプリケーション作成など幅が広がりそうです。Objective-CにおけるJSONのパースに関してはQiita記事に多数存在していますので検索してみてください。あとでちゃんと日本語として結果を取り出すための方法もまとめてみようかな・・。

そういえば(唐突)、PediaRoute.comというWebサイトがあります。これは、Wikipediaで6回リンク辿ればどのページでも行き着くことができるかどうかを調べることができます。これってどういうアルゴリズムで動いてるんでしょうかね。

あと、SPARQL Advent Calendarではありませんが、こんな記事もかいてます。よかったらみてね。DBpediaのある主語の述語プロパティwikiPageWikiLinkの目的語となる文字列を取得するためのSPARQLクエリ

理解の助けになりそうな本