国立国会図書館のAPIで書誌情報を検索してみた話
書名や著者名に対する読み仮名の情報を揃えたくなったので、国立国会図書館のAPIで検索してみた。
国立国会図書館のAPI
いくつか種類があるが、今回使うのはSRUというプロトコル。検索用API3つの中では最も高機能で、前方一致や完全一致、ソート順の指定などもできる。検索対象のフィールドとしてはタイトル、著者、出版社、NDC分類、ISBN、出版年などが使える。
GETでXMLが落ちてくるので、例えば https://iss.ndl.go.jp/api/sru?operation=searchRetrieve&recordPacking=xml&recordSchema=dcndl&maximumRecords=1&query=title%3D%E3%81%86%E3%82%89%E3%82%89%E8%BF%B7%E8%B7%AF%E5%B8%96%201 をブラウザで開くと見れる。
クエリパラメータを簡単に解説するとこういう感じ。
operation=searchRetrieve
コマンド的なものrecordPacking=xml
検索結果をそのままXMLに埋め込む指定。デフォルトはtextで、XMLが文字列として入ってくる。recordSchema=dcndl
レコードのスキーマを色々選べる。デフォルトのdc
はdcndl
よりもシンプルで、カナ情報とかは入っていない。maximumRecords=1
いわゆるlimitquery=...
検索クエリを CQL (Contextual Query Language) で記述する。ここではtitle=うらら迷路帖 1
。後述。
そしてお目当てのカナ情報は <dcndl:transcription>
タグに入っている。
なおここで取れるデータは別組織から提供を受けたものもあるため、メタデータのライセンスはデータプロバイダごとに異なる。国立国会図書館オンラインのデータだとCC-BY 4.0、とか。
CQLと戦う
queryパラメータに指定するクエリについてはAPIのドキュメントにも説明はあるものの、CQLを知っている前提の説明なのでこれだけだとよくわからない。特に前方一致に使う ^
が exact
等と並列に書かれていてさんざんはまった *1。なのでCQL自身については別途 https://www.loc.gov/standards/sru/cql/spec.html や https://www.loc.gov/standards/sru/cql/contextSets/theCqlContextSet.html などを読むのがよさそう。
もしくは同様のAPIである 国立国会図書館東日本大震災アーカイブのAPIドキュメント を読むと、基本的な使い方や雰囲気は掴める。
とはいえ結局 field =|==|exact|all|any "term" [and/or ...]
で、加えるとしたらソート条件ぐらいだろう。
=
と==
- 半角スペースを含んでいてもフレーズ検索される
- 全角スペースを含むとフレーズ検索ではなくなる
^
は==
だと無視される
exact
での完全一致- 日本語扱いの英字?(NEW GAME! とか)は全角でも半角でもマッチするが、英語扱いの英字は半角でないとマッチしない
NEW GAME!
で言うところのスペースや感嘆符は半角にしないとマッチしない
- メタ文字
^
での前方一致- 前述の「日本語扱いの英字」は全角英字にしないとヒットしない
title = "^NEW" AND creator = 得能正太郎
はよくてtitle = "^NEW" AND creator = 得能正太郎
はダメtitle exact "NEW GAME!"
やtitle = "NEW GAME!"
は大丈夫
- 空白を含むフレーズの前方一致ができない
title = "^foo bar"
は検索結果が0件になるtitle all "^foo bar"
はできるが、これはfooとbarが連続している保証がない- つまり
title = "^foo" and title = "foo bar"
とするのが一番近いだろうか…
- つまり
- CQLの資料を見るに、
adj
とか/string
とかprox
とかが使えればあるいはという気もするが、対応していない様子
- 前述の「日本語扱いの英字」は全角英字にしないとヒットしない
取得結果を読み取る
JSなら例えばこんな感じで。
const title = "..."; const creator = "..."; const xml = await fetch("https://iss.ndl.go.jp/api/sru?operation=searchRetrieve&recordPacking=xml&recordSchema=dcndl&onlyBib=true&maximumRecords=1&query="+encodeURIComponent(`title = "${title}" and creator = "${creator}" and sortBy=issued_date/sort.ascending`)).then(x => x.text()); const doc = new DOMParser().parseFromString(xml, "application/xml"); const title_kana = doc.querySelector('title transcription')?.textContent; const creator_kana = doc.querySelector('creator transcription')?.textContent; console.log(`${title}(${title_kana}) - ${creator}(${creator_kana})`);
XMLはDOMParserを通せばDOMで触れるので、あとはquerySelectorでもなんでも。本当は名前空間を指定した方がいいのだろうけど、querySelectorでやるのも面倒だったので省略。
おまけ
ところでKindleの芳文社創立70周年セールが話題ですね。いい機会なので、たまにはアソシエイトIDを活用してみましょう。
擬人化されていないやつは苦手です、ええもちろん。
CSPを避けてUserScriptでCSS追加するやつ
拝啓 UserScript作者の皆様におかれましてはいかがお過ごしでしょうか。
わたしは以前書いたUserScriptが、サイト側のCSP導入によって <style>
の挿入だけ動かなくなっていました。
今日はその話。
いつものやつ
document.head.insertAdjacentHTML('beforeend', ` <style> #foo { display: none } #bar { cursor: pointer } </style> `);
…このシンタックスハイライト、Template literal 認識してなくない?
CSPでunsafe-inlineが許可されなくなると、こんな感じで蹴られるようになる。
Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' https:". Either the 'unsafe-inline' keyword, a hash ('sha256-alRSRYviRMwuItyDEgtLXyYEpCVEYkqt5i6LJx1Oes4='), or a nonce ('nonce-...') is required to enable inline execution.
この時 HTMLStyleElement#sheet
が null
になっていることで判別ができる(普通は appendChild したところで CSSStyleSheet が入る)。
別解1. Constructable Stylesheet Objects を使う
https://wicg.github.io/construct-stylesheets/
Motivationを斜め読みした感じ…Web Components で Shadow Root の中にだけ適用されるスタイルシートが増えるなかで、スタイルシートの処理や内部表現を効率化したい。styleタグについてはそういうことをしたりもするけど、JS側でスタイルを変更することもあってどうしたものか。というところで、CSSStyleSheetをJS側で作って再利用できるようにしよう、ということらしい?
で現状Blinkでしか使えないこのインターフェイス、なんとCSPの制約を受けない。将来もそうなのかはどうなんだろうなと思いつつ。
const sheet = new CSSStyleSheet(); // syncでない、Promise返す版もある sheet.replaceSync(` #foo { display: none } #bar { cursor: pointer } `); // adoptedStyleSheets は FrozenArray なので要素の追加はできない。再代入はできる。 document.adoptedStyleSheets = document.adoptedStyleSheets.concat(sheet);
別解2. 既存の CSSStyleSheet にルールを追加する
CSPが効いていようと、既にドキュメントに関連付けられている CSSStyleSheet には(同一オリジンなら)ルールを追加できる。
// 同一オリジンで最も後ろにあるスタイルシートを探す const usableSheet = [...document.styleSheets].filter(x => x.href?.startsWith(location.origin)).slice(-1)[0]; usableSheet.insertRule('#foo { display: none }`, usableSheet.cssRules.length); usableSheet.insertRule('#bar { cursor: pointer }`, usableSheet.cssRules.length);
@media
などのat-rulesも書ける。惜しいのは複数のルールをまとめて追加できないことだけども、条件が常に満たされるようなグループに入れてしまえば大抵いけそうではある。
const usableSheet = [...document.styleSheets].filter(x => x.href?.startsWith(location.origin)).slice(-1)[0]; usableSheet.insertRule(`@supports (display:block) { #foo { display: none } #bar { cursor: pointer } }`, usableSheet.cssRules.length);
あとがき
最終的にきっかけとなったUserScriptは <style>
→ adoptedStyleSheets → insertRule の順に試すようにした。ただこれだと一度style要素を挿入してみる際にコンソールにエラーが出力されてしまうので、邪魔と言えば邪魔。サイレントに確認する方法を探るか、あるいはその方法はスキップしてしまってもいいかもしれない。