はじめに

Hypothes.isは、Webページ上にハイライトやコメントを付けられるオープンソースのアノテーションツールです。ブラウザ拡張やJavaScriptの埋め込みで手軽に使えますが、蓄積したアノテーションをバックアップしたい、あるいはTEI/XMLなど別の形式で活用したいケースもあります。

本記事では、Hypothes.is APIを使ってアノテーションをエクスポートし、TEI/XMLに変換する方法を紹介します。

APIキーの取得

  1. Hypothes.isにログイン
  2. Developer settings にアクセス
  3. 「Generate your API token」でAPIキーを生成

取得したキーを.envファイルに保存します。

c#p..eennvv.exampAlPeI.env
HYPOTHESIS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

アノテーションのエクスポート

APIの基本

Hypothes.is APIのベースURLは https://api.hypothes.is/api です。認証はAuthorization: Bearer <API_KEY>ヘッダーで行います。

主要なエンドポイント:

エンドポイント用途
GET /api/profile認証ユーザーのプロフィール取得
GET /api/searchアノテーション検索
GET /api/annotations/{id}個別アノテーション取得

スクリプト

エクスポートからTEI/XML変換までを1つのスクリプト hypothes_export.py にまとめています。

https://github.com/nakamura196/hypothes-export/blob/main/hypothes_export.py

以下、主要な処理を抜粋して説明します。

.envの読み込みとAPI呼び出し

ddeefflewaauirrwonipprfeeiavtiilqqtd_h__p.h_pfgk=au=areaooeerrduentprtyfaludrtvhe("mr_lu(nllie=hs+lhlr)=(iifnt:=lein:enndotiabPneelpsp"bd.javikoo.s?.erst_i=n,sie:"rreohpne.nn/e(qn(alvet/+q"u._tfian,iauAel_h:nn=vrpueusof)edipoirsttai.lran.lth.dlasnior[hl.ousestonna"yiRrr(_rte[mHpbeilr_fi.ksYo.qzoe):pls.=Ptpuaps.(ipsNOhaetepp)nltoTersin.aeirnHssto(rr.tieE.e(nrees(p)Si.u"eant"(:Isur,qdta=)S/rl)(r"]_al)f)/t,Ape"a.s=PinBsd"w1I/cee.i)_{oarcet.KedreonhsEneesdv(tYd(rpe""r"pp:(#i]oa{)"pira))(nap)tmia}s_n")kdey"}="")inline:

全アノテーションの取得(ページネーション対応)

Search APIは1リクエストあたり最大200件なので、offsetをずらして全件取得します。

deffpualortaowrerslifeolfhetoelmfstlfitcfr_isua_sluhiatellaeeraor_l=nttntelfnaen==noslflpo==o+fu_sal=rt2rt=flael_oa00aeastntlaaft0pstlen_npiiiuiit=o+anilo_lomt=no_engtni<aantg[se[stptloae"t".tiiitttu=(teo_omai(s"oxtgnito"e[sttaestinpr]eaelt.osrialn:(en(odr"d"xs)f"c](st:i]hreel"eane,srd"uc(){lhr"t"eu[,ss"uer{lro"t"wu[:ss""eru]ros)"we:sr",u]s)"elri,mi"tl"i:milti"m:itl,im"iotf,fs"eotf"f:se0t}"):offset})

実行

#p#p#pyyyJtJttShShhOoOoJoNnNnSnO+hhNhyyyTpppEooToIttEt/hhIhXee/eMssXsL__M_eeLexxxpppooorrrttt...pppyyy--jtseoin--oonnllyy
UTSSsoaaetvvraee:ldd:aJTc6SEcOItaN/:n:XynMoooLutu:rat_tpouiuusotten/prsaunntanmaetn@anhtoyitpoaonttsih.oejnsss.o.inxsm(l6annotations)

アノテーションのデータ構造

エクスポートされたJSONの各アノテーションは、W3C Web Annotation Data Modelに基づいた構造を持っています。

{}"""""""]icuutttdrsreaa{}"eeixgr:ar"tsg""]t":""ess"e:::toe{}{}{}ad""ul,,1""h"[:re""""""""""""l:at"cctsseetsetepsBct[etyttnnytnyxruU"cp"opaaddpadpaefh2ts":rerrCOer"ecffP0::]""ttof"t:"tiid2y,"::COnf:":"xxE6/hofts:1:""f-ue"t["nfae"6"::G0rx,tRtsitT16T"-2_apaaen"e67e""L-umsnite:x6xk2sp:gn"rt3t87el/ee:"0P,QiTre/Sr:ou"V1n.ee"3so,73acxl:3"itG:moae5/teT0emmc",miS 38@/ptaoe w:hplminl "3yaeranSe,3pg."i[ec.oec,n1lt4t"o[]eo2h,m1/cr7e/]dt"7sp/io,7.advr2igi["\+sev1,n0""[] 0,,1/ :]p 0/[ 0p1 ""[] ,1/ ]s "p",a,n[4]",

3種類のセレクタ

Hypothes.isは、アノテーション対象のテキスト位置を3種類のセレクタで記録しています。

セレクタ仕組み頑健性
RangeSelectorDOM上のXPathで位置を指定△ HTML構造の変更に弱い
TextPositionSelector文字のオフセット位置で指定△ テキストの増減でズレる
TextQuoteSelector対象テキスト+前後の文脈で指定◎ fuzzy matchで再アンカリング可能

元文書が変更された場合、Hypothes.isはこれらのセレクタをフォールバックとして順に試みます。TextQuoteSelectorprefix/suffixを含むfuzzy matchを行うため最も頑健ですが、対象テキスト自体が削除・大幅変更された場合はアノテーションが「orphaned(孤立)」状態になります。

TEI/XMLへの変換

エクスポートしたJSONをTEI/XML形式に変換します。

マッピング方針

Hypothes.isTEI/XML
対象文書(URI・タイトル)<sourceDesc><bibl>
文書ごとのグループ<div>
各アノテーション<ab>
ハイライトテキスト(TextQuoteSelector.exact<quote>
コメント本文<note type="annotation">
タグ<note type="tag">

変換ロジック

TextQuoteSelectorから引用テキストを抽出し、TEI要素にマッピングします。

defg"fre"oet"rt_TutetfrexaonxtrrtQgN_uesioqotefnutleoeistSniereenle(la.taentguncnaerntortnootg(trae"sattteti.ylieogpoxneena.t")cg():te"/ts=p(e=r"lete"facTirtexgox/ertst"Qu",uf,of[ti[]ex])S):e:le"c"t"or":

アノテーションをURIごとにグループ化し、<div><ab><quote> / <note> の構造で出力します。詳細はソースコードを参照してください。

出力例

<<?T/xE<<TmIt/t/Ele<te<tIxif/exb/e>vmHi<<<fito<bxelelt/p/s/iH>dd/otrnaei<tu<po<sleyi<<dd>ssdDttibpuub/oea>vha/iyi=eelitl>bri<<buDdeb<<av>o"rsetliElcbtrireecaqn/b>nh>cSlecxieliebcsrodxuon>=t>teSapcDtflec>r>moto"tm>ttoaexl>D>rltet1ptHmirtsmetee:ee.:>ytoticl>assi>tcw>0/p>neo>:rcpdyoh"/oSdnig>==prewttSde""ernewhmft=t#a=e=nwetrm"=sn<"s"c.s>ots"rn/ap2ot.m>rhc-qn=0deict-aun"2iisH-t0<1ooh6n-y0p"/lttt-gcAp"s>hBeat0=.no><:eU>tp2"ont//ahis-Uroht/dPo:2Tgteie>dn/7F/astxE"/T-nt.lafh18siiemGy3"/os>p"p:?1nl>o0>.sAet80P.h:"EIce3>x<os3p/m..op/i4r>ps2ta/7<ga7/e/7t"a2i>1+thl0ltB0etU:>ph0sP0:d"/E>/feGx"ample.com/page</ref>

元文書の変更とアノテーションの整合性

Hypothes.isのアノテーションは「スタンドオフ注釈」方式で、元文書とは別に保存されます。そのため、元文書が変更されるとアノテーションの位置がズレる可能性があります。

  • 軽微な変更 : TextQuoteSelectorのfuzzy matchにより再アンカリングされることが多い
  • 大幅な変更 : アノテーションが「orphaned」状態になり、対象箇所に紐づかなくなる

TEI/XMLにエクスポートしておけば、<quote>要素にハイライト対象テキストが記録されるため、元文書との対応関係は少なくとも記録として残ります。

まとめ

  • Hypothes.is APIを使えば、自分のアノテーションをプログラムから取得できる
  • TextQuoteSelectorexact/prefix/suffixが、アノテーション対象テキストの特定に最も重要
  • TEI/XMLへの変換により、人文学研究で広く使われるフォーマットで保存・活用できる
  • ただし元文書の変更によるアンカリングのズレには注意が必要

ソースコードはGitHubで公開しています。