Selenium・Playwrightロケーター入門|CSSセレクター・XPath・data-testidの使い方を解説

Selenium・Playwright でブラウザ自動化をするとき、最初の難関は「要素をどう特定するか」です。CSS セレクターと XPath の書き方を理解すれば、テストコードの安定性が大きく変わります。本記事ではロケーターの基礎から、壊れにくい書き方のコツまで解説します。

📌 この記事の対象読者

✅ Selenium・Playwright を始めたばかりで、要素の指定方法がよく分からない方
✅ CSS セレクターと XPath の違いを整理したい方
✅ テストコードが UI 変更のたびに壊れて困っている方
✅ Playwright の新しいロケーター(get_by_role など)に入門したい方

📖 この記事を読むとできること

✔ HTML を見て CSS セレクター・XPath を自分で書けるようになる
✔ Selenium・Playwright の両方でロケーターを正しく使えるようになる
✔ 壊れにくいロケーターの書き方を選べるようになる
data-testid を活用してメンテナンスコストを下げられるようになる

筆者は複数のプロジェクトで Selenium・Playwright を使ったブラウザ自動化テストを実践してきました。
ロケーターが原因で E2E テストが不安定になるケースは非常に多く、本記事はその経験をもとに実務で使える書き方に絞って解説します。

✅ この記事の結論

  • ロケーターの優先順位は data-testid → ID → CSS → XPath の順が一般的
  • Playwright では get_by_roleget_by_text など意味のあるロケーターを使うと壊れにくい
  • 絶対 XPath・位置依存の CSS は避けると長期メンテナンスが楽になる

ロケーターとは何か

ロケーター(Locator)とは、Web ページ上の特定の要素(ボタン・テキストボックス・リンクなど)を見つけるための指定方法です。

Selenium や Playwright がブラウザを操作するとき、「どの要素をクリックするか」「どのテキストボックスに文字を入力するか」をロケーターで指定します。

# ロケーターの例(Selenium)
driver.find_element(By.ID, "login-btn")        # ID で探す
driver.find_element(By.CSS_SELECTOR, "#form input[type='email']")  # CSS で探す
driver.find_element(By.XPATH, "//button[@type='submit']")          # XPath で探す

ロケーターが壊れる=テストが失敗するという直接的な関係があるため、壊れにくいロケーターを選ぶことがテストの安定性を大きく左右します。

ロケーターの種類と使い分け

種類安定性推奨度
data-testid[data-testid="login-btn"]⭐⭐⭐ 最高🔴 最優先
ID#login-btn⭐⭐⭐ 高い🟡 推奨
name 属性[name="username"]⭐⭐⭐ 高い🟡 推奨
CSS セレクター.login-form input⭐⭐ 中程度🟡 許容
XPath(相対)//button[@type='submit']⭐⭐ 中程度🟡 許容
テキスト内容//button[text()='ログイン']⭐ 低い⚠️ 状況次第
XPath(絶対)/html/body/div[2]/form/button⭐ 最低❌ 非推奨

CSS セレクターの書き方

CSS セレクターは HTML の構造を使って要素を特定する書き方です。フロントエンド開発者には馴染みが深く、Selenium・Playwright の両方で使えます。

基本的な書き方

# HTML の例
# <input id="email" class="form-input" type="email" name="email" />

# ID で指定(# を付ける)
"#email"

# クラスで指定(. を付ける)
".form-input"

# 属性で指定([ ] で囲む)
"[type='email']"
"[name='email']"

# タグ + クラスの組み合わせ
"input.form-input"

# 親子関係(スペースで区切る:間接の子孫)
".login-form input"

# 直接の子要素(> で区切る)
".login-form > input"

ロケーター選択フロー

data-testid 属性がある?
✅ YES
→ data-testid を使う(最優先)
❌ NO → 次の条件へ ↓
固有の ID がある?
✅ YES
→ #id を使う
❌ NO → 次の条件へ ↓
Playwright で role / label で特定できる?
✅ YES
→ get_by_role / get_by_label を使う
❌ NO → 次の条件へ ↓
name・type などの属性で特定できる?
✅ YES
→ CSS セレクターを使う
❌ NO → 次の条件へ ↓
🔴 最後の手段:XPath(相対)を使う

迷ったら「ユーザーが認識できる要素」を優先する考え方が Playwright では推奨されています。画面を見てユーザーが「ログインボタン」と認識できるなら get_by_role("button", name="ログイン")、「メールアドレス欄」と認識できるなら get_by_label("メールアドレス") が自然な選択です。

# data-testid(最も安定)
"[data-testid='login-button']"

# フォームの入力欄
"input[type='email']"
"input[type='password']"
"input[placeholder='メールアドレス']"

# ボタン
"button[type='submit']"
"button.btn-primary"

# リンク
"a[href='/dashboard']"

# 部分一致(^=始まり、$=終わり、*=含む)
"[data-testid^='item-']"    # data-testid が "item-" から始まる要素
"[class*='active']"          # class に "active" を含む要素

XPath の書き方

XPath は XML・HTML ドキュメントを辿るためのクエリ言語です。CSS セレクターより表現力が高く、テキスト内容や要素の兄弟関係など CSS では難しい条件も書けます。

基本的な書き方

# //タグ[@属性='値']  ← 最も基本的な相対 XPath
"//button[@type='submit']"
"//input[@id='email']"

# テキスト内容で指定(多言語サービスでは注意)
"//button[text()='ログイン']"
"//button[contains(text(),'ログイン')]"   # 部分一致

# 複数条件(and で結ぶ)
"//input[@type='text' and @name='username']"

# 親要素から指定(./)
"//form[@id='login-form']//button[@type='submit']"

# 絶対 XPath(非推奨:HTML 構造が変わると即壊れる)
# /html/body/div[1]/main/form/button[2]  ← これは使わない

CSS vs XPath の使い分け

場面CSSXPath
ID・クラス・属性で特定できる◎ 短く書ける○ やや冗長
テキスト内容で特定したい✕ 難しい◎ text() で書ける
親・兄弟・祖先要素を辿りたい✕ 上方向は不可◎ parent:: / following-sibling:: / ancestor:: で可能
可読性・メンテナンス性◎ 見やすい△ 長くなりがち
実行速度◎ やや速い△ やや遅い
💡 実務での判断基準
ID・属性・クラスで書けるなら CSS セレクターを優先します。テキスト内容で特定する必要があるとき、または CSS では構造上書けないときに XPath を使います。どちらも書けるなら、可読性の高い方を選びましょう。

Selenium でのロケーターの使い方

Selenium 4.x では By クラスを使って要素を指定します。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://example.com/login")  # サンプルURL

# 要素を取得して操作する
email_input = driver.find_element(By.ID, "email")
email_input.send_keys("user@example.com")          # テキスト入力

password_input = driver.find_element(By.CSS_SELECTOR, "input[type='password']")
password_input.send_keys("password123")            # テキスト入力

submit_btn = driver.find_element(By.CSS_SELECTOR, "[data-testid='login-button']")
submit_btn.click()                                 # クリック

# 要素が表示されるまで待機してから取得・操作(推奨)
wait = WebDriverWait(driver, 10)
error_msg = wait.until(
    EC.presence_of_element_located((By.CSS_SELECTOR, "[data-testid='error-message']"))
)
assert "パスワードが正しくありません" in error_msg.text
⚠️ find_element の文字列指定は非推奨
Selenium 4 以降では driver.find_element("id", "email") のような文字列指定は非推奨です。必ず By.IDBy.CSS_SELECTOR などの定数を使ってください。

Selenium vs Playwright:待機戦略の違い

比較項目SeleniumPlaywright
デフォルトの待機なし(即時検索)◎ 自動待機(標準)
明示的な待機WebDriverWait を自分で書く基本不要(操作前に自動で待つ)
time.sleep 依存リスク△ 書きがち○ 少ない

Playwright でのロケーターの使い方

Playwright は Selenium より新しいロケーター API を持っており、意味のある(セマンティックな)ロケーターが使えます。HTML 構造ではなく「役割」や「テキスト」で指定するため、デザイン変更に強いのが特徴です。

Selenium vs Playwright:ロケーター機能比較

機能SeleniumPlaywright
CSS セレクター
XPath
get_by_role / get_by_text
data-testid 専用API△ CSS で代用✅ get_by_test_id
自動待機△ 明示的に書く必要あり✅ 標準で組み込み済み
Open Shadow DOM△ 追加手順が必要✅ 透過的に扱える

推奨されるロケーター

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()
    page.goto("https://example.com/login")  # サンプルURL

    # data-testid で指定(最も安定)
    page.get_by_test_id("login-button").click()

    # role + name で指定(ARIA ロールを活用)
    page.get_by_role("button", name="ログイン").click()
    page.get_by_role("textbox", name="メールアドレス").fill("user@example.com")

    # テキスト内容で指定
    page.get_by_text("ログイン").click()                # 完全一致
    page.get_by_text("ログ", exact=False).click()       # 部分一致

    # label テキストでフォーム要素を指定
    page.get_by_label("メールアドレス").fill("user@example.com")

    # プレースホルダーで指定
    page.get_by_placeholder("example@email.com").fill("user@example.com")

    # CSS セレクターで指定(従来通り使える)
    page.locator("[data-testid='login-button']").click()
    page.locator("input[type='password']").fill("password123")

    browser.close()

Playwright のロケーター優先順位

優先度ロケーター使用場面
① 最優先get_by_test_id()data-testid 属性が整備されている場合。開発チームと連携して付与するのが前提
② 推奨get_by_role()ボタン・テキストボックス・チェックボックスなど
③ 推奨get_by_label()フォームの入力欄(label タグに関連付けられているもの)
④ 許容get_by_text()テキスト内容で特定できる要素(多言語サービスは要注意)
⑤ 許容locator()(CSS/XPath)上記で書けないとき、または Selenium との共通化が必要なとき

data-testid で壊れにくいテストを作る

data-testid 属性はテスト専用の識別子で、デザイン変更やリファクタリングの影響を受けません。開発チームと協力してテスト対象の要素に付与することで、メンテナンスコストを大幅に下げられます。

<!-- HTML 側に data-testid を付与する -->
<button type="submit" data-testid="login-submit-btn">
  ログイン
</button>

<input type="email"
       data-testid="login-email-input"
       placeholder="メールアドレス" />
# Selenium での使用
driver.find_element(By.CSS_SELECTOR, "[data-testid='login-submit-btn']").click()

# Playwright での使用
page.get_by_test_id("login-submit-btn").click()
page.get_by_test_id("login-email-input").fill("user@example.com")
💡 data-testid の命名ルールを決めておくと管理しやすい
ページ名-コンポーネント名-アクション の形式(例:login-email-inputcheckout-submit-btn)で統一すると、テストコードを読んだだけで何を操作しているか分かります。
💡 data-testid はすべての要素に付ける必要はない
実務では、ボタン・入力欄・リンクなどユーザーが操作する主要なインタラクティブ要素を中心に付与するのが一般的です。装飾用の div や span などに全て付けても管理コストが増えるだけになります。

ロケーターではまりやすいポイント 7選

⚠️ ロケーターでよくある落とし穴

  1. 絶対 XPath を使ってしまう
    ブラウザの DevTools で「XPath をコピー」すると絶対 XPath が生成されることがあります。/html/body/div[2]/main/form/button[1] のような形式は HTML 構造が少し変わっただけで壊れます。必ず属性を使った相対 XPath に書き直しましょう。
  2. 動的に変わる class 名・ID を使ってしまう
    React・Vue などのフレームワークでは、ビルドのたびに class="btn-abc123" のようなハッシュ付きのクラス名が生成されることがあります。こうした値をロケーターに使うと毎回壊れます。data-testid や固定の属性を使いましょう。
  3. 複数の要素がマッチしても find_element() は最初の1件を返す
    find_element(単数)は複数マッチしても例外を出さずに最初の1件を返します。そのため意図せず別の要素を操作しているケースがあります。find_elements(複数)でマッチ件数を確認し、ロケーターをより具体的に絞り込む習慣をつけましょう。
  4. iframe の中の要素を取得できない
    iframe の内部にある要素は、そのまま find_element しても取得できません。Selenium では driver.switch_to.frame()、Playwright では page.frame_locator() で切り替えが必要です。
  5. 要素が表示されていないのに操作しようとしている
    ページ読み込み中や非同期通信中に要素を取得すると ElementNotInteractableException が発生します。Selenium では WebDriverWait、Playwright では自動待機が効きますが、隠れた要素には追加の対応が必要です。
  6. テキストで指定したロケーターが多言語対応で壊れる
    //button[text()='ログイン'] は、英語表示のとき 'Login' になって失敗します。多言語サービスではテキストではなく data-testid や role を使いましょう。
  7. Shadow DOM の中の要素を取得できない
    Web Components や一部のライブラリでは Shadow DOM が使われています。Playwright は Open Shadow DOM を標準で透過的に扱えるため、多くの場合は通常の page.locator() をそのまま利用できます。ただし Closed Shadow DOM は取得できません。Selenium では Shadow DOM の操作に追加の手順が必要です。

よくある質問(FAQ)

Q. ロケーターを自動生成するツールはありますか?

Playwright Codegenplaywright codegen https://example.com)を使うと、ブラウザを操作しながらロケーターを自動生成できます。生成されたコードの出発点として使い、必要に応じて data-testid ベースに書き換えるのがおすすめです。Chrome DevTools のコンソールでも document.querySelector('.btn')$x('//button') でその場で試せます。

Q. CSS セレクターと XPath はどちらが速いですか?

一般的に CSS セレクターの方がわずかに速いとされています。ブラウザのレンダリングエンジンが CSS に最適化されているためです。ただし実際のテスト実行時間への影響は体感できないほど小さく、パフォーマンスより可読性・安定性を優先してロケーターを選ぶ方が重要です。

Q. CSS セレクターと XPath、どちらを覚えれば十分ですか?

CSS セレクターを先に覚えることを推奨します。可読性が高く・書きやすく・実務でも頻繁に使います。XPath は「テキストで要素を特定したい」「親要素を辿りたい」といった CSS では難しいケースに使います。両方少しずつ覚えると実用的です。

Q. DevTools で CSS セレクター・XPath を確認する方法は?

Chrome DevTools でページを開き、要素を右クリック→「検証」で HTML を確認できます。DevTools のコンソールで document.querySelector('#email') と入力すると CSS セレクターをその場で試せます。XPath は $x("//button[@type='submit']") で確認できます。

Q. Playwright の get_by_role で指定できる role の種類は?

ARIA ロールに基づいており、buttontextboxcheckboxlinkheadingcomboboxlistbox などが使えます。ほとんどの HTML 要素には対応するロールがあります。詳細は Playwright 公式ドキュメントの「getByRole」を参照してください。

Q. data-testid を付けてもらうには開発チームにどう依頼すればよいですか?

「テスト自動化のために要素に識別子が必要です。data-testid 属性を主要なインタラクティブ要素(ボタン・入力欄・リンクなど)に付与してください」と依頼します。data-testid はテスト専用属性でプロダクションコードの動作に影響しない点も強調すると受け入れられやすいです。命名規則をチームで決めておくとスムーズです。

Q. Selenium と Playwright ではどちらのロケーターが使いやすいですか?

新規プロジェクトなら Playwright の方がロケーターの表現力が高く、壊れにくいテストを書きやすいです。get_by_roleget_by_test_id のようなセマンティックなロケーターが標準で使えます。ただし既存プロジェクトで Selenium を使っている場合は data-testid × CSS セレクターで十分な安定性を確保できます。

まとめ

ポイント内容
ロケーターの優先順位data-testid → ID → CSS → XPath(相対)
CSS セレクター#id・.class・[属性]・親子関係。可読性が高い
XPathテキスト・親要素など CSS で書けない条件に使う。絶対 XPath は非推奨
Playwright ロケーターget_by_test_id → get_by_role → get_by_label の順で優先
data-testid開発者と連携してテスト専用属性を付与。デザイン変更に強い
避けるべきもの絶対 XPath・動的クラス名・位置指定(div[2] など)

ロケーターの選び方を改善するだけで、テストの安定性は大きく変わります。
まず手持ちのテストコードで絶対 XPath や動的クラス名を使っているロケーターを探して、data-testid や属性ベースのロケーターに書き換えるところから始めてみましょう。

ロケーターが安定すれば、次のステップとして Page Object Model(POM)を使ったテストコードの整理に取り組むとさらに保守しやすくなります。

タイトルとURLをコピーしました