概要
AutoPagerizeやLDRize,LDR Full FeedなどのGreasemonkeyスクリプトはページごとの構造の違いを吸収するため,XPathなどのデータをSITEINFOとしてwikiから取得しています.ですが,SITEINFOにはLDRize専用のmicroformatsについてという記事に書いた問題点があります.同じ記事中で疲れて途中でやめてしまった解決方法の提示の続きを書きます.
また,実装したものをCodeReposにあげてます.インストールした後,このブログで動作の確認ができます.まだ語彙とXSLT(語彙のページと同じ場所にリンクがある)がこれで良いのか判断付かないのでフィードバックが欲しいです.よろしくお願いします.
解決方法
で,結論から先に書くと,これらの問題の解決策は GRDDLで抽出したRDFをSITEINFOとして使う というものです.理由は以下のような点があります.(前回挙げたmicroformatsの問題点とSITEINFOの問題点が全て解決される上にプラスアルファ)
- ページを開くたびにmicroformatsの数だけ探索しなくて済む
- ページの構造を保証できる
- 値の意味を明示できる
- バリデーションができる
- wikiへの依存を少なくできる
逆に,デメリットはユーザの手間がちょっと増えることです.ページの構造に合ったXSLTがなければ作ってHTMLに1行追加する必要があります.XSLTが存在すればHTMLに1行追加だけですが.
現実的に,全てのページがGRDDLに対応してくれるとは思えないので,wikiにURLの正規表現+XPathを書く今の方法も残す予定ではあります.(wikiにURLの正規表現+XSLTのURLを書く方法もやりたい.ページを作った人がページの構造を保証するGRDDLに対して,第三者が簡単に保証する方法として.)
ページを開くたびにmicroformatsの数だけ探索しなくて済む
LDRizeはhAtomとxFolkというmicroformatsに対応していますが,これらに対応できているのは,全てのページ (非対応のページを含む)で「このページはhAtomに対応しているか」「このページはxFolkに対応しているか」を探索(XPathにマッチするかチェック)しているためです.
今は2つのmicroformatsに対応していないため2回の探索だけで済む…わけではなく,tDiaryやWordPress,blogger,PukiWiki,RandomNoteなど,他にもいろいろな構造に対して全てのページで探索しているので,とても無駄が多いのが現状です.
これが,GRDDLに対応したページならば操作は1回で済みます(link要素の探索)し,それ以上増えることもなくなります.正確には,無駄にXSLTを処理しなくてもいいように簡単な探索(こっちで見つかれば終了)と全ての探索の2回ですが.
値の意味を明示できる
link: 'h2//a[contains(@class,"l")]',
これはLDRize用SITEINFOの一部です.現在,SITEINFOの値(右側の部分)にはどのようなXPathを書くべきか,また,それはどこに書かれているか,についての決まりはありません.SITEINFOのwikiにちょろっと書いてあったり,動作を知った上で,それっぽく書いているのが現状です.なぜこれが問題かというと,前回の記事でも書いた通り,「この値も入れて!」と言う人がでてきても,実際入れてしまうと「どういう値か分からないのに誰がメンテするのか」という問題があって入れられない という勿体ないことになってしまうからです.そのためには, linkはこういう構造に対するXPathを書く とあらかじめ明示する必要があるのです.GRDDLでRDFを抽出して,それをSITEINFOとして用いると,linkにはURIの接頭辞が付いて, http://purl.org/net/ldrize/ns#link のようになります.これはURIで色々なものを表すというRDFの特性上こうなるのです.
ページの構造を保証できる
microformatsの問題点
※ クライアントを 「microformatsで構造化されたページを処理するプログラム」の意味で用います.
前回もチラっと書いたmicroformatsの問題点について,もう一度.
- rel="home"と同じ形で別の意味を定義する組織が現れた場合,どのように解釈するのか?
- クライアントはrel-homeと解釈する.でもページを作った人が別の意味で構造化していると,意図しない動作(バグ)になる.少なくともそう見える.
- 同音異義語 みたいな感じ.
- rel="home"と同じ意味で,別の組織がrel="sitetop"を定義し,更に別の組織がrel="toppage"(以下略)のようになった場合,クライアントはいくつかの定義しか利用できなくなる.
- クライアントが解釈できないのならば構造化する意味はない.
- 最近MSがwebsliceとかいうhatomの亜種を作ると言いだしたし.
- 同義語 みたいな感じ.
原因
原因はこんな感じです多分.
- class属性やrel属性などの値は自由に記述することが許されている
- 共通の構造を決めても,それを知らない人がいる(知らないから勝手に定義するし勝手に利用する)
- 「どのページにどの構造が使われているか」の保証が出来ない
- microformatsに厳密性がない
保証がないから厳密性がないわけですが.
解決方法
「どのページにどの構造が使われているか」を保証されていて,それをクライアントが解釈できれば,上記問題点は解決できます.人間が「このページのrel="home"はrel-homeだよ.別のrel="home"じゃないよ.」と教えてくれればいいわけです.で,前回も書いたように,保証された構造のための方法に,RDFaとGRDDLというのがあってどちらかによって保証されれば良いのです.両方とも最終的にはRDFというものを抽出できるようになっていて,RDFではURIで名前を決めるので重複しないから厳密ってことなのです.
- RDFaはmicroformatsと取って代わるもので,XHTMLの中で保証します.
- GRDDLはmicroformatsと一緒に使うもので,XSLTの中で保証します.
どっちでも良いのですが,エンドユーザの手間が少ないの方がみんなが(自分も)楽なので,楽な方を取ります.
- microformats+GRDDLは,一度XSLTを作成すれば後のエンドユーザはmicroformatsに対応した後に1行追加するだけです.
- RDFaはそれぞれのタグごとに名前空間を指定したりしないといけないので,GRDDLのような資源の再利用が難しいです.
というわけで,GRDDLによる解決方法を取ります.
バリデーションができる
XSLT内で条件分岐を行って以下のようにする.
- 正しい構造なら正しくRDFを抽出する
- 正しくない構造なら何も抽出しない
RDFが抽出できた時だけクライアント(LDRizeなど)を実行させるようにすると,HTML構造を変更して,かつ,XSLTも正しいものに変更した時のみ動作し,それ以外の場合(HTML構造だけを変更したときなど)はクライアントが動作しないので,誤動作も起きない.
wikiへの依存を少なくできる
それぞれのページ側にメタデータ(SITEINFO)抽出のためのルール(XSLT)の指定を行うので,wikiが落ちたり消されたりいたずらされても,被害を受けずに済む可能性が高くなります.
関連して,ページから直接SITEINFOを生成できるので,wikiからデータを削除しても良くなって,LDRizeがローカルに保存するデータが少なくなります.現状は,1度も見ないようなページ用のSITEINFOのデータも全てのユーザが全部GM_setValueでprefs.jsに保存しています.wikiのデータを減らして,ページ側でメタデータを生成できるようになれば,自分が見たページ用のSITEINFOをその場で生成してキャッシュしておくことができるようになるため,1度も見たことがないページ用のSITEINFOをキャッシュしなくて良くなります.
GreasemonkeyでGRDDL変換を行いRDFを扱う方法について
RDFとは,URIかデータ(文字列とか)を使って表現した三つ組みのことです.だいたい.具体例をあげると,このブログから抽出できるRDF/XML(XML形式で3つ組みを表したRDF)の一部は下記のような感じです.
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:ldrize="http://purl.org/net/ldrize/ns#">
<rdf:Description rdf:about="http://white.s151.xrea.com/blog/">
<ldrize:article>.//*[contains(concat(" ",normalize-space(@class)," "), " hentry ")]</ldrize:article>
</rdf:Description>
</rdf:RDF>
一応GRDDLで抽出できるこのブログの全てのRDFも見られます.これはウェブサービスとして提供されているものですが,XSLTの処理系があればRDF/XMLを出力することができます.とりあえず,GreasemonkeyからはXSLTを使ってRDF/XMLを出力できるので,そのXMLをXPathで処理しています(本当はちゃんとRDFパーサとか使うべき.Greasemonkeyからは扱えませんが拡張やuserChrome.jsからはRDFも扱えるらしい.http://developer.mozilla.org/ja/docs/RDF ).
ここで,上記のRDF例のインデントの一番深い部分を見ると,SITEINFOに良く似ています.wiki上の表現に倣って変換してみると下記のようになります.データが取得できれば,あとは今までのスクリプトと変わりはありません.
article: './/*[contains(concat(" ",normalize-space(@class)," "), " hentry ")]'
prefs.jsに保存されるキャッシュデータは↓のようになります.structuredUriRegExの正規表現にマッチするかどうかを確認して,マッチしなければGRDDL変換を行います.
[{section:\".//*[contains(concat(\\\" \\\",normalize-space(@class),\\\" \\\"), \\\" section \\\")]\",
article:\".//*[contains(concat(\\\" \\\",normalize-space(@class),\\\" \\\"), \\\" hentry \\\")]\",
view:\".//*[contains(concat(\\\" \\\",normalize-space(@class),\\\" \\\"), \\\" entry-title \\\")]//text()\",
link:\".//*[contains(concat(\\\" \\\",normalize-space(@rel),\\\" \\\"),\\\" bookmark \\\")]\",
structuredUriRegEx:\"^http://white.s151.xrea.com/blog/\",
expire:(new Date(1204137557921))
},
{...}]
- 生成されるデータはGRDDL変換を行ったページ = 自分が見たページ
- 一定期間が過ぎたキャッシュは破棄される
- 1+2 = 余計なデータは持たない
今後について
まだコードや語彙やXSLTに問題や不安が残ってるので,それらが解決したらuserscripts.orgにあげて,テスト版でなくなると思います.
…あとから気づいたけれど,同じ構造(例えばhAtom)でもサイトが違えばその数だけXPathをキャッシュしようとするから,同じXPathが沢山prefs.jsに保存されることになってしまう.要検討.