Webスクレイピングやブラウザ自動化において、特定の要素を正確に特定することは最も重要な工程の一つです。
モダンなWebサイトでは、IDやクラス名が動的に生成されたり、頻繁に変更されたりすることも少なくありません。
このような不安定な環境で、特定の要素を起点にして目的の要素を探し出す「XPathの軸(Axes)」の活用は、自動化エンジニアにとって必須のスキルとなります。
本記事では、PythonとSeleniumを組み合わせた実務で特に多用されるfollowing-siblingとparentの使い方に焦点を当て、具体的なコード例と共に詳しく解説します。
SeleniumにおけるXPath軸の重要性
WebアプリケーションのUIが複雑化する中で、要素の絶対パスによる指定はメンテナンス性の低下を招きます。
例えば、「名前」というラベルの横にある入力フォームを特定したい場合、IDが毎回変わる環境では従来のセレクタでは対応できません。
そこで、基準となる静的な要素(ラベルなど)を見つけ、そこからの相対的な位置関係で目的の要素を指定する手法が有効です。
XPathには、ノード間の家系図のような関係性を定義する「軸」という概念が存在します。
この軸を使いこなすことで、HTMLの構造変化に強い頑健なスクリプトを記述することが可能になります。
特に、同じ階層に並ぶ要素を取得するfollowing-siblingと、上位階層へ遡るparentは、実務での利用頻度が非常に高いテクニックです。
following-sibling:隣接する兄弟要素の取得
following-siblingは、現在の要素(コンテキストノード)と同じ親を持ち、かつその要素よりも後ろ(下方向)に位置する兄弟要素を選択するための軸です。
この軸は、設定画面のラベルと設定値の組み合わせや、ニュースサイトのタイトルと公開日のような、並列したデータ構造から情報を抽出する際に威力を発揮します。
following-siblingの基本構文
XPathでの記述方法は、軸名::要素名というルールに基づきます。
例えば、//dt[text()='氏名']/following-sibling::ddという記述は、「『氏名』というテキストを持つdt要素の次にあるdd要素」を意味します。
特定のタグを指定するだけでなく、*(ワイルドカード)を使用して任意のタグを選択することも可能です。
Pythonによる実践的なコード例
以下に、定義リスト(dl/dt/dd)形式のHTMLから、特定のラベルに対応する値を取得するPythonスクリプトを示します。
from selenium import webdriver
from selenium.webdriver.common.by import By
# WebDriverのセットアップ(Chromeの例)
driver = webdriver.Chrome()
# テスト用HTML構造を想定:
# <dl>
# <dt>商品名</dt><dd>高性能キーボード</dd>
# <dt>価格</dt><dd>25,000円</dd>
# </dl>
try:
driver.get("https://example.com/product-page")
# 「価格」というラベルの次にあるdd要素を取得
price_xpath = "//dt[text()='価格']/following-sibling::dd[1]"
price_element = driver.find_element(By.XPATH, price_xpath)
print(f"取得した価格: {price_element.text}")
finally:
driver.quit()
取得した価格: 25,000円
ここで注意すべき点は、following-sibling::dd[1]のようにインデックスを指定することで、直後の要素のみに限定できる点です。
インデックスを指定しない場合、条件に合致する後続の兄弟要素がすべて選択対象となります。
parent:親要素への遡及とコンテナの特定
parent軸は、現在の要素の直接の親要素を選択するために使用されます。
一見すると、子要素を探すよりも親要素を探す機会は少ないように感じられるかもしれません。
しかし、「特定のテキストを含むリンクが含まれているカード全体の要素を取得したい」といったケースでは、この親への遡及が不可欠です。
parentとancestorの違い
親要素を指定する方法には、parent::の他に、..(ドット2つ)という省略記法も存在します。
これらはどちらも直近の親を指しますが、parent::divのように記述することで、特定のタグである場合のみ親を取得するといったフィルタリングが可能です。
また、親よりもさらに上の階層(祖父やそれ以上)を探したい場合は、ancestor軸を使用します。
「特定のボタンが含まれるフォーム全体を操作したい」という場面では、parentやancestorを戦略的に使い分ける必要があります。
親要素を取得するコード例
以下のコードは、特定のボタンをクリックするために、そのボタンを包んでいる親要素のクラス名を確認する例です。
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
try:
driver.get("https://example.com/form-page")
# 送信ボタンを起点として、その親であるform要素を特定
# parent::form と指定することで、親がformである場合のみ取得
parent_form_xpath = "//button[@id='submit-btn']/parent::form"
form_element = driver.find_element(By.XPATH, parent_form_xpath)
# 親要素の属性を取得して確認
form_id = form_element.get_attribute("id")
print(f"親フォームのID: {form_id}")
finally:
driver.quit()
親フォームのID: main-registration-form
応用:軸を組み合わせて複雑な動的要素を攻略する
実際のWebサイトでは、1つの軸だけでは目的の要素に辿り着けない複雑なDOM構造が存在します。
その場合、複数の軸をチェイン(連結)させて記述することが可能です。
例えば、「特定のユーザー名が表示されている行にある、削除ボタンをクリックする」というシナリオを考えてみましょう。
テーブル構造での活用例
テーブル形式のデータでは、行(tr)の中に複数のセル(td)が並んでいます。
ユーザー名が記載されたtd要素から親のtr要素へ戻り、そこから再び子要素の削除ボタンを探すという流れをXPathで表現できます。
| ステップ | XPathの記述 | 意味 |
|---|---|---|
| 1. 基準の特定 | //td[text()='田中太郎'] | 名前のセルを見つける |
| 2. 親へ移動 | /parent::tr | その行全体を対象にする |
| 3. 目的の要素へ | //button[@class='delete'] | 行内の削除ボタンを指定する |
これらを連結した最終的なXPathは、//td[text()='田中太郎']/parent::tr//button[@class='delete']となります。
このように「アンカー(基準点)」を決めてから上下左右に移動する考え方が、動的要素の取得における鍵となります。
XPath軸を使用する際のベストプラクティス
XPathは非常に強力ですが、複雑に書きすぎると可読性が低下し、デバッグが困難になる側面もあります。
まず、軸を使用する前に、相対ロケータ(Relative Locators)の利用を検討するのも一つの手です。
Selenium 4から導入された相対ロケータは、with_tag_name("input").to_right_of(label_element)のように、直感的なメソッドで要素を特定できます。
しかし、より細かい条件分岐や、多階層にわたる検索が必要な場合は、依然としてXPath軸の方が柔軟性に優れています。
XPathを記述する際は、「どの要素が最も変化しにくいか」を見極め、それを起点に最小限の軸移動で記述することを心がけてください。
また、ブラウザの開発者ツール(F12)のコンソールパネルで、$x("//your/xpath")を実行して、事前に正しく要素が取得できるかテストすることを推奨します。
まとめ
本記事では、PythonとSeleniumを用いたWeb自動化において、XPathのfollowing-siblingとparent軸を活用する方法を解説しました。
following-siblingは、ラベルとデータのペアを取得する際に非常に効率的であり、parentは特定の要素を含むコンテナ全体を操作する際に欠かせません。
これらの軸をマスターすることで、IDやクラス名に依存しない、変化に強い柔軟なセレクタを書くことができるようになります。
複雑なDOM構造を持つモダンなWebサイトのスクレイピングに苦戦している方は、ぜひ今回の手法を取り入れてみてください。
正確な要素特定は、自動化スクリプトの実行成功率を飛躍的に向上させる第一歩となるはずです。
