CSS セレクター学習ノート¶
このノートは CSS のセレクター仕様について簡単に記す。スクレイピングに関する技術でXPath に並んで重要なものだ。ここに書いてあることを習得することで、スクレイピング以外にも CSS コードをエレガントに記述することもできるようになる。
練習方法¶
ブラウザーの開発ツールのコンソールを利用するか、Scrapy のシェルを利用する。
XPath 学習ノート の XPath 練習方法の節に記したものと同じ方法が使える。違いは次の通り:
ブラウザーの開発用コンソールでは関数
$x()
の代わりに関数$()
や$$()
を用いる。Scrapy シェルではメソッド
.xpath()
の代わりにメソッド.css()
を用いる。
いずれも引数は CSS セレクターを表す文字列とすればよい。
他にも BeautifulSoup を使う方法や、HTML-XML-utils のコマンド hxselect
を利用する方法がある。
See also
Level 1¶
セレクター仕様にはレベルという概念があるようで、数字が小さいものほど基本的な仕様となっている。
ただし、ここで E
, F
は任意の HTML 要素名を表すものとする。例えば a
,
div
, img
, h1
などだ。また、link
, visited
, active
はすべてリテラルとする。
セレクター例 |
選択要素 |
---|---|
|
文書中の |
|
文書中の |
|
文書中の |
|
要素 |
|
|
|
要素 |
|
要素 |
ここまではふつうの CSS を書くときにも頻繁に指定するものなので馴染みがある。むしろスクレイピングではこのような interactive 関連パターンは使わないはずだ。
E:active
については、何をもって要素が active であるのかを決めるのかは要素によって異なる。
仕様書によれば F
が E
の子である必要はなく、例えば孫でもひ孫でも可とある。空白文字で区切るのは子孫指定と憶えたい。
Level 2¶
レベル 2 から要素の属性に対する条件を指定できるようになる。
セレクター例 |
選択要素 |
---|---|
|
すべての要素を表す。 |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
省略。 |
|
省略。 |
|
要素 |
|
要素 |
|
要素 |
セレクター *
は単体で用いるのではなく、他のパターンと組み合わせて用いる。ただし *.warning
や *#myid
はレベル 1 仕様を用いてそれぞれ単に
.warning
や #myid
と書ける。
セレクター E[attr]
の例を一つ挙げる。h1[title]
は``title`` を属性に持つ
h1
要素すべてを指定する。
セレクター E[attr="value"]
はよく用いるだろう。
例:
span[class="example"]
例:
span[hello="Cleveland"][goodbye="Columbus"]
アルファベットの大文字小文字オプションもあるが割愛。
セレクター a[rel~="copyright"]
は要素 a
であって、属性 rel
の値が例えば "copyright copyleft copyeditor"
であるならば、それらすべてがマッチする。
セレクター a[hreflang|="en"]
がマッチするのは次のような要素だ:
<a hreflang="en"></a>
<a hreflang="en-US"></a>
<a hreflang="en-scouse"></a>
E:hover
および E:focus
はスクレイピング向けではないので、本ノートでは説明しない。
木構造に関わるセレクターは上下方向と横方向があることを意識するとよい。
セレクター E:first-child
は後述する :nth-child(1)
と同値だ。例として
div > p:first-child
を挙げる。これは要素 div
内に最初にある要素が
p
であるならば、それを指定する:
<p>The last P before the note.</p> <!-- マッチしない -->
<div class="note">
<p>The first P inside the note.</p> <!-- マッチする -->
</div>
<p>The last P before the note.</p> <!-- マッチしない -->
<div class="note">
<h2>Note</h2>
<p>The first P inside the note.</p> <!-- マッチしない -->
</div>
セレクター E > F
は要素 F
であって要素 E
の子であるものを指定する。
例:
body > p
: 要素body
の子であるような要素p
すべてを指定する。例:
div ol>li p
: 要素li
の子孫にあたる要素p
すべてを指定する。ただしそのようなli
はいずれも要素ol
の子であるものとし、さらにそのようなol
は要素div
の子孫であるものとする。
Level 3¶
Level 3 で一気にセレクターのバリエーションが増える。
セレクター例 |
選択要素 |
---|---|
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
ユーザーインターフェイス要素 |
|
上記の無効状態版。 |
|
チェックボックスまたはラジオボタン |
|
ふつうは |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
要素 |
|
唯一の要素 |
まず否定を覚えておこう。ここで sel
は有効なセレクターを表すものとする。
例:
button:not([DISABLED])
: 要素button
のうち有効状態のものすべてを指定する。例:
*:not(FOO)
:FOO
を除くすべての要素を指定する。例:
html|*:not(:link):not(:visited)
: これは宿題とする。
次はレベル 2 で習った属性セレクターの仲間だ。これらはスクレイピングで活躍しそうだ。
E[attr^="val"]
E[att$="val"]
E[att*="val"]
以上のいずれにおいても、val
が空である場合にはセレクターは何も表していないものとする。
例:
object[type^="image/"]
例:
a[href$=".html"]
例:
p[title*="hello"]
擬似クラスを含むセレクター仕様がいくつか存在する。E:target
についてはよくわからない。
全体と空。E:root
は <html>...</html>
を指定するのがふつうなので、スクレイピングではたぶん使わない。
E:empty
は要素 E
であって子要素を有しないものすべてを指す。例えば
p:empty
は <p></p>
, <p> </p>
, のようなものをすべて指定する。スクレイピングでうまい使い方がありそうな気がする。
レベル 3 の目玉と思われる、子要素を序数で指定するセレクターでは序数の指定方式にクセがある。
インデックスは 1 始まり。
even
,odd
を指定することが許される。An+B
記法というものがある。詳しくは仕様書を見たほうがいいが、これのせいでインデックスが 0 始まりでない。
スクレイピングでは表要素の何番目の列を取得するという用途が頻繁にあるので、習得必須かもしれない。
E:last-child
は要素 E
であって、その親要素の最後の子要素であるものを指定する。例えば ol > li.last-child
とすると <ol>
要素すべてに対する最後の``<li>`` 要素をすべて指す。
親要素を意識しない序数によるセレクターもある。例をまとめて挙げる:
img:nth-of-type(2n+1){ float: right; }
img:nth-of-type(2n){ float: left; }
body > h2:not(:first-of-type):not(:last-of-type){ /* ... */ }
dl dt:first-of-type { /* ... */ }
/* 各行に対して最後のセルを指定する */
tr > td:last-of-type { /* ... */ }
E:only-of-type
は唯一の要素 E
を指定する。複数存在する E
なら指定しないということなのでうまく利用できる状況があるかもしれない。
Level 4¶
レベル 4 で頭の片隅にあるといつか使うかもしれないものを。ただし、これを実装している処理系は現在私の手許にないかもしれない。少なくとも Chrome ベースの某ブラウザーではダメ。
セレクター例 |
選択要素 |
---|---|
|
引数が複数あっても構わなくなった。 |
|
要素 |
|
|
|
要素 |
|
要素であって、ハイパーリンクを表すようなものにマッチ。 |
|
要素 |
|
要素 |
|
要素 |
E:is()
の例を挙げる。
*|*:is(:hover, :focus)
: マウスが乗っているか、フォーカスが合っているような要素ならなんでもマッチ。*|*:is(*:hover, *:focus)
: デフォルトの名前空間限定で、マウスが乗っているか、フォーカスが合っているような要素ならなんでもマッチ。
E:where()
は説明が少々難しい。まず、次のコード片は期待通り働かない:
a:not(:hover) {
text-decoration: none;
}
nav a {
text-decoration: underline;
}
こういうときに :where()
を用いる。次なら期待通り働く。
a:where(:not(:hover)) {
text-decoration: none;
}
nav a {
/* Works now! */
text-decoration: underline;
}
指定度の理解をする必要がある。これについては後述する。
E:has(rs1, rs2, ..., rsn)
は例を見たほうがわかりやすい。
a:has(> img)
: 要素<a>
であって、子に要素<img>
を含むようなものにマッチ。dt:has(+ dt)
: 要素<dt>
であって、直後に別の<dt>
が続くようなものにマッチ。section:not(:has(h1, h2, h3, h4, h5, h6))
: 要素<section>
であって、いかなる<h[1-6]>
を含まないようなものにマッチ。section:has(:not(h1, h2, h3, h4, h5, h6))
: 要素<section>
であって、<h[1-6]>
のどれでもない要素を含むようなものにマッチ。
セレクター :any-link
URL を抽出するスクレイピングで使えるかもしれない。平たく言えば属性 href
のある要素にマッチする。さらに論理的には
:is(:link, :visited)
と同値。
レベル 4 にしてやっと表関連専門のセレクターが仕様に含まれる。次の例は C, E, G を灰色にする。HTML では C, E は 3 列目にあり、G は 2 列目と 3 列目にまたがっている。 G の文字は何も指定がなければ 2 列目に描画されると思う。
col.selected || td {
background: gray;
color: white;
font-weight: bold;
}
<table>
<col span="2">
<col class="selected">
<tr><td>A <td>B <td>C
<tr><td colspan="2">D <td>E
<tr><td>F <td colspan="2">G
</table>
E:nth-col(n)
にせよ E:nth-last-col(n)
にせよ An+B
記法における位置の決定方法に注意。
指定度¶
ある要素に対するセレクターの 指定度 とは、次の数からなる三組である:
そのセレクターにある ID セレクターの個数
そのセレクターにあるクラスセレクター、属性セレクター、擬似クラスの個数
そのセレクターにある型セレクターと疑似要素の個数
例:
* /* (0, 0, 0). universal selector は無視するものとする */
LI /* (0, 0, 1). HTML タグ名は型セレクターの一つ */
UL LI /* (0, 0, 2). UL の子孫であるような LI */
UL OL+LI /* (0, 0, 3). UL の子孫であるような LI であって、直前に OL が先行するもの */
H1 + *[REL=up] /* (0, 1, 1). 第 2 成分と第 3 成分はそれぞれ REL, H1 による */
UL OL LI.red /* (0, 1, 3). */
LI.red.level /* (0, 2, 1). LI 要素であって red クラスでも level クラスでもあるようなもの */
#x34y /* (1, 0, 0). id の値が x34y であるような要素すべて */
セレクターがセレクターリストであれば、その指定度はリストにあるセレクターそれぞれに対して計算される。リストに対する与えられたマッチング過程に対して、最終的な指定度はマッチするリスト内にある、もっとも具体的なセレクターの指定度である。
ただし擬似クラスのいくつかは別のセレクターに評価コンテキストを提供するので、指定度の計算法が特別なものになる。
擬似クラス
:is()
,:not()
,:has()
の指定度は、セレクターリスト引数にある最も具体的な複セレクターの指定度に置き換わる。類比的に、セレクター
:nth-child()
や:nth-last-child()
の指定度は次の指定度の和になる:擬似クラスそれ自身の指定度(一つの擬似クラスセレクターとして勘定)
(存在すれば)セレクターリスト引数にある最も具体的な複セレクターの指定度
擬似クラス
:where()
の指定度はゼロに置き換わる。
例:
:is(em, #foo)
の指定度は次のいずれかにマッチしたときに (1, 0, 0) となる:<em>
,<p id=foo>
,<em id=foo>
..qux:where(em, #foo#bar#baz)
の指定度は (0, 1, 0) となる。というのも:where()
の外部にある.qux
しかセレクターの指定度に寄与しないからだ。:nth-child(even of li, .item)
の指定度は (0, 2, 0) となる。自身の指定度
次のいずれかにマッチしたときの擬似クラスの指定度:
<li>
<ul class=item>
<li class=item id=foo>
:not(em, strong#foo)
の指定度は任意の要素にマッチしたときに (1, 0, 1) となる。この値はstrong#foo
の指定度と等しい。
指定度の順序関係は辞書式順序で定義される。左の成分同士から比較する。大きい方がより具体的である。