新しい朝が来た

みんなセレクタ登録しようぜ!って書いたのに1件も増えてなかったので現状fub専用のDBなんて俺しかメンテしないんだったらこりゃダメだな、と思ったんだけど更新履歴見たら試行錯誤してた人が1人いたので少し希望があった。
セレクタ書き方講座みたいの書いたら少しは増えるだろうか。MSN産経ニュースは俺が書いちゃったけど。


ステータスバーのアイコンダブルクリッコでuser.jsを無効にすると以後有効にしてもスクリプトエラーになってたので、一応exeだけ差し替えておきました。
http://madchester.s54.xrea.com/archives/test.zip
あとユーザースタイルシートの効きが悪い件は認識済みなのですが、IE8のバグだと思うのでちょっと保留中です。(以前のよく効く方式だと全く適用されずに違う方法で適用してます)

XPathからセレクタへの移植は簡単なお仕事です

最終的に捨てるかもしんないしあれだけど、頭の体操だと思って1個やってみたらいいじゃんすか。
ていうかこんなタイトル付けといて説明がクソ長いのでもうダメだ!!!

0. 参加資格

  • え?キミOpenID持ってないの?
  • IE8入れてないってどういうこと?

1. 移植したいSITEINFOを選ぶ

http://wedata.net/databases/AutoPagerize/items
本家DBを開いて、検索するかダラダラ眺めて決定しよう。既に書いてあるヤツ選ぶと無駄なのでセレクタ版だとページャれなくて不便やんけ、と自分で思ったとこが無難。
あと絶対にこっちの本家DBを書き換えたりするドジはしないでください(履歴が見れるので戻せますが)。

初心者にお勧めのタイプ

変換が必要なのは、pageElement と nextLink です。insertBefore もたまにあるけど普通無いです。
id("xxxx") とか xxx[@xxx=""] とかしか書いてないヤツはちょうかんたんだから最初の一歩としてはお勧めです。

2. XPathCSSセレクタの超基本的な仕様の比較

階層の表現

XPathでの階層の表現は以下の通りです。

x/y   yはxの直接の子要素。親子関係です。
x//y  途中の階層不問で、<x>〜</x>の中に<y>があれば孫だろうがひ孫だろうがひひ孫が(以下略)

セレクタでの階層の表現は以下の通りです。

x>y   「/」と同等の表現は「>」で表す。
x y   「//」と同等の表現はスペースで表す。

HTMLとにらめっこではなく、後述しますがIE8の開発者ツールを使って見るとツリー形式で見れるので直感的に分かります。
そもそも階層表現の移植と言う意味では

  • 「//」をスペースに変える。
  • 「/」を「>」に変える。

だけ憶えておけば問題ないです。

属性の指定

XPathでの属性の指定は以下の通りです。

x[@y="zzz"]    <td align="right"></td>という要素を検索するには td[@align="right"] と書きます。

セレクタでの属性の指定は以下の通りです。

x[y="zzz"]     「@」が無いだけです。ほとんど同じです。

但し、セレクタの場合、class属性だけは別の書き方をします。
ドットでタグ名とclass名を結合し、<div class="main"></div> のような要素を検索するには div.main と書きます。
タグ名を問わない場合や、他のタグで使われてないclass名の場合はタグ名を付けず、.main だけで問題ありません。
あと、属性値が完全合致ではなく、xxxを含むとかxxxで始まるとかそういうのもあります。XPathより簡単に書ける。あんまり使わないので割愛します。

IDの指定

XPathでの"main"というIDの指定は以下の通りです。

id("main")

セレクタで表現すると

#main

となります。
IDはユニークであるが故にタグ名は付けなくてもいいのですが、場合によっては必要になります。
その場合は以下のように、タグ名#ID という表現をします。

div#main
その他の細かい表現

残念なことにIE8で対応されたCSSセレクタはCSS2.1相当でCSS3から位置指定が可能な書き方が増えます。
CSS の互換性と Internet Explorer
first-childとか対応してることになってるけどエラーにならないだけでヒットしないような気がします。
あと、どうしても「最後の」という表現が無いと話にならないので、「last-of-type」だけ勝手に拡張しました。
弟(+)、兄弟(~)、末っ子(last-of-type)を駆使して位置を表現します。分かりにくいかも。

#main>p+a

id="main" のタグの子要素 p タグのすぐ後(子ではなく弟)にある a タグを表現。
隣接している、という条件ですので、弟は直後の1人だけです。間に違うタグが挟まってもヒットしません。
その代わり複数は書けるので、hrタグが挟まるなら、 p+hr+a という書き方は可能です。

#main>p~a

id="main" のタグの子要素 p タグと同階層でかつ後にある a タグ全て。
弟全員です。かなりややこしいので、上級者向けのとんち問題に使うことがあります。
目印のタグより前の a は捨てたいとか、そういう時ですかね。

#main>a:last-of-type

id="main" のタグの子要素で最後の a タグ。
SITEINFOに使うには非常に使い勝手が良い指定ですが、IE8が未対応なので、なるべく自粛してください。
「前へ」「次へ」なんかが並んでる時に使いがちなんですが、結構落とし穴があります。
最後のページで「次へ」が無くなって「前へ」を開いちゃうとか。エンドレスります。

3. 移植実践編

例1(初心者レベル):はてなダイアリー > 新着日記一覧
nextLink : //a[@rel="next"]
pageElement : //ul[@class="list-plain"]

CSSセレクタで書くと以下のようになります。

nextLink : a[rel="next"]
pageElement : ul.list-plain

自動変換のロジック書いたほうが早いぐらい簡単だから手でやろうぜ!
正直こんなのは動作確認すらしません。

例2(中級者レベル):Amazon 検索結果
nextLink : id("pagnNextLink")
pageElement : id("rightResults atfResults btfResults")
              //div[@class="listView" or @class="defaultView"]
              |id("Results")/table

pageElementが少しめんどくさいですね。
実際にサイトを確認する必要がありそうです。本家DBのexampleUrlという項目に検証用のURLが載っているので開いてみます。
IE8で開くんですよ。fubじゃダメです。
http://www.amazon.co.jp/s/?field-keywords=windows
F12を押して開発者ツールを開きます。こいつちょうべんりな新機能。fubには無い。
右上の検索ボックスに「rightResults」を入れて検索してみました。ヒットするけど完全一致しない。もっと長い名前ならあるけど…
「atfResults」も同じく。「btfResults」があった!!

下にまさしく続けてdiv class="listView"も存在します。この場合セレクタは以下のようになります。

#btfResults>div.listView

え?なんかXPathあんなに長かったのにヘンじゃね?rightResultsとか無いし?って自分するどいなー。
exampleUrlは他にも2個あります。
http://www.amazon.co.jp/s/?url=search-alias%3Dtoys&field-keywords=lego
http://www.amazon.co.jp/s/rh=n%3A13299531
Amazonさん吐き出すHTMLを統一してくれてないみたいです。ただこの2つのURLのセレクタは同じでした。

#atfResults>div.defaultView

rightResultsやResultsは見あたらなかったんで、例が無いだけかもしれませんが、古いだけかもしんないので困るまで放置します。

nextLink : #pagnNextLink
pageElement : #btfResults>div.listView, #atfResults>div.defaultView

or は基本的にカンマで区切ります。XPathみたいに途中に or は入れられない(多分)ので、単に移植すると以下のようになりますが、XPathに負けてる感が強いのでやめますね。

pageElement : #btfResults div.listView, #btfResults div.defaultView,
              #atfResults div.listView, #atfResults div.defaultView,
              #rightResults div.listView, #rightResults div.defaultView,
              #Results>table
例3(上級者レベル):Google Search

この世で最も開くと言っても過言ではないグーグル先生は最初にチャレンジしたからかもしれませんが、結構手強かったです。
しかしグーグル先生がページャれないようじゃセレクタとかクソだしマジ未来無いので避けては通れません。

pageElement : id("res")/div[ol or div]

pageElement の XPath は、id="res" の直下の div で ol か div を子要素に持つもの、という意味です。
IE8のCSSセレクタにそんなの無い!!無理です。
開発者ツールで見てみます。

id="res" 発見。
その配下の div を開いてみると、直下に ol を持ってるのが1個います。こいつですよ!!
このdivをセレクタで指定しないといけないんですが属性が全然指定されてない。無理じゃん。
無理なわけあるかGoogleに対応できないAutoPagerizeに何の意味があるっていうんだ!!
じっと見ていると、同じ階層に div が3つ。h2がすぐ上にありますね。おいおい目印発見だ!

pageElement : #res>h2+div

id="res" の子供の h2 の弟の div という書き方になりました。頭いいですね。
俺マジ冴えてんなー。続けて nextLink 行きましょう。

nextLink : id("nav")//td[last()]/a | id("nn")/parent::a

nextLink の XPath は、id="nav" の配下の一番最後の td の直下の a。もしくは id="nn" の親の a です。
だからセレクタにそんな指定無いって言ってんのにふざけるな!という気分ですが、swdyhさんから挑戦だと思って頑張ります。
負けるなCSSセレクタ
もう説明がめんどくさくなったので結論。

nextLink : #nav td.b~td.b>a

こう書いてみた。「~」は同階層の弟(隣接問わず)を表します。同じ階層に class="b" の td があって2個目のほうが「次へ」なので。
で、ここらへんとんちしまくりに疲れて、IE8では対応してないんだけど独自にCSS3の「:last-of-type」を諦めて実装した、という経緯です。
なるべく使わないほうが世のためですが、どうしようも無ければ使いましょう。試してないけど多分こんな感じで分かりやすい。

nextLink : #nav td.b:last-of-type>a
開発者ツールちょう便利
  • IEで解析したいサイトを開いて、F12を押すと開発者ツールが表示されます。
  • ある程度大きいモニタを使ってる場合はドッキングさせたほうが使いやすいです(ウィンドウの右上に切り替えボタンがある)。
  • 開発者ツールにフォーカスを当てて Ctrl+B もしくはツールバー左端の矢印ボタンを押すと、要素が選択できます。
    • nextLink を探す際に、これで「次へ」のリンクとかをクリックすると対応する要素がHTMLのツリーでも選択されるので便利です。
    • 移植でない場合に pageElement の決定に迷いがちですが、これで適当に table とか div とかを選ぶことが可能です(カーソルオーバーで枠が表示される)。
  • スクリプトタブを選択すると fubデバッグコンソールみたいのでスクリプトが実行できます。
    • alert(document.querySelector('').href); と貼り付けて、シングルクォートの中に自分で書いたセレクタを入れて実行すれば nextLink が意図通りか確認できる!!
    • 但し last-of-type は IE8では使えないので確認できない!!
    • あとIE8モードになってないとquerySelectorとか使えないので手動で切り替える必要があるサイトもあります(メニューの右端)。
その他よくあるパターン

XPathのほうによくある指定方法を解説しておくと

  • 文字列指定タイプ
//a[text()="次のページ"]

対応策無し。別の方法を模索する必要があり、現在このパターンが一番困ってます。
CSS3に相応のものが無いため、独自拡張するのもなんだよなあ、という感じで。
需要の高いサイトがどうしようも無い場合は、contains()独自拡張を検討します。

  • class名の指定がなんかややこしい
//li[contains(concat(" ",@class," "), " next ")]/a

class名に"next"を含む、という表現なんですけど、厳密に書くと

li[class~="next"]>a

なんでしょうけど意図がよく分からないので

li.next>a

で動くんじゃないすか?まあ実際のclass名見て判断しましょう。

  • following-siblingて何?
xxx/following-sibling::td[1]/a

following-siblingは「~」で大体、代用が効くと思います。

こちらを参考にするとこのXPath何がやりたいの?ってのが分かるかもしれません。
http://piro.sakura.ne.jp/latest/blosxom/mozilla/xul/2007-09-13_selector-to-xpath.htm
そもそもXPathが冗長だったり複雑すぎる場合、普通に自分で作ったほうが簡単なので、参考程度と割り切ってください。

4. DB登録に関して

現状、fub版のDBは直接ガンガン書き換えて構いません。が、逐一キャッシュ消したりしながら動作確認もそこそこめんどくさいです。
本来はスクリプト中のSITEINFOに書いて確認するのがいいのかもしれません。けど他の人と被ったりするかも。
できたなーと思ったら
http://wedata.net/databases/AutoPagerizeForSelectors/items
でアイテムの追加をしてください。
名前、url、exampleUrlは本家からそのままコピペで結構です。
ほんとキミはいい子やな〜。

5. よくやる間違いとかコツとか
  • 「前のページ」とかがトップだと出なくて2ページ目に増えてて、td>a とか書いてると1〜2ページがループになったりする。
  • 「last-of-type」を使って td>a:last-of-type とかで「次へ」を選択すると、最終ページから「次へ」のリンクが消えてて同じようにループになったりする。
  • Yahoo関係はほんと難しいのに頭絞ってセレクタ書いたらnextLinkのURLがリダイレクタでfubだとリダイレクトしないので徒労に終わったりした時のガッカリ感が異常。
  • 「1 2 3 4 5 次へ」みたいなタイプはカレントのページ番号が strong タグだったりするので、td>strong+a とかがかっこよくキマる。マジ渋い。
  • わかんなかったらTwitterのアカウント取って「@fub 助けて!」ってpostすれば相談に乗ります(followはしないけど気にしないでください)。