ツール開発講座 〜第8回 XPathで複数要素を取得してみよう〜 | 元溥帝のまったりブログ

元溥帝のまったりブログ

ブラウザ三国志 溥帝○○翼賛会の公式ブログのようです。

今日はXPathを使って複数の要素を取得する方法を紹介します。

前回まで使ってきた
var xpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr[3]"
var hogehoge = document.evaluate(xpath,document,null,9,null).singleNodeValue
この方法だと、xpathで指定される3番目のtr要素が取得出来ました。

今回紹介するのはこんな書き方です。
var xpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr"
var hogehoge = document.evaluate(xpath,document,null,7,null)
evaluateのカッコの中の数字が9から7に変更になって、末尾のsingleNodeValueがなくなっています。
(xpathも最後の[3]がなくなっています)
これでxpathが".//*[@id='gray02Wrapper']/table[3]/tbody/tr[番号]"
になってる全ての要素を取得できます。

要素はsnapshotというものに順番に入っていて、使いたい時は
hogehoge.snapshotItem(番号)
という感じで取得します。

ここで注意しなくてはいけないのは
snapshotItemの最初の番号は0という事です。
一方、一番目のtr要素はxpath表記だと".//*[@id='gray02Wrapper']/table[3]/tbody/tr[1]"
です。

言葉で聞いていてもよくわからないと思いますので、例を見ながら慣れて行きましょう。

同盟順位一位の人のxpathは".//*[@id='gray02Wrapper']/table[3]/tbody/tr[3]"
ですが、
これを取得する場合
var xpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr"
var hogehoge = document.evaluate(xpath,document,null,7,null)
var rank1Tr = hogehoge.snapshotItem(2)
となります。
tr[3]を取りたいからsnapshotItem(2)
xpath表記の番号よりsnapshotItemの番号は1つ少ないんですね。
・・・なんかヤヤコシイですが、そんなもんなんだと諦めるしかありません><

さて、snapshotItemの数はsnapshotLengthで取得できます。

まぁとにかく例を見てください。
前回のコードを、これをつかって書き換えてみます。

まずは自分の名前を変数に代入。
var myName = "ぷり☆けつ"

次に同盟ランクの全ての行を取得。
var rowXpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr"
var row = document.evaluate(rowXpath,document,null,7,null)

行数を取得
var len = row.snapshotLength

各行で君主名とmyNameが同じか調べるためにforループを作る。
for(var i=3; i<len-1; i++){

}
基本的に前回と同じですが、行数を予め調べた変数が入ります。
lenではなくてlen-1って言うのがちょっとヤヤコシイですね^^;

forループの中身
 var xpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr["+i+"]/td[2]/a"
 var target = document.evaluate(xpath,document,null,9,null).singleNodeValue
 var userName = target.textContent
  
 if (myName == userName){
  row.snapshotItem(i-1).style.backgroundColor = "yellow"
  break
 }
前半は前回と同じですが、黄色くしたい行は既にsnapshotに入ってるので
row.snapshotItem(i-1)
としています。
このrowは
var rowXpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr"
var row = document.evaluate(rowXpath,document,null,7,null)
で取得したrowですね。

全体像
// ==UserScript==
// @name bro3-HighLightName
// @namespace http://ameblo.jp/putei1724
// @include http://*.3gokushi.jp/alliance/info.php*
// @version 1
// @grant none
// ==/UserScript==

var myName = "ぷり☆けつ"

var rowXpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr"
var row = document.evaluate(rowXpath,document,null,7,null)

var len = row.snapshotLength

for(var i=3; i<len-1; i++){
 var xpath = ".//*[@id='gray02Wrapper']/table[3]/tbody/tr["+i+"]/td[2]/a"
 var target = document.evaluate(xpath,document,null,9,null).singleNodeValue
 var userName = target.textContent
  
 if (myName == userName){
  row.snapshotItem(i-1).style.backgroundColor = "yellow"
  break
 }
}
いかがでしょうか?

1行1行追っていかないと難しいかもしれませんが、頑張ってみてください。

どうしても分からない部分があったらコメントください。
「この辺から(第何回の何行目あたりから)意味不明になってきた」みたいに具体的に言っていただければ
それに応じた補講も検討しようかと思っています。

余談。
Beyondの中身を見てみると
$x
とか
$a
とか書いてあるのを見つけられるかと思います。
これは、今回まで勉強してきたXPathによる要素取得のショートカットみたいなものです。

よく使う表現なのに、毎回document.evaluate~なんて書いてたら面倒ですし、
コードが長くなればそれだけ重くなります。
そこでこんなショートカットが発明されたんですね。

http://www.otchy.net/20090120/first-five-lines-of-greasemonkey/
こちらのブログが元なのかな?なんて思いますので、まだ理解できないかもしれませんが
斜め読みしておくと、将来幸せになれるかもしれません。

次回予告。

実はこれまで勉強してきたXPathによる情報取得方法は高度な方法です。
開発者の立場からすると非常に簡単に、そして確実に要素を取得できるので便利なのですが
場合によっては動作が重くなってしまいます。
正しい使い方をすれば、人間が体感できるほどの差は生まれない事になっているのですが
我々のような素人が下手な使い方をすると、重さが体感できてしまったりしますw
実際今回のコードも環境によっては重さを体感できちゃいます。

そこで次回は違う方法で要素を取得して、このツールを軽量化してみたいと思います。


 


追記。
「XPathによる要素取得は遅い」という印象を持たれるような書き方をしてしまいましたが
これは正確性に欠ける表現かもしれません。

このブログで紹介したXPathの表記方法よりも、もっと効率のいい書き方が実はあって
そのような書き方をすれば、遅いなんてことは無いのだろうと思います。
効率のいいXPath表記方法については、私の技量の問題もあってこの企画中では紹介できません。

また、高速化を目的として次回紹介する取得方法からXPathによる取得方法への切り替えを勧めているブログもありますので
正確性にかけるどころか、もしかしたら間違っているのかもしれません。

私なりの速度検証はもちろん行なっていますが、ここでそれについての議論をするつもりはありません。
次回紹介する方法も学習した後、皆さんの判断で最適と思われる方法をその都度選択していただけたらと思います。