概要
wikidataはQ番号という形式のWikidataエンティティIDを、SPARQLを使って検索します、最初のQ番号を検索する部分が大変なので、それを検索するインターフェイスを作りました。
参考にしたもの
cwrcのwikidata-entity-lookupを試す
を参考に、オリジナルのリポジトリcwrc/wikidata-entity-lookupを使いました。
HTML & Javascript
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wikidata Entity Lookup Example</title>
<!-- ライブラリを読み込む -->
<script src="./wikidata-entity-lookup.js"></script>
<style>
body { font-family: sans-serif; line-height: 1.6; }
#results { margin-top: 20px; border: 1px solid #ccc; padding: 10px; }
.entity { border-bottom: 1px solid #eee; padding: 5px 0; }
.entity:last-child { border-bottom: none; }
a { text-decoration: none; color: #0066cc; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<h1>Wikidata Entity Lookup Example</h1>
<p>検索したいキーワードを入力してください。</p>
<input type="text" id="search-input" value="Douglas Adams">
<button id="search-button">検索</button>
<h2>検索結果:</h2>
<div id="results">ここに結果が表示されます...</div>
<script type="module">
import WikidataEntityLookup from './wikidata-entity-lookup.js';
WikidataEntityLookup({
// DOM要素を取得
const searchInput = document.getElementById('search-input');
const searchButton = document.getElementById('search-button');
const resultsDiv = document.getElementById('results');
// 検索を実行する関数
function performSearch() {
const query = searchInput.value;
if (!query) {
resultsDiv.innerHTML = '検索キーワードを入力してください。';
return;
}
resultsDiv.innerHTML = '検索中...';
WikidataEntityLookup.findPerson(query)
.then(data => {
// 4. 結果を表示
console.log('Search results:', data);
resultsDiv.innerHTML = ''; // 結果をクリア
if (data && data.length > 0) {
data.forEach(entity => {
const entityDiv = document.createElement('div');
entityDiv.className = 'entity';
const name = entity.name || 'No namel';
const description = entity.description || 'No description';
const url = entity.uri;
entityDiv.innerHTML = `
<strong><a href="${url}" target="_blank">${name}</a></strong> (ID: ${entity.id})<br> <span>${description}</span> `;
resultsDiv.appendChild(entityDiv);
});
} else {
resultsDiv.innerHTML = '検索結果が見つかりませんでした。';
}
})
.catch(error => {
// エラーハンドリング
console.error('Search failed:', error);
resultsDiv.innerHTML = '検索中にエラーが発生しました。';
});
}
// ボタンがクリックされたら検索を実行
searchButton.addEventListener('click', performSearch);
// ページ読み込み時に初期値で検索を実行
performSearch();
</script>
</body>
</html>
ライブラリ
wikidata-entity-lookup
そのままですが、ローカルブラウザで動かすために、冒頭のwikidata-sdk だけCDNから読み込むように修正しました。
また、結果を日本語のラベルで表示させるため、以下の箇所を ja にし、検索件数はとりあえず10件にしてあります。
language: 'ja',
limit: 10,
wikidata-entity-lookup.js
//import wdk from 'wikidata-sdk';
import wdk from 'https://cdn.jsdelivr.net/npm/wikidata-sdk@8.1.1/+esm'
const findPerson = (queryString) => callWikidata(getPersonLookupURI(queryString), queryString);
const findPlace = (queryString) => callWikidata(getPlaceLookupURI(queryString), queryString);
const findOrganization = (queryString) => callWikidata(getOrganizationLookupURI(queryString), queryString);
const findTitle = (queryString) => callWikidata(getTitleLookupURI(queryString), queryString);
const findRS = (queryString) => callWikidata(getRSLookupURI(queryString), queryString);
const getPersonLookupURI = (queryString) => getEntitySourceURI(queryString);
const getPlaceLookupURI = (queryString) => getEntitySourceURI(queryString);
const getOrganizationLookupURI = (queryString) => getEntitySourceURI(queryString);
const getTitleLookupURI = (queryString) => getEntitySourceURI(queryString);
const getRSLookupURI = (queryString) => getEntitySourceURI(queryString);
// note that this method is exposed on the npm module to simplify testing,
// i.e., to allow intercepting the HTTP call during testing, using sinon or similar.
const getEntitySourceURI = (queryString) => {
// the wdk used below, actually uses the wikidata php api
return wdk.searchEntities({
search: queryString,
format: 'json',
language: 'ja',
limit: 10,
});
};
const callWikidata = async (url, queryString) => {
const response = await fetchWithTimeout(url).catch((error) => {
return error;
});
//if status not ok, through an error
if (!response.ok)
throw new Error(
`Something wrong with the call to Wikidata, possibly a problem with the network or the server. HTTP error: ${response.status}`
);
const responseJson = await response.json();
const results = responseJson.search.map(({ concepturi: uri, label: name, description }) => {
return {
nameType: 'unknown',
id: uri,
uriForDisplay: uri.replace('http', 'https'),
uri,
name,
repository: 'wikidata',
originalQueryString: queryString,
description,
};
});
return results;
};
/*
config is passed through to fetch, so could include things like:
{
method: 'get',
credentials: 'same-origin'
}
*/
const fetchWithTimeout = (url, config = {}, time = 30000) => {
/*
the reject on the promise in the timeout callback won't have any effect, *unless*
the timeout is triggered before the fetch resolves, in which case the setTimeout rejects
the whole outer Promise, and the promise from the fetch is dropped entirely.
*/
// Create a promise that rejects in <time> milliseconds
const timeout = new Promise((resolve, reject) => {
const id = setTimeout(() => {
clearTimeout(id);
reject('Call to Wikidata timed out');
}, time);
});
// Returns a race between our timeout and the passed in promise
return Promise.race([fetch(url, config), timeout]);
};
export default {
findPerson,
findPlace,
findOrganization,
findTitle,
findRS,
getPersonLookupURI,
getPlaceLookupURI,
getOrganizationLookupURI,
getTitleLookupURI,
getRSLookupURI,
fetchWithTimeout,
};
デモ
Wikidata Entity Lookup Example
今後
WikidataからエンティティIDが分かれば、そのあとは適当なSPARQL wrapperなどでデータを取り出せばよいです。