DBpediaでWikipediaからデータを取得する方法
はじめに
この記事は「フィヨルドブートキャンプ Part 2 Advent Calendar 2021 - Adventar」 17日目の記事です。
先日リリースした自作サービスで、Wikipediaのデータをもとにデータベースを構築する際に使用した、DBpedia(日本語版)の簡単な解説を行いたいと思います。
実際にサービスで使用したのはDBpedia(英語版)でしたが、DBpedia(日本語版)の方がわかりやすいので、今回はDBpedia(日本語版)をもとに説明をいたします。
DBpediaについて
DBpedia(日本語版)から引用させていただきました。
DBpediaはWikipediaから情報を抽出してLOD (Linked Open Data)として公開するコミュニティプロジェクトです.本家のDBpediaは主にWikipedia英語版を対象としています.DBpedia Japanese の目的は,Wikipedia日本語版を対象としたDBpediaを提供することです
DBpedia(日本語版)
データを取得する
以下に実際にデータを取得する方法を記載いたしますが、検索に使用するSPARQLについては、簡単な解説にさせていただきます。
織田信長の孫のデータを取得する
DBpediaでデータを抽出する例として、織田信長の孫のデータを取得してみます
- 以下のSPARQLクエリをSPARQL EndpointのQuery Textに入力します。
(SPARQLの簡単な解説については後述します)#SPARQLクエリ PREFIX dbpj: <http://ja.dbpedia.org/resource/> PREFIX dbp-owl: <http://dbpedia.org/ontology/> SELECT DISTINCT ?name ?abstract WHERE { ?child dbp-owl:parent dbpj:織田信長. ?grandchild dbp-owl:parent ?child; rdfs:label ?name. OPTIONAL{ ?grandchild dbp-owl:abstract ?abstract} }
- Results Formatで取得したいデータのフォーマットを選択します(今回はHTML形式で取得します)
- Execute Queryで検索を実行します。
- 以下のように、信長の孫の名前と解説が表示されました。
xmlやjsonにすればデータベース構築の際にデータの加工が容易になると思います。
実行したSPARQLについて簡単な解説
1行目から順番に解説します
- 1行目
目的語としてURIを入力する際の共通部分<http://ja.dbpedia.org/resource/>をdbpjとして定義しています。PREFIX dbpj: <http://ja.dbpedia.org/resource/>
- 2行目
今回利用する述語が定義されているURIをdbp-owlとして定義しています。PREFIX dbp-owl: <http://dbpedia.org/ontology/>
- 3行目
4行目以降のWHERE節で割り当てられた変数のうち、SELECT句の横に列挙されている変数(今回は?nameと?abstract)が出力として表示されます。SELECT DISTINCT ?name ?abstract
- 4行目
4行目以降の"WHERE{ }"の部分はWHERE節といい、WHERE節で列挙されているRDFトリプル文がすべて真となるような、変数割り当てが検索されます。WHERE {
"RDFトリプル文"については以下で解説いたします。 - 5行目
変数(?child)を主語として、述語に親の意味を持つURI(dbp-owl:parent)、目的語に織田信長の意味を持つURI(dbpj:織田信長)を定義しています。?child dbp-owl:parent dbpj:織田信長.
これにより、変数(?child)の親は織田信長であるという意味となり、変数(?child)には信長の子供が格納されます。 - 6行目
5行目の信長の位置に信長の子供(?child)が定義されているので、変数(?grandchild)には信長の子供の子供、要するに孫が格納されます。?grandchild dbp-owl:parent ?child;
- 7行目
主語が省略されていますが、ここの主語には6行目の変数(?grandchild)が定義されます。rdfs:label ?name.
よって、変数(?grandchild)の見出し(dfs:label)は変数(?name)であると定義され、信長の孫の記事の見出しが変数(?name)に格納されます。 - 8行目
7行目と同じように解説(dbp-owl:abstract)を変数(?abstract)に格納したいところですが、OPTIONAL{ ?grandchild dbp-owl:abstract ?abstract}
Wkipediaの記事のなかには解説がないものもあります。そこで、検索条件として必須でないトリプル文を入れたいので、Optional句を用います。
Optional句の中身は、WHERE句で用いたトリプル文とほぼ同じで、
変数(?grandchild)の解説(dfs:abstract)は変数(?abstract)であると定義され、信長の孫の記事の解説が変数(?abstract)に格納されます。
以上を実行し最終的にWikipediaからデータから、信長の孫の記事の見出し(人の記事の場合その人の名前)と解説を抽出することができました。
今回は信長の孫を検索しましたが、孫の孫の孫の孫の...と繰り返していけば織田信成さん (フィギュアスケート選手)の記事に行き着くかもしれませんね。
終わりに
簡単にDBpediaについて解説しようとしましたが、思いのほかSPARQLの解説が煩雑になってしましました。
今回は信長の孫のデータの抽出を例にしましたが、DBpedia(日本語版)の例にもあるように、"誕生日が1月1日の人物"、"全国の地域限定ゆるキャラ"、"歴史の解説と見出し、位置情報"など、これ以外にも様々なデータをWikipediaから抽出することができます。
また、DBpedia(日本語版)を例にして解説しましたが、記事の数的にはDBpedia(英語版)の方が多いので、抽出するデータによって使い分けるといいかなと思います。
歴史検索サービス「Mapnica」をリリースしました
はじめに
FJORD BOOT CAMP(フィヨルドブートキャンプ) の "Webサービスを作って公開する" という最終プラクティスで作成した自作サービスをリリースしましたので、そのサービスのご紹介になります。
Mapnicaについて
サービスの概要
Mapnicaは歴史の出来事を地図上に表示するサービスです。
年代やキーワードでの検索が可能で、検索した出来事が地図上に表示されます。
作成した理由
私は趣味で世界史の勉強をしているのですが、例えばアウステルリッツの戦いについてwikiなどで調べると、「なるほど、ナポレオンが神聖ローマ帝国とロシア帝国に勝利したのか」というところまではわかります。
しかし、日本人である私としてはヨーロッパの地名であるアウステルリッツという言葉には馴染みが薄いので、「あれ?これってフランスで起きたことなの?それともドイツ?ロシア?」という疑問が湧いてきます。(ちなみに現在の国で言うとチェコで起きた出来事です)
このような経験から、歴史の出来事が地図上のどこで起きたのかわからない問題を解決したいと思いこのサービスを作成しました。
このサービスを使うターゲット
このサービスのターゲットは世界史の勉強をしている中で、一歩踏み出して歴史の出来事が現在のどこで起きたのか知りたいと感じる、少し物好きな人に向けたサービスとなります。
欲しい機能
サービスに欲しい機能として、"Google Mapに出来事のマーカーを表示する"、"マーカーをクリックすると解説が出る"、 "年代とキーワードでの検索機能"、"これら全てがページ遷移しないで実行される"の4つを挙げました。
ユーザーログイン機能やユーザによるデータの編集機能なども考えましたが、取り扱っている情報が歴史という大抵の場合変化することほぼないと言えるものでしたので今回は見送りました。
サービス名について
Mapnicaは、Map(地図)+Chronica(年代記)の造語です。地図上に各年代の出来事を表示するサービスなので、この名前にいたしました。気に入っています。
技術スタック
- Ruby on Rails 6.1.4
- Ruby 3.0.2
- Vue 3
- Rspec
- CI/CD
- GitHub Actions
- Heroku
使い方
歴史上の出来事を地図上にマーカーとして表示
- 歴史上の出来事を地図上にマーカーとして表示します。
- 表示されたマーカーをクリックすると、クリックされたマーカーの解説が表示されます。
年代による検索
- スライダーを操作することで年代による検索を実行し、検索結果のマーカーが地図上に表示されます。
キーワード検索
- 入力したキーワードに合致した検索結果をリスト形式で表示します。
- 表示された検索結果をクリックすると、クリックされた検索結果の解説とマーカー、同じ年代の出来事が地図上に表示されます。
大変だったこと
デザイン・UI
ユーザー目線でのデザインを考えることが、慣れていなかったので大変でした。
また、レスポンシブデザインに対応させることを考えた際、使い勝手を考えた結果スマホとPCで、画面の構成をかなり変更することになったので、シンプルに作業量が倍になったような感覚でした。
ただ、書いたコードによってUIや見た目が変化する過程はやっぱり楽しいですね。
データベースの作成
歴史のデータベースの作成にDBpedia(英語版)を使用したのですが、欲しい情報を得るために一からSPARQLの勉強をしなければならず、なかなかサービス本体のコードを書くことができなかったので焦りました。
また、自分が欲しい情報というのは具体的に検索する場合どのようなものなのか、例えば歴史という言葉を構成するものは、出来事(イベント)と時間情報の組み合わせだと考えていたのですが、それだけだとスポーツの大会やコンサートの情報まで取得されてしまうので、一言に歴史と言ってもそれがどのような情報で構成されている言葉なのか定義しようとすると大変だということがわかりました。
今回データベースを作成するにあたり、私が定めた歴史という言葉の定義は、出来事(イベント)と時間情報に加えて、後世に影響を与えたという情報を有しているものといたしました。
Google Maps Apiの設定
超有名なAPIなので情報こそ調べればたくさん出てくるのですが、自分の構想している機能に合致した情報を精査することに苦労しました。
例えば、Google Mapに検索結果のマーカーを表示させたい際に、自分で作成したデータベースの情報を検索してマーカーを表示させたかったのですが、大半の情報はGoogle Maps Apiに備え付けられている検索機能についての情報でした。この場合私の検索方法が悪かったのですが、自分が欲しい情報を検索したり質問したりする際は、当然のことなのですが、その欲しい情報につい自分の中で具体的かつ詳細に絞り込む必要があるので、改めて情報を調べるという当たり前の行為の難しさを実感しました。
Vue.js
FJORD BOOT CAMPのプラクティスで学習してはいたのですが、Vue2とVue3の違いや、コンポーネント間での最適なデータの受け渡し、ページのどの領域をコンポーネント化すると恩恵を受けられるかなど悩むことが多かったです。
しかし、自分的にはVue.jsの扱いに少し自信がつきました。
感想
自分が実現したかった機能はすべて実装できたので、構想したものを実装する能力は上がったと思います。また、サービスの特性上JavaScriptをガリガリ書けたので、プラクティスではRubyに触れる機会が多かったこともあり、自分としてはいい経験ができたと思っています。
それと、サービスの構想からデプロイまで一貫して体験できた経験は今後さまざまな場面において活かせると思いました。
サービス自体は完成しましたが、完成した後からも改善点がたくさん湧いてきているので、今後も継続的にアップデートしていきたいです。