XPath に文字列を埋め込むときの注意
よく、以下のように XPath に文字列を埋め込む事があります
document.evaluate('//*[@class="' + text + '"]', document, null, 7, null);
まあ、僕もよくこんなコード書くんですけど。
でも、これって
text が外部から来るものだったら、意図通りの動作をしないんですよね
たとえば、以下のような例です。
var text = '"] | /hoge/fuga/piyo | .["'; document.evaluate('//*[@class="' + text + '"]', document, null, 7, null);
というわけで
任意の文字列を XPath の式に変換する JavaScript を書いてみた
以下で試せます
コードはこちら
function escapeXPathExpr(text) { var matches = text.match(/[^"]+|"/g); function esc(t) { return t == '"' ? ('\'' + t + '\'') : ('"' + t + '"'); } if (matches) { if (matches.length == 1) { return esc(matches[0]); } else { var results = []; for (var i = 0; i < matches.length; i ++) { results.push(esc(matches[i])); } return 'concat(' + results.join(', ') + ')'; } } else { return '""'; } }
以下のように使います
document.evaluate('//*[@class=' + escapeXPathExpr(text) + ']', document, null, 7, null);
まとめ
急いでコードを書いているときは忘れがちですが。
「文脈」と「任意のデータ」があれば、やっぱりそこにはエスケープの必要性があるということを忘れてはいけませんね
フィードをオートディスカバリーする XPath
こんな感じ
document.evaluate('/html/head/link[contains(concat(" ", @rel, " "), " alternate ") and (@type = "application/x.atom+xml" or @type = "application/atom+xml" or @type = "application/xml" or @type = "text/xml" or @type = "application/rss+xml" or @type = "application/rdf+xml")]', document, null, 7, null)
ブックマークレットにしてみる
javascript:var ___r=document.evaluate('/html/head/link[contains(concat(" ", @rel, " "), " alternate ") and (@type = "application/x.atom+xml" or @type = "application/atom+xml" or @type = "application/xml" or @type = "text/xml" or @type = "application/rss+xml" or @type = "application/rdf+xml")]',document,null,7,null);for(var ___t=[],___i=0;___i<___r.snapshotLength;___i++)___t.push(___r.snapshotItem(___i).href);alert(___t.join('\n'));
でも、この辺あまり詳しくないんです><
あまり、自身ないっす
XPath を指定すると、リンクに Pathtraq のスコアを付加する関数
はじめに
Pathtraq API を使ってもっといろいろできないかなあと思って、 XPath で指定したリンクに Pathtraq のスコアを付加する JavaScript の関数を作ってみました。
Greasemonkey や Bookmarklet から使うことができます。
コード
function appendPtScore(xpath) { var self = arguments.callee; var obj = new self.PtObject(xpath); obj.timeout(); } appendPtScore.PtObject = function(xpath) { var self = this; this.id = appendPtScore.PtObject.count++; this.xpath = xpath; var result = document.evaluate(xpath, document, null, 7, null); this.elms = []; for (var i = 0; i < result.snapshotLength; i ++) { this.elms.push(result.snapshotItem(i)); } this.elmsLength = result.snapshotLength; this.elmsIndex = 0; this.script = null; appendPtScore['callback_' + this.id] = function(data) { self.callback(data) }; }; appendPtScore.PtObject.count = 0; appendPtScore.PtObject.prototype = { callback: function(data) { var self = this; document.body.removeChild(this.script); this.script = null; var a = this.elms[this.elmsIndex++]; var a2 = document.createElement('a'); a2.href = 'http://pathtraq.com/page/' + a.href; var img = document.createElement('img'); img.src = 'http://pathtraq.com/favicon.ico'; a2.appendChild(img); var span = document.createElement('span'); span.appendChild(document.createTextNode('(' + data.count + ')')); span.style.fontSize = '0.8em'; span.style.color = '#88CCFF'; span.style.fontWeight = 'bold'; a2.style.textDecoration = 'none'; a2.appendChild(span); a.parentNode.insertBefore(a2, a.nextSibling); setTimeout(function() { self.timeout() }, 1000); }, timeout: function() { if (this.elmsLength <= this.elmIndex) return; if (this.elms[this.elmsIndex].tagName.toLowerCase() == 'a') { this.script = document.createElement('script') this.script.src = 'http://api.pathtraq.com/page_counter?m=popular&callback=appendPtScore.callback_' + this.id + '&api=json&url=' + encodeURIComponent(this.elms[this.elmsIndex].href); document.body.appendChild(this.script); } else { this.elmsIndex++; this.timeout(); } } };
ライセンス
Public Domain です。改変、再配布、商用なんでもどうぞ。
Greasemonkey や Bookmarklet などにご自由にお使いください
駄文 - Selenium の中の人からメール来た><
という訳で
とりあえず、コミット権が欲しかったら username と password 送ってって言ってみた。
もし、ホントに送ってきたら id:yappo さんに転送します。
XPathGraph のテクニック
XPathGraph とは
http://xpath.kayac.com/
URL と XPath を指定すると URL の先をスクレイピングしてグラフを作ってくれるサービスです。
このエントリでは、どのような手順で XPath を組み立てていけばいいかをよくあるパターンで解説します。
0. 値の元となる要素を探す
<div class="hoge">1,234,567</div>
//div[@class="hoge"]
1. カンマを外すなど、純粋な数値に変換する
1,234,567
translate(//div[@class="hoge"], ",", "")
translate 関数は、第一引数の div を文字列に変換し、カンマを空の文字に置換します(つまり、カンマを削ります)
2. 演算する
1234567
translate(//div[@class="hoge"], ",", "") div 1000
div は割り算の演算子です。 div によって、translate の結果の文字列が数値に変換されます。
3. 小数点を消す
1234.567
floor(translate(//div[@class="hoge"], ",", "") div 1000)
floor 関数によって小数点は切り捨てられます。XPathGraph では 1.2 は 12 としてグラフにされてしまうので、必ず割り算をしたときは小数点を切り捨てるようにしておきましょう。
4. 完成!
1234
あとは、毎日グラフをチェックしてニヤニヤしましょう。
駄文 - 毎日 XPathGraph を見るのが楽しい
ちょっとずつできあがってきた
http://xpath.kayac.com/user/amachang
XPathGraph のプラス演算子が無視されるバグと回避方法
以下のエントリで
加減乗除算ができると書きましたが
XPathGraph がすごい件と、XPath で出来ることのヒント - IT戦記
現状 XPathGraph では、プラス演算子が無視されるバグがあるようです。