XPath関数とは?XML/HTML要素を自在に操るための関数群を徹底解説

XMLやHTMLからデータを取り出す際に使われるXPath(XML Path Language)。このXPathをさらに強力にするのがXPath関数です。

XPath関数を使えば、単純な要素の選択だけでなく、文字列の操作数値計算条件判定など、様々な処理を組み合わせて複雑な検索ができるようになります。まるでプログラミング言語の関数のように、XMLデータを自在に操れるんです。

この記事では、XPath関数の基本から主要な関数の使い方、実践的な応用例まで、初心者の方にも分かりやすく解説していきます。Webスクレイピングやデータ処理の効率が格段に上がりますよ!


スポンサーリンク
  1. XPath関数とは?データ処理を強化する機能
    1. 基本的な使い方
    2. XPath関数の特徴
  2. サンプルXML:関数の実例で使用
  3. XPath関数の分類:4つの主要カテゴリ
    1. 1. ノードセット関数(Node-set Functions)
    2. 2. 文字列関数(String Functions)
    3. 3. 数値関数(Number Functions)
    4. 4. 真偽値関数(Boolean Functions)
  4. ノードセット関数:要素の集合を操作
    1. count() – ノード数を数える
    2. position() – 現在の位置番号
    3. last() – 最後の位置番号
    4. id() – ID属性で検索
    5. local-name() – ローカル名を取得
  5. 文字列関数:テキストデータを自在に操作
    1. contains() – 部分文字列の検索
    2. starts-with() – 前方一致判定
    3. substring() – 部分文字列の抽出
    4. substring-before() / substring-after() – 区切り文字での分割
    5. concat() – 文字列の連結
    6. string-length() – 文字列の長さ
    7. normalize-space() – 空白の正規化
    8. translate() – 文字の置換
  6. 数値関数:計算と集計を行う
    1. number() – 数値への変換
    2. sum() – 合計の計算
    3. floor() – 切り捨て
    4. ceiling() – 切り上げ
    5. round() – 四捨五入
  7. 真偽値関数:条件判定を行う
    1. boolean() – 真偽値への変換
    2. not() – 否定
    3. true() / false() – 定数値
  8. XPath 2.0以降の新機能:より強力な関数群
    1. 文字列関数の追加
    2. 数値関数の追加
    3. 日付・時刻関数
    4. シーケンス関数
  9. 実践的な使用例:複雑な検索を実現
    1. 例1:大文字小文字を無視した部分一致検索
    2. 例2:価格範囲で絞り込み
    3. 例3:複数条件の組み合わせ
    4. 例4:空でない説明文を持つ本
    5. 例5:著者の姓でソート(取得後に処理)
    6. 例6:特定の単語数以上のdescription
    7. 例7:価格の合計と平均
    8. 例8:最も高い本と安い本
  10. 関数の組み合わせ:複雑な処理を実現
    1. パターン1:条件の入れ子
    2. パターン2:計算結果での絞り込み
    3. パターン3:文字列操作の連鎖
    4. パターン4:動的な閾値
    5. パターン5:複数属性の評価
  11. Javaでの実装例:実際のコード
    1. 基本的な使い方
    2. カスタム関数の登録(XPath 2.0以降)
  12. Pythonでの実装例
  13. パフォーマンス最適化のヒント
    1. 1. 述語の順序を最適化
    2. 2. 不要な関数呼び出しを避ける
    3. 3. descendant軸の使用を最小化
    4. 4. 位置述語を後半に
    5. 5. contains()より完全一致
  14. まとめ:XPath関数でデータ処理を強化

XPath関数とは?データ処理を強化する機能

XPath関数とは、XPath式の中で使える組み込み関数のことです。

XPath自体は、XMLやHTMLドキュメントから特定の要素を選択するための言語ですが、関数を組み合わせることで、より高度な処理が可能になります。

基本的な使い方

通常のXPath(関数なし):

//book/title

これは「すべてのbook要素の子であるtitle要素」を選択します。

関数を使ったXPath:

//book[contains(title, 'Java')]

これは「titleに’Java’という文字列を含むbook要素」を選択します。

XPath関数の特徴

1. 組み込み済み

  • 追加のライブラリ不要
  • XPathの標準仕様に含まれる
  • どの環境でも基本的に利用可能

2. 戻り値の型

  • 文字列(string)
  • 数値(number)
  • 真偽値(boolean)
  • ノードセット(node-set)

3. 引数を取る

  • 関数に応じて0個以上の引数
  • 引数の型も決まっている
  • 型変換が自動的に行われる場合も

サンプルXML:関数の実例で使用

これから説明する関数の例で使用するXMLです。

<?xml version="1.0" encoding="UTF-8"?>
<library>
    <book id="1" category="technology">
        <title>Effective Java</title>
        <author>Joshua Bloch</author>
        <price currency="JPY">4500</price>
        <publishYear>2018</publishYear>
        <description>Java programming best practices</description>
    </book>
    <book id="2" category="technology">
        <title>Clean Code</title>
        <author>Robert Martin</author>
        <price currency="JPY">3800</price>
        <publishYear>2008</publishYear>
        <description>A handbook of agile software craftsmanship</description>
    </book>
    <book id="3" category="business">
        <title>The Lean Startup</title>
        <author>Eric Ries</author>
        <price currency="JPY">2500</price>
        <publishYear>2011</publishYear>
        <description></description>
    </book>
</library>

XPath関数の分類:4つの主要カテゴリ

XPath 1.0の関数は、大きく4つのカテゴリに分類されます。

1. ノードセット関数(Node-set Functions)

ノード(要素)の集合を操作する関数です。

主な関数:

  • count():ノード数を数える
  • position():現在の位置を取得
  • last():最後の位置を取得
  • id():ID属性で要素を検索
  • local-name():ローカル名を取得
  • namespace-uri():名前空間URIを取得
  • name():修飾名を取得

2. 文字列関数(String Functions)

文字列データを処理する関数です。

主な関数:

  • string():文字列に変換
  • concat():文字列を連結
  • starts-with():特定の文字列で始まるか判定
  • contains():特定の文字列を含むか判定
  • substring():部分文字列を抽出
  • substring-before():特定文字列の前を取得
  • substring-after():特定文字列の後を取得
  • string-length():文字列の長さを取得
  • normalize-space():空白を正規化
  • translate():文字を置換

3. 数値関数(Number Functions)

数値計算を行う関数です。

主な関数:

  • number():数値に変換
  • sum():合計を計算
  • floor():切り捨て
  • ceiling():切り上げ
  • round():四捨五入

4. 真偽値関数(Boolean Functions)

条件判定を行う関数です。

主な関数:

  • boolean():真偽値に変換
  • not():否定
  • true():真を返す
  • false():偽を返す
  • lang():言語を判定

それぞれの関数を詳しく見ていきましょう。


ノードセット関数:要素の集合を操作

ノードセット関数は、要素の数や位置を扱います。

count() – ノード数を数える

構文:

count(node-set)

説明:
指定したノードセットに含まれるノードの数を返します。

例1:すべてのbook要素の数

count(//book)

結果:3

例2:特定カテゴリのbook数

count(//book[@category='technology'])

結果:2

例3:条件に合うノードを数える

count(//book[price > 3000])

結果:2(価格が3000円より高い本)

実用例:

// Javaでの使用
XPath xpath = XPathFactory.newInstance().newXPath();
Double count = (Double) xpath.evaluate(
    "count(//book[@category='technology'])", 
    doc, 
    XPathConstants.NUMBER
);
System.out.println("技術書の数: " + count.intValue());

position() – 現在の位置番号

構文:

position()

説明:
処理中のノードが何番目かを返します(1から始まる)。

例1:最初の3冊を選択

//book[position() <= 3]

例2:2番目のbookのtitle

//book[position() = 2]/title

結果:Clean Code

例3:偶数番目のbook

//book[position() mod 2 = 0]

結果:2番目と4番目(存在すれば)

注意点:

  • 位置は1から始まる(0ではない)
  • コンテキストによって位置が変わる

last() – 最後の位置番号

構文:

last()

説明:
現在のコンテキストでの最後のノードの位置を返します。

例1:最後のbook要素

//book[position() = last()]

または省略形:

//book[last()]

例2:最後から2番目

//book[position() = last() - 1]

例3:最後以外のすべて

//book[position() != last()]

実用例:最新の本を取得

//book[publishYear = //book/publishYear[not(. < //book/publishYear)]]

id() – ID属性で検索

構文:

id(object)

説明:
指定したID値を持つ要素を返します。

例:ID属性が’2’の要素

id('2')

複数のIDを指定:

id('1 2 3')

注意点:

  • DTDまたはスキーマでID属性が定義されている必要がある
  • 単なる@id属性とは異なる場合がある

local-name() – ローカル名を取得

構文:

local-name(node-set?)

説明:
要素名から名前空間プレフィックスを除いた部分を返します。

例1:最初のbookの名前

local-name(//book[1])

結果:book

例2:名前空間付き要素の場合

<ns:book xmlns:ns="http://example.com">
local-name(//ns:book)

結果:book(プレフィックスns:が除かれる)

実用例:動的な要素名の確認

//*[local-name() = 'title']

これは名前空間に関係なくすべてのtitle要素を選択します。


文字列関数:テキストデータを自在に操作

文字列関数は、XPathで最もよく使われる関数群です。

contains() – 部分文字列の検索

構文:

contains(string, substring)

説明:
最初の文字列が2番目の文字列を含むかを判定します。

例1:タイトルに’Java’を含む本

//book[contains(title, 'Java')]

結果:「Effective Java」の本

例2:著者名に’Martin’を含む

//book[contains(author, 'Martin')]

例3:大文字小文字の区別

//book[contains(title, 'java')]

結果:マッチしない(’Java’と’java’は別)

実用例:部分一致検索

String query = "//book[contains(translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'java')]";
// 大文字小文字を無視して検索

starts-with() – 前方一致判定

構文:

starts-with(string, prefix)

説明:
文字列が特定の文字列で始まるかを判定します。

例1:’Effective’で始まるタイトル

//book[starts-with(title, 'Effective')]

例2:’C’で始まるタイトル

//book[starts-with(title, 'C')]

結果:「Clean Code」

例3:通貨がJPYの価格

//book/price[@currency='JPY']

注意点:
XPath 1.0にはends-with()関数がありません(XPath 2.0以降で追加)。

substring() – 部分文字列の抽出

構文:

substring(string, start, length?)

説明:
文字列の一部を抽出します。

引数:

  • string:元の文字列
  • start:開始位置(1から始まる)
  • length:取得する長さ(省略可)

例1:最初の3文字

substring(//book[1]/title, 1, 3)

結果:Eff

例2:4文字目から最後まで

substring(//book[1]/title, 4)

結果:ective Java

例3:年の下2桁

substring(//book[1]/publishYear, 3, 2)

結果:18(2018の下2桁)

注意点:

  • 位置は1から始まる(0ではない)
  • 範囲外の指定でもエラーにならない

substring-before() / substring-after() – 区切り文字での分割

構文:

substring-before(string, delimiter)
substring-after(string, delimiter)

説明:
特定の区切り文字の前後を取得します。

例1:空白の前を取得(名前の抽出)

substring-before(//book[1]/author, ' ')

結果:Joshua

例2:空白の後を取得(姓の抽出)

substring-after(//book[1]/author, ' ')

結果:Bloch

例3:ファイル拡張子の取得

substring-after('document.xml', '.')

結果:xml

実用例:URLからドメインを抽出

substring-before(substring-after('http://example.com/path', '://'), '/')

結果:example.com

concat() – 文字列の連結

構文:

concat(string1, string2, ...)

説明:
複数の文字列を結合します。2個以上の引数が必要です。

例1:著者の姓名を整形

concat('Author: ', //book[1]/author)

結果:Author: Joshua Bloch

例2:価格と通貨を結合

concat(//book[1]/price, ' ', //book[1]/price/@currency)

結果:4500 JPY

例3:複数の要素を結合

concat(//book[1]/title, ' by ', //book[1]/author, ' (', //book[1]/publishYear, ')')

結果:Effective Java by Joshua Bloch (2018)

注意点:

  • 最低2つの引数が必要
  • 数値は自動的に文字列に変換される

string-length() – 文字列の長さ

構文:

string-length(string?)

説明:
文字列の文字数を返します。

例1:タイトルの文字数

string-length(//book[1]/title)

結果:14(”Effective Java”)

例2:長いタイトルの本を検索

//book[string-length(title) > 15]

例3:空の要素を検出

//book[string-length(description) = 0]

結果:3番目のbook(descriptionが空)

実用例:バリデーション

//book[string-length(title) > 0 and string-length(title) <= 100]

normalize-space() – 空白の正規化

構文:

normalize-space(string?)

説明:
前後の空白を削除し、連続する空白を1つにまとめます。

例1:前後の空白を削除

normalize-space('  Effective Java  ')

結果:Effective Java

例2:連続空白の削除

normalize-space('Effective    Java')

結果:Effective Java

例3:実際のXMLでの使用

<title>
    Effective Java
</title>
normalize-space(//book[1]/title)

結果:改行とインデントが削除される

実用例:クリーンなデータ取得

String title = xpath.evaluate(
    "normalize-space(//book[1]/title)", 
    doc
);

translate() – 文字の置換

構文:

translate(string, from, to)

説明:
文字列内の文字を別の文字に置換します。

引数:

  • string:元の文字列
  • from:置換元の文字セット
  • to:置換先の文字セット

例1:大文字を小文字に変換

translate('Effective Java', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')

結果:effective java

例2:数字を削除

translate('Book123', '0123456789', '')

結果:Book

例3:ハイフンをアンダースコアに

translate('hello-world', '-', '_')

結果:hello_world

実用例:大文字小文字を無視した検索

//book[contains(
    translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),
    'java'
)]

数値関数:計算と集計を行う

数値関数は、数値データの処理に使います。

number() – 数値への変換

構文:

number(object?)

説明:
引数を数値に変換します。

例1:文字列を数値に

number('4500')

結果:4500(数値型)

例2:要素の値を数値に

number(//book[1]/price)

結果:4500

例3:変換できない場合

number('abc')

結果:NaN(Not a Number)

実用例:数値比較

//book[number(price) > 3000]

sum() – 合計の計算

構文:

sum(node-set)

説明:
ノードセット内のすべての値を数値に変換して合計します。

例1:すべての本の価格合計

sum(//book/price)

結果:10800(4500 + 3800 + 2500)

例2:特定カテゴリの合計

sum(//book[@category='technology']/price)

結果:8300

例3:条件付き合計

sum(//book[publishYear > 2010]/price)

実用例:平均値の計算

sum(//book/price) div count(//book)

結果:3600(平均価格)

floor() – 切り捨て

構文:

floor(number)

説明:
数値を整数に切り捨てます(小数点以下を削除)。

例1:小数の切り捨て

floor(3.7)

結果:3

例2:平均価格の切り捨て

floor(sum(//book/price) div count(//book))

結果:3600

例3:負の数の場合

floor(-2.3)

結果:-3(より小さい整数)

ceiling() – 切り上げ

構文:

ceiling(number)

説明:
数値を整数に切り上げます。

例1:小数の切り上げ

ceiling(3.1)

結果:4

例2:ページ数の計算

ceiling(count(//book) div 10)

結果:1(10件ごとのページ数)

例3:負の数の場合

ceiling(-2.7)

結果:-2(より大きい整数)

round() – 四捨五入

構文:

round(number)

説明:
数値を最も近い整数に丸めます。

例1:四捨五入

round(3.5)

結果:4

例2:切り捨てられる場合

round(3.4)

結果:3

例3:平均価格の四捨五入

round(sum(//book/price) div count(//book))

結果:3600

注意点:

  • 0.51に丸められる(偶数への丸めではない)
  • 負の数の場合、-2.5-2になる

真偽値関数:条件判定を行う

真偽値関数は、条件の判定に使います。

boolean() – 真偽値への変換

構文:

boolean(object)

説明:
引数を真偽値に変換します。

変換ルール:

  • 数値:0false、それ以外はtrue
  • 文字列:空文字列はfalse、それ以外はtrue
  • ノードセット:空はfalse、1つ以上あればtrue

例1:要素の存在確認

boolean(//book[@id='1'])

結果:true(要素が存在する)

例2:空文字列の判定

boolean(//book[3]/description)

結果:false(descriptionが空)

例3:数値の判定

boolean(0)

結果:false

not() – 否定

構文:

not(boolean)

説明:
真偽値を反転します。

例1:特定の属性を持たない要素

//book[not(@category='technology')]

例2:空でない要素

//book[not(string-length(description) = 0)]

例3:特定の文字列を含まない

//book[not(contains(title, 'Java'))]

実用例:存在しない要素の検出

//book[not(discount)]

結果:discount要素を持たないすべてのbook

true() / false() – 定数値

構文:

true()
false()

説明:
常に真または偽を返します。

例1:すべての要素を選択

//book[true()]

例2:何も選択しない

//book[false()]

例3:動的な条件式のプレースホルダー

String condition = userWantsFilter ? "price > 3000" : "true()";
String xpath = "//book[" + condition + "]";

XPath 2.0以降の新機能:より強力な関数群

XPath 2.0以降では、多くの便利な関数が追加されました。

文字列関数の追加

upper-case() / lower-case() – 大文字小文字変換

upper-case('hello')  // 'HELLO'
lower-case('WORLD')  // 'world'

例:大文字小文字を無視した検索

//book[lower-case(title) = 'effective java']

ends-with() – 後方一致

ends-with(string, suffix)

例:’.xml’で終わるファイル

//file[ends-with(@name, '.xml')]

matches() – 正規表現マッチング

matches(string, pattern)

例:メールアドレスの検証

//contact[matches(email, '^[^@]+@[^@]+\.[^@]+$')]

replace() – 正規表現置換

replace(string, pattern, replacement)

例:空白の削除

replace('hello world', '\s+', '')

結果:helloworld

数値関数の追加

abs() – 絶対値

abs(-5)  // 5
abs(3)   // 3

min() / max() – 最小値・最大値

min((1, 2, 3))  // 1
max((1, 2, 3))  // 3

例:最安値・最高値の本

//book[price = min(//book/price)]
//book[price = max(//book/price)]

avg() – 平均値

avg((1, 2, 3, 4, 5))  // 3

例:平均価格

avg(//book/price)

日付・時刻関数

current-date() – 現在の日付

current-date()

current-time() – 現在の時刻

current-time()

current-dateTime() – 現在の日時

current-dateTime()

例:今年出版された本

//book[year-from-date(current-date()) = publishYear]

シーケンス関数

distinct-values() – 重複除去

distinct-values(//book/@category)

結果:('technology', 'business')

count() の拡張

count(distinct-values(//book/@category))

結果:2(ユニークなカテゴリ数)

index-of() – 要素の位置検索

index-of(('a', 'b', 'c'), 'b')

結果:2


実践的な使用例:複雑な検索を実現

関数を組み合わせた実践的な例を見ていきましょう。

例1:大文字小文字を無視した部分一致検索

問題:
‘java’という文字列を含む本を、大文字小文字に関係なく検索したい。

XPath 1.0の解決策:

//book[contains(
    translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'),
    'java'
)]

XPath 2.0の解決策:

//book[contains(lower-case(title), 'java')]

例2:価格範囲で絞り込み

問題:
3000円以上5000円以下の本を検索。

解決策:

//book[number(price) >= 3000 and number(price) <= 5000]

または:

//book[price >= 3000 and price <= 5000]

例3:複数条件の組み合わせ

問題:
カテゴリが’technology’で、かつ2015年以降に出版された本。

解決策:

//book[@category='technology' and number(publishYear) >= 2015]

例4:空でない説明文を持つ本

問題:
description要素が存在し、かつ空でない本。

解決策:

//book[description and string-length(normalize-space(description)) > 0]

例5:著者の姓でソート(取得後に処理)

問題:
著者の姓(スペースの後)でソートしたい。

XPathでの取得:

//book[substring-after(author, ' ') = 'Bloch']

プログラム側でソート:

List<Node> books = new ArrayList<>();
// XPathで全book要素を取得
NodeList nodes = (NodeList) xpath.evaluate("//book", doc, XPathConstants.NODESET);

for (int i = 0; i < nodes.getLength(); i++) {
    books.add(nodes.item(i));
}

// 姓でソート
books.sort((a, b) -> {
    String lastNameA = xpath.evaluate("substring-after(author, ' ')", a);
    String lastNameB = xpath.evaluate("substring-after(author, ' ')", b);
    return lastNameA.compareTo(lastNameB);
});

例6:特定の単語数以上のdescription

問題:
説明文が3単語以上含まれる本を検索。

XPath 2.0での解決策:

//book[count(tokenize(normalize-space(description), '\s+')) >= 3]

例7:価格の合計と平均

問題:
すべての本の価格の合計と平均を取得。

合計:

sum(//book/price)

平均:

sum(//book/price) div count(//book)

平均(XPath 2.0):

avg(//book/price)

例8:最も高い本と安い本

最高価格の本:

//book[price = max(//book/price)]

最低価格の本:

//book[price = min(//book/price)]

XPath 1.0での代替:

//book[not(price < //book/price)]  // 最高価格
//book[not(price > //book/price)]  // 最低価格

関数の組み合わせ:複雑な処理を実現

複数の関数を組み合わせることで、より高度な処理が可能です。

パターン1:条件の入れ子

//book[
    contains(lower-case(title), 'java') and 
    number(price) > 3000 and 
    string-length(description) > 0
]

意味:

  • タイトルに’java’を含む(大文字小文字無視)
  • かつ価格が3000円より高い
  • かつ説明文が空でない

パターン2:計算結果での絞り込み

//book[price > (sum(//book/price) div count(//book))]

意味:
平均価格より高い本を選択。

パターン3:文字列操作の連鎖

normalize-space(
    substring-after(
        substring-before(//book[1]/description, 'craftsmanship'),
        'A handbook of'
    )
)

意味:
‘A handbook of’と’craftsmanship’の間の文字列を抽出し、空白を正規化。

パターン4:動的な閾値

//book[
    number(price) > (min(//book/price) + 
                    (max(//book/price) - min(//book/price)) * 0.5)
]

意味:
価格範囲の中央値より高い本を選択。

パターン5:複数属性の評価

//book[
    (@category='technology' and number(publishYear) >= 2015) or
    (@category='business' and number(price) < 3000)
]

意味:

  • 技術書で2015年以降に出版、または
  • ビジネス書で3000円未満

Javaでの実装例:実際のコード

XPath関数をJavaで使う実例です。

基本的な使い方

import javax.xml.xpath.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;

public class XPathFunctionExample {
    public static void main(String[] args) throws Exception {
        // XMLをパース
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new File("library.xml"));

        // XPathを作成
        XPathFactory xPathfactory = XPathFactory.newInstance();
        XPath xpath = xPathfactory.newXPath();

        // 例1:count関数
        Double count = (Double) xpath.evaluate(
            "count(//book[@category='technology'])", 
            doc, 
            XPathConstants.NUMBER
        );
        System.out.println("技術書の数: " + count.intValue());

        // 例2:sum関数
        Double sum = (Double) xpath.evaluate(
            "sum(//book/price)", 
            doc, 
            XPathConstants.NUMBER
        );
        System.out.println("合計金額: " + sum);

        // 例3:contains関数
        NodeList books = (NodeList) xpath.evaluate(
            "//book[contains(title, 'Java')]", 
            doc, 
            XPathConstants.NODESET
        );
        System.out.println("'Java'を含む本の数: " + books.getLength());

        // 例4:複雑な条件
        String complexXPath = "//book[" +
            "contains(translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'java') " +
            "and number(price) > 3000]";

        NodeList results = (NodeList) xpath.evaluate(
            complexXPath, 
            doc, 
            XPathConstants.NODESET
        );

        for (int i = 0; i < results.getLength(); i++) {
            Element book = (Element) results.item(i);
            String title = xpath.evaluate("title", book);
            String price = xpath.evaluate("price", book);
            System.out.println("Title: " + title + ", Price: " + price);
        }
    }
}

カスタム関数の登録(XPath 2.0以降)

// Saxon HEを使用した例
import net.sf.saxon.xpath.*;

XPathFactoryImpl xpathFactory = new XPathFactoryImpl();
XPathEvaluator xpath = (XPathEvaluator) xpathFactory.newXPath();

// カスタム関数を登録可能

Pythonでの実装例

Pythonでも同様にXPath関数を使えます。

from lxml import etree

# XMLをパース
tree = etree.parse('library.xml')

# 例1:count関数
count = tree.xpath('count(//book[@category="technology"])')
print(f"技術書の数: {int(count)}")

# 例2:sum関数
total = tree.xpath('sum(//book/price)')
print(f"合計金額: {total}")

# 例3:contains関数
books = tree.xpath("//book[contains(title, 'Java')]")
print(f"'Java'を含む本の数: {len(books)}")

# 例4:テキスト値の取得
titles = tree.xpath("//book/title/text()")
for title in titles:
    print(f"Title: {title}")

# 例5:複雑な条件
complex_xpath = """
    //book[
        contains(translate(title, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'java')
        and number(price) > 3000
    ]
"""
results = tree.xpath(complex_xpath)

for book in results:
    title = book.xpath('string(title)')
    price = book.xpath('string(price)')
    print(f"Title: {title}, Price: {price}")

# 例6:XPath 2.0関数(lxmlは1.0のみ対応)
# lower-case()などはlxmlでは使えない

パフォーマンス最適化のヒント

XPath関数を効率的に使うためのテクニックです。

1. 述語の順序を最適化

非効率:

//book[contains(description, 'programming') and @category='technology']

効率的:

//book[@category='technology' and contains(description, 'programming')]

理由:
属性チェックの方が高速なので、先に評価してノード数を減らす。

2. 不要な関数呼び出しを避ける

非効率:

//book[number(price) > 3000 and number(price) < 5000]

効率的:

//book[price > 3000 and price < 5000]

理由:
自動的な型変換に任せる。

3. descendant軸の使用を最小化

非効率:

//book//title

効率的:

//book/title

理由:
直接の子要素を指定する方が高速。

4. 位置述語を後半に

非効率:

//book[1][@category='technology']

効率的:

//book[@category='technology'][1]

理由:
絞り込み後に位置を指定する方が論理的。

5. contains()より完全一致

非効率(必要な場合):

//book[contains(@category, 'tech')]

効率的(完全一致で十分な場合):

//book[@category='technology']

理由:
完全一致の方が高速。


まとめ:XPath関数でデータ処理を強化

XPath関数は、XML/HTMLデータの処理を大幅に強化します。

この記事のポイント:

  • XPath関数は4つのカテゴリに分類される
  • ノードセット関数で要素の数や位置を扱う
  • 文字列関数でテキスト処理が自在に
  • 数値関数で計算や集計が可能
  • 真偽値関数で条件判定を実現
  • contains()やstarts-with()は頻繁に使用される
  • substring()で部分文字列を抽出できる
  • sum()やcount()で集計処理が可能
  • XPath 2.0で多くの便利な関数が追加された
  • 関数の組み合わせで複雑な処理を実現
  • パフォーマンスを意識した最適化が重要

XPath関数を使いこなすことで、WebスクレイピングやXMLデータ処理の効率が格段に向上します。

特にcontains()substring()count()sum()は実務で頻繁に使用するので、しっかりマスターしておきましょう。

関数を組み合わせることで、プログラムコードを書かずにXPath式だけで複雑な処理を実現できるのも大きな魅力です。ぜひ実際のプロジェクトで活用してみてください!

コメント

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