文集文档索引

XPath


  • 文集信息
  • 目录大纲
  • 最新文档
  • 知识宇宙

文集详情

文集导读

XPath XPath 章节详解:从入门到精通的代码实践 XPath 基础:XML 结构与 XPath 语法 要理解 XPath,首先需要了解 XML 文档的结构。XML (Extensible Markup Language) 是一种标记语言,用于描述数据。它使用标签来定义文档的结构和内容,形成一个树状的节点结构。 1.1 XML 文档结构 一个典型的 XML 文档由以下部分组成: 根元素 (Root Element): XML 文档只有一个根元素,它是所有其他元素的父元素。 元素 (Element): XML 文档的基本构建块,由开始标签、结束标签和内容组成。元素可以嵌套,形成层级结构。 属性 (Attribute): 提供关于元素的额外信息,位于元素的开始标签中,以键值对的形式存在。 文本节点 (Text Node): 元素的内容,即标签之间的文本。 注释 (Comment): 用于在 XML 文档中添加注释,不会被解析器处理。 处理指令 (Processing Instruction): 为应用程序提供处理 XML 文档的指令。 命名空间 (Namespace): 用于避免 XML 文档中元素和属性名称冲突。 示例 XML 文档 (books.xml): 1.2 XPath 语法基础 XPath 使用路径表达式来选取 XML 文档中的节点或节点集。

XPath

XPath 章节详解:从入门到精通的代码实践

1. XPath 基础:XML 结构与 XPath 语法

要理解 XPath,首先需要了解 XML 文档的结构。XML (Extensible Markup Language) 是一种标记语言,用于描述数据。它使用标签来定义文档的结构和内容,形成一个树状的节点结构。

1.1 XML 文档结构

一个典型的 XML 文档由以下部分组成:

  • 根元素 (Root Element): XML 文档只有一个根元素,它是所有其他元素的父元素。

  • 元素 (Element): XML 文档的基本构建块,由开始标签、结束标签和内容组成。元素可以嵌套,形成层级结构。

  • 属性 (Attribute): 提供关于元素的额外信息,位于元素的开始标签中,以键值对的形式存在。

  • 文本节点 (Text Node): 元素的内容,即标签之间的文本。

  • 注释 (Comment): 用于在 XML 文档中添加注释,不会被解析器处理。

  • 处理指令 (Processing Instruction): 为应用程序提供处理 XML 文档的指令。

  • 命名空间 (Namespace): 用于避免 XML 文档中元素和属性名称冲突。

示例 XML 文档 (books.xml):

<?xml version="1.0" encoding="UTF-8"?> <bookstore> <book category="cooking"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="children"> <title lang="en">Harry Potter</title> <author>J. K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="web"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore>

1.2 XPath 语法基础

XPath 使用路径表达式来选取 XML 文档中的节点或节点集。路径表达式类似于文件系统中的路径,通过一系列的步骤来定位目标节点。

基本语法元素:

  • 节点 (Node): XML 文档的构成单元,包括元素、属性、文本节点等。XPath 将 XML 文档视为节点树。

  • 路径 (Path): 由一个或多个步骤组成的表达式,用于定位节点。

  • 轴 (Axis): 定义节点之间的树形关系,例如 child (子节点)、parent (父节点)、descendant (后代节点) 等。

  • 节点测试 (Node Test): 用于筛选特定类型的节点,例如元素名称、属性名称、节点类型等。

  • 谓词 (Predicate): 用于进一步筛选节点,使用方括号 [] 包围,可以包含表达式。

  • 运算符 (Operator): 用于在表达式中进行比较、逻辑运算等,例如 =, !=, <, >, and, or

  • 函数 (Function): XPath 提供了丰富的内置函数,用于处理字符串、数字、节点集等。

基本路径表达式示例:

  • /bookstore/book: 选取根元素 bookstore 下的所有 book 子元素。 (绝对路径)

  • book/title: 选取当前上下文中所有 book 元素的 title 子元素。 (相对路径)

  • //book: 选取文档中所有名为 book 的元素,无论其位置。 (后代节点)

  • @category: 选取当前元素的 category 属性。

  • text(): 选取当前节点的文本内容。

mermaid graph TD 图示 XML 树结构:

2. XPath 轴 (Axes):节点关系导航

XPath 轴用于定义节点之间的关系,允许我们在 XML 文档树中进行不同方向的导航。理解和灵活运用轴是 XPath 强大的关键。

常用的 XPath 轴:

  • child: 选取当前节点的直接子节点。 (默认轴,可以省略 child::)

    • 示例: bookstore/child::book 等同于 bookstore/book
  • parent: 选取当前节点的父节点。

    • 示例: book/parent::bookstore 选取 book 元素的父节点 bookstore
  • ancestor: 选取当前节点的所有祖先节点 (父节点、父节点的父节点,以此类推)。

    • 示例: title/ancestor::bookstore 选取 title 元素的所有祖先节点中名为 bookstore 的节点。
  • descendant: 选取当前节点的所有后代节点 (子节点、子节点的子节点,以此类推)。

    • 示例: bookstore/descendant::title 选取 bookstore 元素下所有后代节点中名为 title 的节点。
  • following-sibling: 选取当前节点之后的所有兄弟节点 (同级节点,但必须在当前节点之后)。

    • 示例: book[1]/following-sibling::book 选取第一个 book 元素之后的所有 book 兄弟节点。
  • preceding-sibling: 选取当前节点之前的所有兄弟节点 (同级节点,但必须在当前节点之前)。

    • 示例: book[3]/preceding-sibling::book 选取第三个 book 元素之前的所有 book 兄弟节点。
  • attribute: 选取当前节点的属性。 (简写 @)

    • 示例: book/@category 选取 book 元素的 category 属性。 等同于 book/attribute::category
  • namespace: 选取当前节点的命名空间节点。

  • self: 选取当前节点自身。

    • 示例: book/self::book 选取 book 元素自身,通常用于谓词中。
  • current(): 在 XPath 1.0 中不可用,XPath 2.0 及更高版本中表示上下文项。

  • root: 选取文档的根节点。

    • 示例: /root() 选取根节点 bookstore

代码实践 (Python 和 lxml 库):

from lxml import etree xml_content = """ <bookstore> <book category="cooking"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="children"> <title lang="en">Harry Potter</title> <author>J. K. Rowling</author> <year>2005</year> <price>29.99</price> </book> <book category="web"> <title lang="en">Learning XML</title> <author>Erik T. Ray</author> <year>2003</year> <price>39.95</price> </book> </bookstore> """ root = etree.fromstring(xml_content) # 选取所有 book 子元素 (child 轴) books = root.xpath("child::book") print("Child Axis (book elements):", [book.tag for book in books]) # 输出元素标签名 # 选取第一个 book 元素的父节点 (parent 轴) parent_book1 = root.xpath("book[1]/parent::bookstore")[0] print("Parent Axis (parent of book[1]):", parent_book1.tag) # 选取 title 元素的祖先节点 bookstore (ancestor 轴) ancestor_bookstore = root.xpath("title/ancestor::bookstore")[0] print("Ancestor Axis (ancestor of title):", ancestor_bookstore.tag) # 选取 bookstore 元素下所有后代 title 元素 (descendant 轴) descendant_titles = root.xpath("bookstore/descendant::title") print("Descendant Axis (titles in bookstore):", [title.text for title in descendant_titles]) # 输出文本内容 # 选取第一个 book 元素之后的所有 book 兄弟节点 (following-sibling 轴) following_siblings = root.xpath("book[1]/following-sibling::book") print("Following-sibling Axis (books after book[1]):", [book.get('category') for book in following_siblings]) # 输出属性值 # 选取第三个 book 元素之前的 book 兄弟节点 (preceding-sibling 轴) preceding_siblings = root.xpath("book[3]/preceding-sibling::book") print("Preceding-sibling Axis (books before book[3]):", [book.get('category') for book in preceding_siblings]) # 选取 book 元素的 category 属性 (attribute 轴) categories = root.xpath("book/@category") print("Attribute Axis (book categories):", categories) # 选取当前节点自身 (self 轴 - 示例在谓词中使用) self_book = root.xpath("book[self::book][1]")[0] # 实际上 self::book 在这里是多余的,book[1] 已经选择了第一个 book print("Self Axis (first book):", self_book.get('category')) # 选取根节点 (root 轴) root_node = root.xpath("/root()") # 注意: lxml 中 /root() 不直接工作,可以使用 /* 来选取根元素 root_element = root.xpath("/*")[0] print("Root Axis (root element):", root_element.tag)

mermaid graph TD 图示 XPath 轴关系:

3. 节点测试 (Node Tests):精确定位节点

节点测试用于筛选特定类型的节点。它可以根据节点名称、节点类型或命名空间来选择节点。

常用的节点测试:

  • 元素名称: 直接使用元素名称作为节点测试,例如 book, title, author

    • 示例: bookstore/book 选取 bookstore 元素的所有 book 子元素。
  • 通配符 *: 匹配任何元素节点。

    • 示例: bookstore/* 选取 bookstore 元素的所有子元素,无论元素名称。
  • 节点类型测试:

    • node(): 匹配任何类型的节点 (元素、属性、文本节点等)。

    • text(): 匹配文本节点。

    • comment(): 匹配注释节点。

    • processing-instruction(): 匹配处理指令节点。

  • 命名空间前缀: 用于限定命名空间,例如 prefix:elementName

代码实践:

# 选取所有元素节点 (node() 结合轴) all_nodes = root.xpath("//node()") print("Node Test (all nodes - first 5):", [node.tag if hasattr(node, 'tag') else str(node)[:20] + "..." for node in all_nodes[:5]]) # 打印前 5 个,区分元素和文本节点 # 选取所有文本节点 (text()) text_nodes = root.xpath("//text()") print("Node Test (text nodes - first 5):", [node.strip()[:20] + "..." for node in text_nodes[:5] if node.strip()]) # 打印前 5 个非空文本节点 # 选取所有注释节点 (comment()) - 示例 XML 中没有注释,这里演示语法 # comments = root.xpath("//comment()") # 如果 XML 中有注释,则可以这样选取 # 选取所有 book 元素 (元素名称测试) book_elements = root.xpath("//book") print("Node Test (book elements):", [book.tag for book in book_elements]) # 选取 bookstore 下的所有子元素 (通配符 *) bookstore_children = root.xpath("bookstore/*") print("Node Test (bookstore children):", [child.tag for child in bookstore_children])

4. 谓词 (Predicates):条件筛选节点

谓词用于在路径表达式中添加条件,进一步筛选节点。谓词写在方括号 [] 中,可以包含各种表达式,例如位置路径、比较运算、函数调用等。

常用的谓词类型:

  • 位置谓词: 根据节点在节点集中的位置进行筛选。

    • [1]: 选取第一个节点。

    • [last()]: 选取最后一个节点。

    • [position() <= 2]: 选取前两个节点。

  • 比较谓词: 使用比较运算符 ( =, !=, <, >, <=, >=) 比较节点的值或属性值。

    • [@category='cooking']: 选取 category 属性值为 'cooking' 的节点。

    • [price > 35]: 选取 price 子元素值大于 35 的节点。

  • 布尔谓词: 使用布尔运算符 (and, or, not()) 组合多个条件。

    • [@category='cooking' and price < 35]: 选取 category 属性为 'cooking' 且 price 子元素值小于 35 的节点。
  • 存在性谓词: 检查某个节点是否存在。

    • [author]: 选取包含 author 子元素的节点。

代码实践:

# 选取第一个 book 元素 (位置谓词) first_book = root.xpath("bookstore/book[1]")[0] print("Predicate (first book category):", first_book.get('category')) # 选取最后一个 book 元素 (位置谓词 - last()) last_book = root.xpath("bookstore/book[last()]")[0] print("Predicate (last book category):", last_book.get('category')) # 选取前两个 book 元素 (位置谓词 - position()) first_two_books = root.xpath("bookstore/book[position() <= 2]") print("Predicate (first two book categories):", [book.get('category') for book in first_two_books]) # 选取 category 属性为 'cooking' 的 book 元素 (比较谓词 - 属性) cooking_books = root.xpath("bookstore/book[@category='cooking']") print("Predicate (cooking book title):", [book.find('title').text for book in cooking_books]) # 选取 price 子元素值大于 35 的 book 元素 (比较谓词 - 元素值) expensive_books = root.xpath("bookstore/book[price > 35]") # lxml 默认将元素内容视为字符串,需要转换为数字才能比较数值大小 print("Predicate (expensive book titles):", [book.find('title').text for book in expensive_books]) # 字符串比较,这里会选取价格 '39.95' 的书 # 为了进行数值比较,可以使用 string-to-number() 函数 (XPath 1.0 不直接支持,部分库可能扩展支持或使用其他方法,XPath 2.0+ 支持) # 在 lxml 中,可以直接比较数值字符串,lxml 会尝试进行类型转换 expensive_books_numeric = root.xpath("bookstore/book[number(price) > 35]") # 显式转换为数字,更严谨 print("Predicate (expensive book titles - numeric comparison):", [book.find('title').text for book in expensive_books_numeric]) # 选取 category 为 'cooking' 且 price 小于 35 的 book 元素 (布尔谓词 - and) specific_books = root.xpath("bookstore/book[@category='cooking' and number(price) < 35]") print("Predicate (specific book titles):", [book.find('title').text for book in specific_books]) # 选取包含 author 子元素的 book 元素 (存在性谓词) books_with_author = root.xpath("bookstore/book[author]") print("Predicate (books with author - count):", len(books_with_author)) # 所有 book 都有 author,所以数量为 3

5. XPath 函数 (Functions):增强查询能力

XPath 提供了丰富的内置函数,用于处理字符串、数字、布尔值、节点集等,极大地增强了 XPath 的查询能力。

常用的函数类别:

  • 节点集函数: 处理节点集,例如 last(), position(), count(), name(), local-name(), namespace-uri()

  • 字符串函数: 处理字符串,例如 string(), concat(), substring(), string-length(), contains(), starts-with(), translate(), normalize-space().

  • 数值函数: 处理数值,例如 number(), sum(), floor(), ceiling(), round().

  • 布尔函数: 处理布尔值,例如 boolean(), not(), true(), false(), lang().

代码实践:

# 节点集函数 - count() 统计 book 元素的数量 book_count = root.xpath("count(bookstore/book)") print("Function (count of books):", book_count) # 字符串函数 - string-length() 获取第一个 book title 的长度 title_length = root.xpath("string-length(bookstore/book[1]/title)") print("Function (length of first book title):", title_length) # 字符串函数 - substring() 获取第一个 book title 的前 5 个字符 title_substring = root.xpath("substring(bookstore/book[1]/title, 1, 5)") print("Function (substring of first book title):", title_substring) # 字符串函数 - contains() 判断 title 是否包含 "XML" contains_xml = root.xpath("bookstore/book[contains(title, 'XML')]/title/text()") print("Function (titles containing 'XML'):", contains_xml) # 数值函数 - sum() 计算所有 book 的 price 总和 (需要先转换为数字) total_price = root.xpath("sum(bookstore/book/price)") # 默认字符串相加,结果可能不正确 # 使用 number() 函数转换为数字 total_price_numeric = root.xpath("sum(bookstore/book/number(price))") # lxml 可以直接处理数值字符串求和 print("Function (sum of book prices):", total_price_numeric) # 布尔函数 - not() 选取 category 不是 'cooking' 的 book not_cooking_books = root.xpath("bookstore/book[not(@category='cooking')]") print("Function (not cooking book categories):", [book.get('category') for book in not_cooking_books])

6. XPath 与编程语言的集成

XPath 通常不会单独使用,而是集成到各种编程语言中,用于处理 XML 数据。常见的集成方式包括:

  • Python (lxml, xml.etree.ElementTree): lxml 是一个高性能的 XML 和 HTML 处理库,提供了强大的 XPath 支持。xml.etree.ElementTree 是 Python 标准库中的 XML 处理模块,也支持 XPath,但性能和功能可能不如 lxml。

  • Java (javax.xml.xpath): Java 提供了 javax.xml.xpath 包,用于进行 XPath 查询。

  • JavaScript (浏览器 DOM API, xmldom): 浏览器 DOM API 提供了 evaluate() 方法,用于在 JavaScript 中执行 XPath 查询。xmldom 是一个在 Node.js 环境下模拟 DOM API 的库,也支持 XPath。

  • C# (.NET System.Xml.XPath): .NET 框架提供了 System.Xml.XPath 命名空间,用于进行 XPath 查询。

  • PHP (DOMXPath): PHP 的 DOM 扩展提供了 DOMXPath 类,用于执行 XPath 查询。

Python 代码示例 (lxml): (前面代码实践已大量使用 lxml)

Java 代码示例 (javax.xml.xpath):

import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import java.io.File; public class XPathExample { public static void main(String[] args) throws Exception { File xmlFile = new File("books.xml"); // 假设 books.xml 文件存在 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(xmlFile); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); // 查询所有 title 元素 XPathExpression expr = xpath.compile("//title/text()"); Object result = expr.evaluate(document, XPathConstants.NODESET); org.w3c.dom.NodeList nodes = (org.w3c.dom.NodeList) result; System.out.println("Titles:"); for (int i = 0; i < nodes.getLength(); i++) { System.out.println(nodes.item(i).getNodeValue()); } // 查询 category 为 "web" 的 book 的 title XPathExpression webBookTitleExpr = xpath.compile("//book[@category='web']/title/text()"); String webBookTitle = (String) webBookTitleExpr.evaluate(document, XPathConstants.STRING); System.out.println("\nWeb Book Title: " + webBookTitle); } }

总结:

本章节详细介绍了 XPath 的核心概念,包括 XML 结构、XPath 语法、轴、节点测试、谓词和函数。通过丰富的代码实践,展示了如何在 Python 中使用 lxml 库进行 XPath 查询,并简要介绍了 XPath 在其他编程语言中的应用。掌握 XPath,将使你能够高效地处理和提取 XML 数据,在 Web 开发、数据分析等领域发挥重要作用。 通过不断练习和深入学习,你将能够熟练运用 XPath,解决更复杂的 XML 数据处理问题。

目录大纲

    最新文档

    知识宇宙

    正在加载知识图谱...


    转发