Open1
xpathってなんじゃい

xpathってなんじゃい
スクレイピング (selenium利用) でのxpathとは
XPathの由来
XPath (XML Path Language) は1999年にW3Cによって策定されたXMLドキュメント内のノードを選択するためのクエリ言語です。XPathは「XML Path Language」の略で、XMLドキュメントの階層構造を「パス」の概念で表現し、特定の要素を指定できます。
代表的な使われ方
- Webスクレイピング: HTMLドキュメント内の特定の要素を取得
- XML処理: XML文書の解析とデータ抽出
- テスト自動化: UI要素の特定と操作
- データ変換: XSLT(XSL Transformations)での使用
スクレイピングでXPathを知っておくべき理由
- CSSセレクタでは不可能な「親要素への遡り」「兄弟要素の取得」「テキスト内容での検索」ができる
- 動的サイトや複雑なDOM構造でも確実に目的の要素を特定できる
- 一つのXPath式で複数の条件を組み合わせた高度な検索が可能
1. 基本構文
# 絶対パス: ルートから要素まで
driver.find_element(By.XPATH, "/html/body/div/p")
# 相対パス: どこからでも該当要素を検索
driver.find_element(By.XPATH, "//p")
# 属性指定
driver.find_element(By.XPATH, "//div[@class='content']")
# 複数属性の指定
driver.find_element(By.XPATH, "//input[@type='text'][@name='username']")
# 属性値の部分一致
driver.find_element(By.XPATH, "//div[contains(@class, 'product')]")
# 属性値の前方一致
driver.find_element(By.XPATH, "//div[starts-with(@id, 'item-')]")
# 属性の存在確認(値は問わない)
driver.find_element(By.XPATH, "//img[@alt]")
# 属性が存在しない要素
driver.find_element(By.XPATH, "//div[not(@class)]")
詳細な属性指定パターン
基本的な属性マッチング
# 完全一致
driver.find_element(By.XPATH, "//div[@class='menu-item']")
# 複数の値を持つ属性(class属性など)での完全一致
driver.find_element(By.XPATH, "//div[@class='nav item active']")
# 複数属性の組み合わせ(AND条件)
driver.find_element(By.XPATH, "//button[@type='submit'][@disabled='true']")
文字列関数を使った属性マッチング
# contains(): 部分一致(最も頻繁に使用)
driver.find_element(By.XPATH, "//div[contains(@class, 'product')]")
# starts-with(): 前方一致
driver.find_element(By.XPATH, "//input[starts-with(@name, 'user_')]")
# ends-with(): 後方一致(XPath 2.0以降、一部のブラウザでサポート外)
# 代替案: substring関数を使用
driver.find_element(By.XPATH, "//img[substring(@src, string-length(@src) - 3) = '.jpg']")
# normalize-space(): 前後の空白を除去してマッチング
driver.find_element(By.XPATH, "//span[normalize-space(@title)='重要な情報']")
属性値の条件指定
# 数値比較(文字列として比較されるため注意)
driver.find_element(By.XPATH, "//input[@maxlength > '10']")
# 複数の値のいずれかにマッチ
driver.find_element(By.XPATH, "//input[@type='text' or @type='email' or @type='password']")
# 特定の値以外
driver.find_element(By.XPATH, "//div[@status != 'disabled']")
# 属性の存在チェック
driver.find_element(By.XPATH, "//img[@alt]") # alt属性が存在する
driver.find_element(By.XPATH, "//div[not(@hidden)]") # hidden属性が存在しない
data属性やカスタム属性
# data属性の取得
driver.find_element(By.XPATH, "//div[@data-product-id='12345']")
driver.find_element(By.XPATH, "//button[contains(@data-action, 'purchase')]")
# カスタム属性
driver.find_element(By.XPATH, "//section[@role='main']")
driver.find_element(By.XPATH, "//input[@aria-label='検索キーワード']")
実用的な属性指定の例
# CSSクラスが複数設定されている場合の安全な指定方法
# NG: class="nav-item active selected" に対して [@class='nav-item'] は失敗
# OK: contains関数を使用
driver.find_element(By.XPATH, "//li[contains(@class, 'nav-item')]")
# 動的に生成されるIDへの対応
driver.find_element(By.XPATH, "//div[starts-with(@id, 'product-') and contains(@id, '-details')]")
# フォーム要素の柔軟な指定
driver.find_element(By.XPATH, "//input[(@type='text' or @type='email') and @required]")
# 複数条件での絞り込み
driver.find_element(By.XPATH, "//a[@href and contains(@class, 'btn') and not(contains(@class, 'disabled'))]")
2. よく使う軸(Axes)
-
descendant::
: 子孫要素すべて -
parent::
: 親要素 -
following-sibling::
: 後続の兄弟要素 -
preceding-sibling::
: 先行の兄弟要素
# 親要素を取得
driver.find_element(By.XPATH, "//span[@class='price']/parent::div")
# 次の兄弟要素を取得
driver.find_element(By.XPATH, "//h2/following-sibling::p[1]")
3. 述語(Predicates)
# インデックス指定(1から始まる)
driver.find_element(By.XPATH, "//div[@class='item'][1]")
# 最後の要素
driver.find_element(By.XPATH, "//div[@class='item'][last()]")
# 条件指定
driver.find_element(By.XPATH, "//a[contains(@href, 'example.com')]")
4. 便利な関数
# テキスト内容で検索
driver.find_element(By.XPATH, "//button[text()='送信']")
# 部分一致
driver.find_element(By.XPATH, "//div[contains(@class, 'product')]")
# 正規化された空白でのテキスト検索
driver.find_element(By.XPATH, "//span[normalize-space()='価格']")
# 複数条件のOR
driver.find_element(By.XPATH, "//input[@type='text' or @type='email']")
# 複数条件のAND
driver.find_element(By.XPATH, "//div[@class='item' and @data-status='active']")
5. 実践的なスクレイピングパターン
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
# 商品リストから価格を取得
prices = driver.find_elements(By.XPATH, "//div[@class='product']//span[@class='price']")
# テーブルの特定行・列を取得
cell = driver.find_element(By.XPATH, "//table//tr[2]/td[3]")
# 動的に生成されるIDを持つ要素
element = driver.find_element(By.XPATH, "//div[starts-with(@id, 'dynamic_')]")
# ネストしたリストアイテム
items = driver.find_elements(By.XPATH, "//ul[@class='menu']//li[not(ul)]")
6. パフォーマンスとメンテナンス性の考慮
避けるべきパターン:
# 深いネスト(脆弱)
driver.find_element(By.XPATH, "/html/body/div[1]/div[2]/div[3]/span[1]")
# インデックスに依存しすぎ(レイアウト変更に弱い)
driver.find_element(By.XPATH, "//div[5]/p[2]")
推奨パターン:
# 意味のある属性を使用
driver.find_element(By.XPATH, "//div[@data-testid='user-profile']")
# 複数の候補を用意
xpath_candidates = [
"//button[@id='submit-btn']",
"//button[text()='送信']",
"//input[@type='submit']"
]
7. デバッグとテスト
# ブラウザの開発者ツールでXPathをテスト
# Console: $x("//div[@class='content']")
# 要素が見つからない場合のハンドリング
try:
element = driver.find_element(By.XPATH, "//div[@class='content']")
except NoSuchElementException:
print("要素が見つかりません")
XPathはHTMLの構造を理解し、柔軟に要素を特定できる強力なツールですが、サイトの構造変更に敏感なため、堅牢なセレクタを作成することが重要です。