简介
XPath(XML Path Language)是一种用于在 XML 文档中查找信息的语言。它基于树状结构的 XML 文档,可以通过路径表达式来选取节点或节点集。也常常用来解析 HTML。
如果你是一个前端,对用路径获取元素可能陌生又熟悉。陌生是很多的路径,熟悉的路径上又有熟悉的属性和方法。下面我们就来探究一下 XPath 的魅力。
Python XPath 解析器
首先我们先了解一下工具,Python 是最简单的语言之一了,使用 Python 了解 XPath 尤其重要。
lxml
BeautifulSoup
scrapy
html5lib
- python 标准库的:
xml.etree.ElementTree
以 lxml 为例,首先安装 lxml 包:
pip install lxml
节点选择器
"/"
: 从根节点选取。"//"
: 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。"."
: 选取当前节点。".."
: 选取当前节点的父节点。"@"
: 选取属性。
以下是一个示例:
from lxml import html # 创建一个 HTML 示例 html_string = ''' <html> <body> <div id="main"> <p class="text">Hello, world!</p> <p class="text">Welcome to lxml.</p> <a href="http://example.com">Example</a> </div> </body> </html> ''' # 解析 HTML 字符串 tree = html.fromstring(html_string) # 从根节点选取 root = tree.xpath('/html')[0] print("Root node:", root.tag) # 输出: Root node: html # 从文档中的所有 p 节点中选择 paragraphs = tree.xpath('//p') print("All paragraph texts:", [p.text for p in paragraphs]) # 输出: ['Hello, world!', 'Welcome to lxml.'] # 选取当前节点的父节点 p = tree.xpath('//p')[0] parent = p.xpath('..')[0] print("Parent of first paragraph:", parent.tag) # 输出: div # 选取当前节点 print("Current paragraph node:", p.tag) # 输出: p # 选取属性值 link = tree.xpath('//a')[0] href = link.xpath('@href')[0] print("Href attribute of the link:", href) # 输出: http://example.com
通配符
"*"
: 匹配任何元素节点。"@*"
: 匹配任何属性节点。
示例:
from lxml import html # 创建一个 HTML 示例 html_string = ''' <html> <body> <div id="main"> <p class="text">Hello, world!</p> <p class="text">Welcome to lxml.</p> <a href="http://example.com" title="Example">Example</a> </div> </body> </html> ''' # 解析 HTML 字符串 tree = html.fromstring(html_string) # 匹配任何元素节点 elements = tree.xpath('//*') print("All element tags:", [el.tag for el in elements]) # 输出: ['html', 'body', 'div', 'p', 'p', 'a'] # 匹配任何属性节点 attributes = tree.xpath('//@*') print("All attribute values:", [attr for attr in attributes]) # 输出: ['main', 'text', 'text', 'http://example.com', 'Example']
谓词 (Predicates)
"[ ]"
: 谓词用于查找特定的节点或包含特定值的节点。"[position()]"
: 用于选取节点的位置。"[@attribute='value']"
: 用于选取具有特定属性值的节点。
from lxml import html # 创建一个 HTML 示例 html_string = ''' <html> <body> <div id="main"> <p class="text">Hello, world!</p> <p class="text">Welcome to lxml.</p> <a href="http://example.com" title="Example">Example</a> </div> </body> </html> ''' # 解析 HTML 字符串 tree = html.fromstring(html_string) # 使用谓词查找特定的节点 # 查找所有 p 元素中,文本包含 'Welcome' 的节点 specific_paragraphs = tree.xpath('//p[contains(text(), "Welcome")]') print("Paragraphs containing 'Welcome':", [p.text for p in specific_paragraphs]) # 输出: ['Welcome to lxml.'] # 使用 position() 选择节点的位置 # 选择第二个 p 元素 second_paragraph = tree.xpath('//p[position()=2]')[0] print("Second paragraph text:", second_paragraph.text) # 输出: Welcome to lxml. # 使用特定属性值选择节点 # 选择具有 title="Example" 的 a 元素 specific_link = tree.xpath('//a[@title="Example"]')[0] print("Link with title 'Example':", specific_link.text) # 输出: Example
函数
"text()"
: 选取文本内容。"contains()"
: 检查某个节点的文本或属性值是否包含特定的子串。"starts-with()"
: 检查某个节点的文本或属性值是否以特定的子串开头。"count()"
: 计算选定的节点数。
from lxml import html # 创建一个 HTML 示例 html_string = ''' <html> <body> <div id="main"> <p class="text">Hello, world!</p> <p class="text">Welcome to lxml.</p> <a href="http://example.com" title="Example">Example</a> <a href="http://example.org" title="Example">Another Example</a> </div> </body> </html> ''' # 解析 HTML 字符串 tree = html.fromstring(html_string) # 使用 text() 选取文本内容 paragraph_texts = tree.xpath('//p/text()') print("Paragraph texts:", paragraph_texts) # 输出: ['Hello, world!', 'Welcome to lxml.'] # 使用 contains() 检查文本是否包含特定子串 contains_text = tree.xpath('//p[contains(text(), "lxml")]') print("Paragraphs containing 'lxml':", [p.text for p in contains_text]) # 输出: ['Welcome to lxml.'] # 使用 starts-with() 检查文本是否以特定子串开头 starts_with_text = tree.xpath('//p[starts-with(text(), "Hello")]') print("Paragraphs starting with 'Hello':", [p.text for p in starts_with_text]) # 输出: ['Hello, world!'] # 使用 count() 计算选定的节点数 num_links = tree.xpath('count(//a)') print("Number of links:", int(num_links)) # 输出: 2
运算符
"|"
: 选择若干路径。"+、-、*、div"
:算术运算符。
多路径选择
from lxml import html # 创建一个 HTML 示例 html_string = ''' <html> <body> <div class="content"> <p>First paragraph.</p> <p>Second paragraph.</p> <span>Some span text.</span> <a href="http://example.com">Example link</a> </div> </body> </html> ''' # 解析 HTML 字符串 tree = html.fromstring(html_string) # 使用 "|" 选择多个路径 elements = tree.xpath('//p | //span') print("Selected elements:", [el.tag for el in elements]) # 输出: ['p', 'p', 'span'] # 获取这些元素的文本 texts = [el.text for el in elements] print("Texts of selected elements:", texts) # 输出: ['First paragraph.', 'Second paragraph.', 'Some span text.']
xpath 节点运算
from lxml import etree # 创建一个 XML 示例 xml_string = ''' <root> <numbers> <num1>10</num1> <num2>20</num2> <num3>30</num3> </numbers> </root> ''' # 解析 XML 字符串 tree = etree.XML(xml_string) # 选择数字节点并进行算术运算 sum_result = tree.xpath('(/root/numbers/num1 + /root/numbers/num2)[1]') difference_result = tree.xpath('(/root/numbers/num2 - /root/numbers/num1)[1]') product_result = tree.xpath('(/root/numbers/num1 * /root/numbers/num3)[1]') division_result = tree.xpath('(/root/numbers/num3 div /root/numbers/num1)[1]') print("Sum of num1 and num2:", sum_result) # 输出: 30 print("Difference between num2 and num1:", difference_result) # 输出: 10 print("Product of num1 and num3:", product_result) # 输出: 300 print("Division of num3 by num1:", division_result) # 输出: 3
Scrapy 中的 xpath
Scrapy 中的 xpath 在 Spider 类中的 parse 方法的 response 的中获取。下面是一些示例:
import scrapy class ExampleSpider(scrapy.Spider): name = 'example' start_urls = ['http://example.com'] def parse(self, response): # 提取标题 title = response.xpath('//title/text()').get() print("Title:", title) # 提取所有段落文本 paragraphs = response.xpath('//p/text()').getall() print("Paragraphs:", paragraphs) # 提取第二个段落 second_paragraph = response.xpath('//p[2]/text()').get() print("Second paragraph:", second_paragraph) # 提取具有特定 href 的链接文本 link_text = response.xpath('//a[@href="http://example.com"]/text()').get() print("Link text:", link_text) # 提取所有 href 属性 links = response.xpath('//a/@href').getall() print("All href attributes:", links)
小结
本文主要介绍了 Python 中的 XPath 语法,以及 XPath 的解析库,这里着重介绍了 lxml 用法,同时也介绍了 scrapy 中 xpath 的各种用法。在日常编码中这些内容已经足够日常使用了。
作者:编程杂货铺