什么是 XPath
XPath(XML Path Language) 是一门在 XML 和 HTML 文档中查找信息的语言,可用来在 XML 和 HTML 文档中对元素和属性进行遍历。
XPath 开发工具
1.Chrome 插件 XPath Helper
2.Firefox 插件 XPath Checker
XPath 语法
-
选取节点
XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
表达式 | 描述 | 示例 | 结果 |
---|---|---|---|
nodename | 选取此节点的所有子节点 | bookstore | 选取 bookstore 下所有的子节点 |
/ | 如果是在最前面,代表从根节点选取。否则选择某节点下的某个节点 | /bookstore | 选取根元素下所有的 bookstore 节点 |
// | 从全局节点中选择节点,随便在哪个位置 | //book | 从全局节点中找到所有的 book 节点 |
@ | 选取某个节点的属性 | //book[@price] | 选择所有 book 节点的 price 属性 |
-
谓词
路径表达式 | 描述 |
---|---|
/bookstore/book[1] | 选取 bookstore 下的第一个子元素 |
/bookstore/book[last()] | 选取 bookstore 下的最后一个 book 元素 |
bookstore/book[position()<3] | 选取 bookstore 下前面两个子元素 |
//book[@price] | 选取拥有 price 属性的 book 元素 |
//book[@price=10] | 选取所有属性 price 等于 10 的 book 元素 |
//book/a/text() | 选取所有 book 标签下的 a 标签的文本 |
-
通配符
* 星号表示通配符
通配符 | 描述 | 示例 | 结果 |
---|---|---|---|
* | 匹配任意节点 | /bookstore/* | 选取 bookstore 下的所有子元素 |
@* | 匹配节点中的任何属性 | //book[@*] | 选取所有带有属性的 book 元素 |
-
选取多个路径
通过在路径表达式中使用 "|" 运算符,可以选取若干个路径。
示例如下:
//bookstore/book* | //book/title
# 选取所有 book 元素以及 book 元素下所有的 title 元素
-
运算符
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集 |
+ | 加法 | 6 + 4 | 10 |
- | 减法 | 6 - 4 | 2 |
* | 乘法 | 6 * 4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | price=9.80 | 如果 price 是 9.80,则返回 true,否则返回 false |
!= | 不等于 | price!=9.80 | 如果 price 不是 9.80,则返回 true,否则返回 false |
< | 小于 | price<9.80 | 如果 price 小于 9.80,则返回 true,否则返回 false |
<= | 小于等于 | price<=9.80 | ... |
> | 大于 | price>9.80 | ... |
>= | 大于等于 | price>=9.80 | ... |
or | 或 | price=9.80 or price=9.70 | ... |
and | 与 | price>9.00 and price<9.90 | ... |
mod | 计算除法的余数 | 5 mod 2 | 1 |
-
contains
有时候某个属性包含多个值,就可以使用 contains
函数,示例代码如下:
//div[contains(@class, 'job_detail')
lxml 库
lxml 是一个 HTML/XML 的解析库,主要功能是如何解析和提取 HTML/XML 数据。
lxml 和 正则是一样,也是用 C 语言实现的,是一款高性能的 Python HTML/XML 解析器,可以利用之前学习的 XPath 语法,来快速定位特定元素以及节点信息。
lxml python 官方文档:http://lxml.de/index.html
可通过 pip 安装:pip install lxml
- 基本使用
我们可以利用他来解析 HTML 代码,且在解析 HTML 代码的时候,如果 HTML 代码不规范,他会自动进行补全。示例代码如下:
from lxml.html import etree
htmlText = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
'''
# 利用 etree.HTML,将字符串解析为 HTML 文档
html = etree.HTML(htmlText)
# 按字符串序列化 HTML 文档
result = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')
print(result)
- 在文件中读取 html 代码
除了直接使用字符串进行解析,lxml 还支持从文件中读取内容。我们新建一个 hello.html 文件:
<!-- hello.html -->
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
然后利用 etree.parse()
方法来读取读取文件,如下:
from lxml.html import etree
html = etree.parse('files/hello.html')
result = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')
print(result)
另外,需要注意的是,使用 etree.parse()
方法解析 html 内容时,有时候会报 lxml.etree.XMLSyntaxError
的错,这是因为 etree.parse()
默认使用的是 XML 的解析器,所以当 html 内容不规范,比如出现某个标签缺少闭合标签时,就会报这个错误。这时,可使用 etree.HTMLParser()
创建一个 HTML 的解析器,然后作为 etree.parse()
方法的参数即可,如下:
htmlParser = etree.HTMLParser(encoding='utf-8')
html = etree.parse('files/hello.html', parser=htmlParser)
result = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')
print(result)
- 在 lxml 中使用 XPath 语法
(1) 使用 xpath
语法,应该使用 Element.xpath
语法,来执行 xpath 的选择
(2) xpath
函数返回来的永远是一个列表
from lxml.html import etree
htmlParser = etree.HTMLParser(encoding='utf-8')
html = etree.parse('files/hello.html', parser=htmlParser)
# htmlText = etree.tostring(html, encoding='utf-8', pretty_print=True).decode('utf-8')
# print(htmlText)
lis = html.xpath('//li')
for li in lis:
print(etree.tostring(li, encoding='utf-8', pretty_print=True).decode('utf-8'), end='')
aList = html.xpath('//a/@href')
for a in aList:
print(a)
print('-----------------------')
lis = html.xpath('//li')
for li in lis:
# . 号表示在当前的 li 元素下去匹配
href = li.xpath('.//a/@href')[0] #获取 a 标签的 href 属性
txt = li.xpath('.//a/text()')[0] #获取 a 标签的文本
print(href, txt)