学ぶ / チートシート
XPath と CSS セレクターのチートシート
Selenium、Playwright、ブラウザの DevTools で使える XPath と CSS を並べて比較します。表現できるなら CSS を使いましょう。短く、開発者にもなじみのある構文です。表示テキストでマッチさせたいときや、親・祖先要素へさかのぼりたいときは XPath を使います。
XPath 1.0(ブラウザが実装しているバージョン)、現行の Chrome・Firefox・Safari がサポートする CSS Selectors Level 4 で確認済み
基本
| 目的 | XPath | CSS |
|---|---|---|
| タグで要素を指定(どこでも) | //button | button |
| id で指定 | //*[@id='email'] | #email |
| クラスで指定(複数のうちの 1 つ) | //*[contains(concat(' ', normalize-space(@class), ' '), ' active ')] | .active |
| 直接の子 | //ul/li | ul > li |
| 任意の子孫 | //form//input | form input |
| 複数のセレクター(OR) | //h1 | //h2 | h1, h2 |
| 任意の要素 | //* | * |
属性
| 目的 | XPath | CSS |
|---|---|---|
| 属性を持つ | //button[@disabled] | button[disabled] |
| 等しい | //input[@name='q'] | input[name='q'] |
| 含む | //a[contains(@href, 'orders')] | a[href*='orders'] |
| 前方一致 | //a[starts-with(@href, 'https')] | a[href^='https'] |
| 後方一致 | //a[substring(@href, string-length(@href) - 3) = '.pdf'] | a[href$='.pdf'] |
| 2 つの条件(AND) | //input[@type='text' and @required] | input[type='text'][required] |
| いずれかの条件(OR) | //input[@type='email' or @type='tel'] | input[type='email'], input[type='tel'] |
| 否定 | //input[not(@disabled)] | input:not([disabled]) |
| テスト ID | //*[@data-testid='submit'] | [data-testid='submit'] |
XPath 1.0 には ends-with() がありません。substring() を使う書き方が移植性のある回避策です。
テキスト(XPath のみ)
| 目的 | XPath | CSS |
|---|---|---|
| テキスト完全一致(空白を正規化) | //button[normalize-space()='Sign in'] | 不可 |
| テキストを含む | //p[contains(., 'expired')] | 不可 |
| 大文字・小文字を区別せずに含む | //p[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'expired')] | 不可 |
| 自身のテキストノードのみ | //label[text()='Email'] | 不可 |
.(文字列値)には子要素のテキストも含まれます。text() は要素自身のテキストノードにしかマッチしません。normalize-space() または . を優先しましょう。
要素の関係と軸
| 目的 | XPath | CSS |
|---|---|---|
| 親 | //input[@id='email']/.. | :has(> #email) |
| セルを含む祖先の行 | //td[normalize-space()='#1043']/ancestor::tr | tr:has(td[data-id='1043']) |
| 次の兄弟要素 | //label[.='Password']/following-sibling::input[1] | label + input |
| 後続のすべての兄弟要素 | //h2/following-sibling::p | h2 ~ p |
| 前の兄弟要素 | //button/preceding-sibling::input[1] | 不可 |
| ラベルセルの隣のセル | //td[.='Diego Ramos']/following-sibling::td[1] | 不可(テキストが起点のため) |
| 特定の子要素を含む要素 | //li[.//button[@aria-label='Remove']] | li:has(button[aria-label='Remove']) |
:has() は現行の Chrome、Edge、Safari、Firefox(121 以降)で動作します。古いブラウザや一部のツールでは使えません。
位置
| 目的 | XPath | CSS |
|---|---|---|
| 最初の子の行(1 始まり) | //tbody/tr[1] | tbody tr:first-child |
| n 番目の行 | //tbody/tr[3] | tbody tr:nth-child(3) |
| 最後の行 | //tbody/tr[last()] | tbody tr:last-child |
| 最後から 2 番目 | //tbody/tr[last()-1] | tbody tr:nth-last-child(2) |
| 偶数行すべて | //tbody/tr[position() mod 2 = 0] | tbody tr:nth-child(even) |
| ページ全体で最初に一致する要素 | (//button)[1] | 不可:ツールの first() を使います |
//tr[1] は「ページ上の最初の tr」ではなく「親の中で最初の tr であるすべての tr」という意味です。ページ上の最初の 1 つなら (//tr)[1] を使います。
状態の疑似クラス(CSS)
| 目的 | XPath | CSS |
|---|---|---|
| チェック済みのチェックボックス・ラジオ | //input[@checked] (initial state only) | input:checked |
| 無効 | //button[@disabled] | button:disabled |
| 必須項目 | //input[@required] | input:required |
| 空の要素 | //td[not(node())] | td:empty |
XPath は HTML に書かれたとおりの属性を読むため、ユーザーがチェックを入れても @checked は変わりません。CSS の :checked は現在の状態に追従します。
DevTools で試す
| やりたいこと | コード |
|---|---|
| CSS、すべての一致(Console) | $$('tbody tr') |
| XPath、すべての一致(Console) | $x("//td[.='Mei Lin']") |
| Elements パネルで検索 | Ctrl+F / Cmd+F, then type a selector or XPath |
| 一致数を数える | $$('tbody tr').length |