先日SPARQLで国立国会図書館のLinked Open Data (LOP)からデータを抽出する機会がありました。
個人的に面白いと思ったのですが、SPARQL初心者で今後使わないと忘れてしまいそうなので、自分の備忘録用、また初めて国立国会図書館LOPを利用する方向けに書きました。
本記事は以下の前提で書いています。
- SQLの基本的な書き方は抑えている
- Linked Dataについてはすでに知っている
国立国会図書館のLinked Open Dataとは
- 国立国会図書館が提供する書籍や記事論文などのメタデータをもとに提供している
- 1つのリソースに対して↓のような情報が与えられている
(例)カルボン酸:https://id.ndl.go.jp/auth/ndlsh/00565061
実行したクエリ
<抽出したかった条件>
skos:inScheme=Topical Terms
のuri
, xl:prefLabel
, xl:altLabel
, skos:broader
の一覧を抽出したい
PREFIX xl: <http://www.w3.org/2008/05/skos-xl#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?uri ?label ?altlabel ?broaderlabel WHERE {
?uri xl:prefLabel [ xl:literalForm ?label ].
?uri xl:altLabel [ xl:literalForm ?altlabel ].
OPTIONAL {
?uri skos:broader [ rdfs:label ?broaderlabel].
}
?uri skos:inScheme <http://id.ndl.go.jp/auth#topicalTerms> .
}
混乱したこと
私はこちらのサイト上でクエリ実行をしていたのですが
https://id.ndl.go.jp/information/sparql/
COUNT
やサブクエリが使えず「クエリが間違ってるのかな」と悪戦苦闘していました。
しかしこれはSPARQL1.0を使用していたことが原因でした(汗)
SPARQL1.0: https://id.ndl.go.jp/information/sparql/
SPARQL1.1: https://id.ndl.go.jp/information/sparql-11/
SPARQL1.1では実行できるので、正しい方のリンクになっているか確認してから実行しましょう。
クエリの詳細
PREFIX: 省略形を定義するところ
PREFIX xl: <http://www.w3.org/2008/05/skos-xl#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
-
xl
=http://www.w3.org/2008/05/skos-xl#
ということ。
SELECT
の中でURLを記載してもいいが、PREFIXに記載しておくことでxl
と記載するだけで呼び出すことができる。 - リンクはNamespaceドキュメントで、例えばリソースのページに記載のある
xl:prefLabel
とは何か?が定義されているところ。
SELECT文の基本的なルール
SELECT ?uri ?label ?altlabel ?broaderlabel WHERE {
?uri xl:prefLabel [ xl:literalForm ?label ].
?uri xl:altLabel [ xl:literalForm ?altlabel ].
OPTIONAL {
?uri skos:broader [ rdfs:label ?broaderlabel].
}
?uri skos:inScheme <http://id.ndl.go.jp/auth#topicalTerms> .
}
- SPARQLでは
?
で始まるものが変数と呼ばれる -
SELECT
の後はSQL同様、どんな項目を表示したいかを記載する -
SELECT *
とした場合は定義した変数全てが表示される -
WHERE
以降は主語-述語-目的語(トリプルという)の順番で記載する
例)?uri skos:inScheme <http://id.ndl.go.jp/auth#topicalTerms>
主語:?uri
述語:skos:inScheme
目的語:http://id.ndl.go.jp/auth#topicalTerms
(=?uri
はskos:inScheme
というプロパティを持ち、その値がtopicalTerms
)と解釈できる - トリプルの終わりには
.
をつける(OPTIONAL
など}
で終わる場合はつけなくてもエラーにはならない) -
[]
はブランクノードといい省略形(例でみた方がわかりやすいです)
例)?uri skos:broader [ rdfs:label ?broaderlabel]
は以下と同義sparql?uri skos:broader ?broader ?broader rdfs:label ?broaderlabel
-
OPTIONAL
はあれば表示する、なければ表示しなくてもいい条件を記載する際に用いる
参考にした資料・動画
- https://youtu.be/OKbo8PrQpwQ?si=15T1mJ1B2nxoSK-d
- https://id.ndl.go.jp/information/wp-content/uploads/2023/05/api-spec.pdf
【番外編】SPARQLWrapperを使った場合
私はpythonを主に使うので、SPARQLWrapper
というライブラリを使ってjupyterでもデータを抽出しました。一応その時に使ったコードも記載しておきます。
from SPARQLWrapper import SPARQLWrapper, JSON
import pandas as pd
# SPARQLエンドポイント
sparql = SPARQLWrapper("http://id.ndl.go.jp/auth/ndla/sparql") # NDLのエンドポイント
sparql.setReturnFormat(JSON) # 今回はJSONからデータフレームに整形
# クエリテンプレート
query_template = """
PREFIX xl: <http://www.w3.org/2008/05/skos-xl#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?uri ?label ?altlabel ?broaderlabel WHERE {
?uri xl:prefLabel [ xl:literalForm ?label ].
?uri xl:altLabel [ xl:literalForm ?altlabel ].
OPTIONAL {
?uri skos:broader [ rdfs:label ?broaderlabel].
}
?uri skos:inScheme <http://id.ndl.go.jp/auth#topicalTerms> .
}
"""
# データ取得
offset = 0
all_results = []
while True:
# クエリを設定
sparql.setQuery(query_template + f" OFFSET {offset} LIMIT 1000") # 1回で1000件まで取得できるので1000件ずつ取得する
# クエリ実行
results = sparql.query().convert()
bindings = results["results"]["bindings"]
if len(bindings) == 0:
break # これ以上データがない場合、ループを終了
all_results.extend(bindings)
offset += 1000 # 次の1000件を取得
print(f'offset: {offset}')
# データをPandasデータフレームに変換
df = pd.DataFrame([
{
"uri": res["uri"]["value"],
"label": res["label"]["value"],
"altlabel": res.get("altlabel", {}).get("value", None),
"broaderlabel": res.get("broaderlabel", {}).get("value", None) if "broaderlabel" in res else None,
}
for res in all_results
])
# 取得件数を表示
print(f"取得件数: {len(df)}")
df.head()