Wikidataで遊んでみた その1 – C#でWikidataからのデータ取得
北本です。
今回のテーマはWikidataです。
Wikidataとは、Wikipediaのデータベース版のようなもので、SPARQLというクエリ言語を介してデータを取得することができます。
正直に言いますと、私はデータベース方面の知識はほとんどありません。
こんな私のような初心者が、データベースに触れてみるのには、Wikidataは格好の題材です。Wikipedia同様、あらゆる分野のデータがありますから、貴方の興味を引くものもきっとあることでしょう。そういったデータを弄りながら楽しく知識を身につけられるかもしれません。
では、さっそくコードを紹介します。
以下は、C#からWikidataにクエリを投げて、ピアノソナタを書いている作曲家を生年月日順に並べたものを取得するプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | static void Main(string[] args) { string url = "https://query.wikidata.org/sparql"; byte[] data; using (System.Net.WebClient wc = new System.Net.WebClient()) { System.Collections.Specialized.NameValueCollection ps = new System.Collections.Specialized.NameValueCollection(); ps.Add( "query", @" SELECT DISTINCT ?composerLabel ?dateOfBirth WHERE { ?composer wdt:P106 wd:Q36834; wdt:P569 ?dateOfBirth. ?work wdt:P86 ?composer; wdt:P31 wd:Q1546995. SERVICE wikibase:label { bd:serviceParam wikibase:language ""ja, en"". } } ORDER BY ?dateOfBirth LIMIT 100 "); wc.Headers.Add("User-Agent: Other"); data = wc.UploadValues(url, ps); } string text = System.Text.Encoding.UTF8.GetString(data); Console.Write(text); } |
“https://query.wikidata.org/sparql”にPOSTでデータを送信することでデータを取得します。その際、取得するデータの条件指定としてqueryプロパティにSPARQLのSELECT文を持たせます。
また、ヘッダにUser-Agentの情報がないとサーバに403エラーを返されてしまうようなので、取り敢えず”User-Agent: Other”を渡しています。
「SELECT 変数 WHERE{ }」の形で記述されるSELECT文は、WHERE節に示した条件を全て満たす変数を出力するものです。今回の場合、?composerのラベルと?dateOfBirthを重複なし(DISTINCT)で出力します。エンティティ(項目)を取得する場合、?composerとのみ記述するとその項目へのURLが取得されますが、?composerLabelのように末尾に”Label”を付けると人間にも識別しやすい名称であるラベルが取得されます。
WHERE節の1~4行目の意味は以下のような感じです
・?composerの職業(P106)が作曲家(Q36834)である。
・同上(?composer)の生年月日(P569)は?dateOfBirthである。
・?workを作曲(P86)したのは?composerである。
・同上(?work)の属性(P31)がピアノソナタ(Q1546995)である。
よくよく考えてみると、ピアノソナタを作曲をしている人物の職業は大体作曲家だと思われるので、1行目の条件はあまり意味がなかったかもしれません……。
ここでは、P106だとかQ36834だとかそれだけ見てもよく分からないIDが使われていますが、Wikidata Query Serviceで入力すれば安心です。例えば、「wdt:職業」なり「wdt:occupation」なり入力して、Ctrl + Spaceを押せば補完されて「P106」を簡単に入力できます。
WHERE節の5行目は、Wikidataの拡張書式で、日本語(ja)のラベルを最優先で使用し、その次に英語(en)のラベルを優先して使用するという意味です、
WHERE節後の2文は、それぞれ「?dateOfBirthでソートして出力する」、「結果の取得件数は最大で100件とする」という意味です。
実行すると以下のように出力されます(2020年7月18日に実行したものです)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <?xml version='1.0' encoding='UTF-8'?> <sparql xmlns='http://www.w3.org/2005/sparql-results#'> <head> <variable name='composerLabel'/> <variable name='dateOfBirth'/> </head> <results> <result> <binding name='composerLabel'> <literal xml:lang='ja'>作者不明</literal> </binding> <binding name='dateOfBirth'> <bnode>t2000978403</bnode> </binding> </result> <result> <binding name='composerLabel'> <literal xml:lang='ja'>フランツ・ヨーゼフ・ハイドン</literal> </binding> <binding name='dateOfBirth'> <literal datatype='http://www.w3.org/2001/XMLSchema#dateTime'>1732-03-31T00:00:00Z</literal> </binding> </result> <result> <binding name='composerLabel'> <literal xml:lang='ja'>ヨハン・クリストフ・フリードリヒ・バッハ</literal> </binding> <binding name='dateOfBirth'> <literal datatype='http://www.w3.org/2001/XMLSchema#dateTime'>1732-06-21T00:00:00Z</literal> </binding> </result> ///////////////////////////////////////////////////////////// 中略 ///////////////////////////////////////////////////////////// <result> <binding name='composerLabel'> <literal xml:lang='ja'>ジャン・バラケ</literal> </binding> <binding name='dateOfBirth'> <literal datatype='http://www.w3.org/2001/XMLSchema#dateTime'>1928-01-17T00:00:00Z</literal> </binding> </result> <result> <binding name='composerLabel'> <literal xml:lang='en'>Carl Vine</literal> </binding> <binding name='dateOfBirth'> <literal datatype='http://www.w3.org/2001/XMLSchema#dateTime'>1954-10-08T00:00:00Z</literal> </binding> </result> </results> </sparql> |
デフォルトではXML形式でデータ返ってきますが、formatプロパティに”json”を指定して送信すると、JSON形式でデータを取得できます。
先程のコードの23行目あたりに以下の一文を追加して実行してみます。
1 | ps.Add("format", "json"); |
以下のような出力結果になります(2020年7月18日に実行したものです)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | { "head" : { "vars" : [ "composerLabel", "dateOfBirth" ] }, "results" : { "bindings" : [ { "composerLabel" : { "xml:lang" : "ja", "type" : "literal", "value" : "作者不明" }, "dateOfBirth" : { "type" : "bnode", "value" : "t2000961748" } }, { "composerLabel" : { "xml:lang" : "ja", "type" : "literal", "value" : "フランツ・ヨーゼフ・ハイドン" }, "dateOfBirth" : { "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime", "type" : "literal", "value" : "1732-03-31T00:00:00Z" } }, { "composerLabel" : { "xml:lang" : "ja", "type" : "literal", "value" : "ヨハン・クリストフ・フリードリヒ・バッハ" }, "dateOfBirth" : { "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime", "type" : "literal", "value" : "1732-06-21T00:00:00Z" } }, ///////////////////////////////////////////////////////////// 中略 ///////////////////////////////////////////////////////////// }, { "composerLabel" : { "xml:lang" : "ja", "type" : "literal", "value" : "ジャン・バラケ" }, "dateOfBirth" : { "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime", "type" : "literal", "value" : "1928-01-17T00:00:00Z" } }, { "composerLabel" : { "xml:lang" : "en", "type" : "literal", "value" : "Carl Vine" }, "dateOfBirth" : { "datatype" : "http://www.w3.org/2001/XMLSchema#dateTime", "type" : "literal", "value" : "1954-10-08T00:00:00Z" } } ] } } |
以上のように、WikidataからXML形式やJSON形式のデータを取得することができましたが、そのままではC#でうまく操作できません。次回は、C#で扱えるようにデータを変換する方法について書いてみたいと思います。