うならぼ

申し訳程度のアフィリエイトとか広告とか解析とかは/aboutを参照

国立国会図書館のAPIで書誌情報を検索してみた話

書名や著者名に対する読み仮名の情報を揃えたくなったので、国立国会図書館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 レコードのスキーマを色々選べる。デフォルトの dcdcndl よりもシンプルで、カナ情報とかは入っていない。
  • maximumRecords=1 いわゆるlimit
  • query=... 検索クエリを 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.htmlhttps://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を活用してみましょう。

擬人化されていないやつは苦手です、ええもちろん。

*1:exactは=同様の演算子的なもの(relation)だが、 ^ は検索キーワードに使うメタ文字なので他のrelationと組み合わせて使わないといけない

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#sheetnull になっていることで判別ができる(普通は 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要素を挿入してみる際にコンソールにエラーが出力されてしまうので、邪魔と言えば邪魔。サイレントに確認する方法を探るか、あるいはその方法はスキップしてしまってもいいかもしれない。