0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WikidataのEntityを検索する

Posted at

概要

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などでデータを取り出せばよいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?