- 文集信息
- 目录大纲
- 最新文档
- 知识宇宙
文集详情
文集导读
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 数据处理问题。
目录大纲
最新文档
知识宇宙
正在加载知识图谱...