XPathGraph がすごい件と、XPath で出来ることのヒント
XPathGraph とは
http://xpath.kayac.com/
URL と XPath を指定すると一日に一回その URL をスクレイピングして XPath 式が示す値をグラフにしてくれる!という画期的なサービスです。
例えば、 URL と XPath を指定するだけで以下のようなグラフが作れてしまいます。
本当に楽しいことが出来そうでワクワクしてます!
でも
まだ XPath を登録している人が意外と少ないので、「ひょっとして、このサービスの使いどころが分からないのかなあ。」と思いました。
というわけで
XPath で出来ることのヒントを少し紹介したいと思います。
足し算、引き算、かけ算、割り算
XPath では普通に数値の演算ができます。
たとえば、 //div[@class=counter] で取得してきた div 要素が 1000 という数値を持っていたとすると
2 * (//div[@class=counter] + 200)
というようにその数値を計算式にそのまま突っ込むことができます。
ただし、割り算は / ではなく、 div を使います。
//div[@class=counter] div 3
余りを求める演算は mod を使います。
//div[@class=counter] mod 3
これを使えば、例えば単位を変換してグラフにするということも簡単にできますね。
便利な関数郡
XPathGraph はグラフを作るサービスなので、コンテンツから数値を探してこなければならないように思えますが、実はそうとも限りません。
たとえば、以下のようなものをグラフにすることもできそうです。
count を使ってそのページの全要素数をグラフにする。
count(//*)
count を使って特定の要素の数を調べる
count(//ul[@class="userlist"]/li)
string-length を使ってそのページの文字数をグラフにする。
string-length(//*)
具体例としては http://coderepos.org/share/wiki のページで以下の XPath 式を使うと、 CodeRepos のコミッター数をグラフにできます。
count(id("Committers")/following-sibling::ul/li)
などなど
XPath 式は単純なノード取得をするための式ではなく、文字列の操作、数値の演算もできるのです。
ですので、いろいろと考えれば、 XPathGraph の可能性は無限に広がっていくと思います。
まとめ
XPathGraph すごいです!
みなさんも面白いアイデアがあれば何か作ってみてはいかがでしょうか
JavaScript-XPath を IE8 に対応させました。
JavaScript-XPath をバージョンアップしました
久しぶりのバージョンアップです。よろしくお願いします><
JavaScript-XPath とは
JavaScript で書かれた XPath の実装です
以下からダウンロードできます。
http://coderepos.org/share/wiki/JavaScript-XPath
JavaScript-XPath の jQuery 用のプラグインと Prototype.js 用のプラグインを公開しました
以下のサイトからダウンロードしてお使いください。
使いにくいところがあれば
すぐになおしますので、コメントやブックマークなどで教えていただければ嬉しいです。
XPath のテストコードが WebKit(Safari のレンダリングエンジン)のリポジトリに取り込まれました!
という訳で
引き続き JavaScript-XPath をよろしくお願いいたします!
CodeRepos のコミッターの Icon は JavaScript-XPath を使って挿入しています。
http://coderepos.org/share/wiki
このように、既存の HTML に手を加えることなく、 JavaScript による機能追加ができます。
IE で見てもそれなりに実用的な速度で動いていると思いませんか><(欲をいうともうちょっと最適化したいですが。。)
アッピールアッピール
XPath は jQuery や Prototype.js と競合する技術ではなく、むしろ共存する技術です
これらのコメントから
おそらく、「XPath が jQuery や YUI、Prototype.js、Dojo、MochiKit などの汎用 JS ライブラリと競合する」と思ってる方が多いのかなと思いました。
結論
XPath は汎用 JS ライブラリとは競合する技術ではなく、共存する技術だと僕は考えています。
理由
汎用的な JS のライブラリには、大きく以下のような機能があります。
なので
今、 jQuery や Prototype.js にどういう風に XPath を組み込んで(バインディングして)使うのがキレイかを考えています。
例えば、 jQuery なら
$('//form') .xpath('.//input[@checked="checked"]') .addClass('checked') .end() .xpath('.//input[not(@checked="checked")]') .addClass('checked') .end()
このような使い方が出来るようなバインディングを提供すべきでしょうし、
また、 Prototype.js や Scriptaculous なら
Event.observe('//div/a[@href="http://coderepos.org/share"]', 'click', function(e) { /* event handler */ }) $x('//div').invoke('visualEffect', 'opacity', { from: 1, to: 0 });
このような使い方が出来るようなバインディングを提供すべきでしょう。
XPath が今までの要素取得機能と比べて何が良いのか
ここまで、 XPath が汎用 JS ライブラリと競合するものではなく共存するものだと述べてきましたが、共存させるメリットとは何なのでしょうか。
XPath は、ある要素(やノード)があった場合に id や class の有無に関わらず確実に一つまたは複数の要素を特定出来る。
多くのライブラリでは「CSS セレクタ」を使っていますが、 CSS セレクタには
- 親を遡るセレクタを記述出来ない
- 数式や文字列の結合や比較などのプログラミング的な機能がない
などの問題があります。
その点 XPath では、それらの機能を駆使して確実に要素を特定することができます。
XPath は XPath 1.0 として、DOM から使う方法は DOM 3 XPath として、それぞれ標準化されている*1
現状の汎用ライブラリの要素取得機能は、ほぼ標準的な「CSS セレクタ」を使っていますが、その実装はかなりまちまちで、独自的なものになっています。
その点 XPath の場合は、一つの XPath だけを覚えていればいいのです。
また、要素取得機能に関しては特定ライブラリに依存しなくなり、他のライブラリへの移行コストが少なくなります。
XPath がネイティブ実装されているブラウザでは非常に高速である。
ネイティブ実装されている XPath は非常に高速です。汎用ライブラリのそれと比べても数倍〜の速度です。
そして、現在ネイティブ実装されていないブラウザでも、将来ネイティブに実装される可能性はあります。
JavaScript-XPath でもそれなりに高速である。
また、そのうちキチンとしたスピードの測定を公開したいと思いますが、 JavaScript-XPath の(特に IE での)速度は他の汎用ライブラリと比べても遜色のないスピードです。
と
このように、 XPath を使うメリットは山ほどあります。
JavaScript-XPath をリリースしました!さあ、あなたも XPath を使おう!(解説付き)
JavaScript-XPath とは
JavaScript-XPath は、 DOM 3 XPath を実装していないブラウザに対して、実用的な速度で動作する DOM 3 XPath のエンジンを追加します。
一言で乱暴に言ってしまえば、どのブラウザでも document.evaluate って関数で XPath 使えるようになるよ!ってことです。
以下が公式サイトになります。
DOM 3 XPath ってなんなの!?
めっちゃ簡単(で、ちょっとだけ適当)なDOM 3 XPath の説明をします><。
JavaScript でよく使う document.getElementById や document.getElementsByTagName って関数ありますよね?
DOM 3 XPath はそれのパワーアップ版の関数 document.evaluate のことです!
わーわー!
どのくらいパワーアップしてるかというと。。。
- document.getElementById では id 属性でしか取得できないけど document.evaluate はどんな属性の値からも要素を取得できます!
- document.getElementsByTagName では要素の名前(タグの名前)でしか取得できなかったけど、 tag が 't' から始まるやつ!とか div か p !とかできます!
- 他にも、 type 属性が text の input 要素を含んでいる form 要素を取得!とかもできます!
すごいでしょ?
でも、この便利な関数って一部のブラウザでしか使えないのです。。そこで、 JavaScript-XPath を使うとすべてのブラウザで DOM 3 XPath が使えるようになるんです。
じゃあ、 document.evaluate には何を渡せばいいの?
ここで、 XPath の登場です!
( ゚∀゚)o彡゜XPath!XPath!
document.evaluate は以下のように使います!
var result = document.evaluate( '//div', // これが XPath ! document, // どこから取得するか たとえば、 document.body とかってすると head 以下の要素は取得されない null, // 基本使わないと思っていい 7, // 結果の種類を指定する。基本は 6 か 7 でいい。 6 だったら結果がソートされない可能性がある。 null // 基本使わない ); result.snapshotLength; // 取得した要素(正確にはノード)の数。 result.snapshotItem(0); // 1 個目の要素 result.snapshotItem(1); // 2 個目の要素
ね?簡単でしょ?
XPath 覚えなきゃいけねーじゃねーか!
うん><でも、 XPath って超簡単だよ!
超基本的なチートシートだよ。
全要素 | //* | /descendant::* | |
全 div 要素 | //div | /descendant::div | |
class 属性が "hoge" な div 要素 | //div[@class="hoge"] | /descendant::div[@class="hoge"] | |
id 属性が "hoge" な要素 | id("hoge") | //*[@id="hoge"] | /descendant::*[@id="hoge"] |
title 属性が "hoge" で class 属性が "fuga" でない要素 | //*[@title="hoge" and @class!="fuga"] | /descendant::*[@title="hoge" and @class!="fuga"] | |
form 要素の 3 番目の input 要素 | //form/descendant::input[3] | /descendant::form/descendant::input[3] | *1 |
チェックされたチェックボックスの親要素 | //input[@checked="checked"]/.. | //input[@checked="checked"]/parent::node() |
JavaScript-XPath って使うの結構めんどいんでしょ?
違うよ!ぜんぜん違うよ!
超簡単だよ><
という訳で、 JavaScript-XPath を使う為の 3 ステップを教えちゃいます。
1. 最新の JavaScript-XPath をダウンロードしてくる
ここからダウンロードしてね。
http://svn.coderepos.org/share/lang/javascript/javascript-xpath/trunk/release/javascript-xpath-latest.js
2. 以下のように HTML ファイルにインポートする
<!-- こんな感じで javascript-xpath-***.js を読み込む --> <script src="javascript-xpath-latest.js"></script> <!-- ダウンロードがめんどくて、とりあえず使ってみるだけだったら直接参照してみる><(本番環境ではしないでね><) <script src="http://svn.coderepos.org/share/lang/javascript/javascript-xpath/trunk/release/javascript-xpath-latest.js"></script> -->
3. そして、 DOM 3 XPath を使う!
window.onload = function() { // div 要素全部取ってくる var r = document.evaluate('//div', document, null, 7, null); // この '//div' が XPath。これ、まめ知識ね。 // div 要素の数 alert(r.snapshotLength); // 最初の div 要素 alert(r.snapshotItem(0)); // 2 番目の div 要素 alert(r.snapshotItem(1)); };
ほら、できちゃったでしょ?
わいわいがやがや
まとめ
ぜひぜひ、 JavaScript-XPath を使ってください!
この冬も XPath が熱い!
[余談] でも、 JavaScript-XPath にもバグとかあるんじゃないの?
JavaScript-XPath ではある程度のテストはしており、基本的にはバグは少ないと思います。
でも、人が作るものなので、少しのバグはあると思います><
ですので、あなたの力を貸してください!!
JavaScript-XPath では以下のようなテストを行っています
http://svn.coderepos.org/share/lang/javascript/javascript-xpath/trunk/test/functional/index.html
JavaScript-XPath にあなたのテストファイルを提供してください!
ここ にあなたのサイトの HTML と XPath と 結果を書いたテストデータを提供してください><
そうすれば、あなたの XPath がテストに組み込まれ、半永久的にサポートされることでしょう><
テストデータは、 CodeRepos のコミット権を取得すれば誰でも提供する事ができます。
テストデータの形式
テストデータの形式は以下のような形式です。
テストのタイトル -------- <html>HTMLの内容</html> -------- コンテキストノード(キホン的には / だけでいい) -------- XPath => 結果(CSS セレクタのように div.class#id[attributeName="attributeValue"] というような文字列を空白区切りで ) XPath => 結果 XPath => 結果 : :
具体的にはこんな感じのファイルになります。
よろしくお願いします!