XPath
XPath是一钟确认文档位于XML位置的语言,它同样适用于HTML。它提供了超过100个内置函数、用于字符串、时间、数字的匹配以及序列、节点的处理。现在流行的XPath版本为3.1。
Xpath术语
- 节点:XPath中有七种节点:元素节点、属性节点、文本节点、命名空间节点、处理指令节点、注释节点和文档节点。
- 父节点、子节点:和DOM中的概念类似。
- 同胞节点:若两个节点有相同的父节点,那么它们就互称为同胞节点。
Xpath表达式
最常见的XPath表达式就是路径表达式。路径表达式就是从一个节点到另一个节点或一组节点的书面步骤顺序。这些步骤以“/”字符分开,每一步有三个组成成分:轴描述、节点测试、节点名称。
- 简写的XPath表达式
如一个div位于文本中的位置如下:<html><body><div></div><body><html>
,那么它的位置使用XPath表示就是/html/body/div。
- 完整的XPath表达式
在XPath语法的每个步骤里,用完整的轴描述,然后使用“::”,它的后面跟着节点测试的内容。
- 轴描述语法
轴描述元素用于表示HTML文档分支的遍历方向。
坐标 | 名称 | 缩写语法 |
---|---|---|
child | 子节点 | 默认,不需要 |
attribute | 属性 | @ |
descendant | 子孙节点 | 不提供 |
desendant-or-self | 自身引用及子孙节点 | // |
parent | 父节点 | .. |
ancestor | 祖先节点 | 不提供 |
ancestor-or-self | 自身引用及祖先节点 | |
following | 下文节点 | |
preceding | 前文节点 | |
following-sibiling | 下一个同级节点 | |
preceding-sibiling | 上一个同级节点 | |
self | 自己 | |
namespace | 名称空间 |
attribute坐标简写语法的一个范例就是//a/@href,在HTML文档书里,选择所有a元素的href属性。self坐标通常和术语同用,以参考当前的选定节点。如h3[.='See also']在当前节点选中了h3的元素,该元素的内容为See also。
节点测试
节点测试的对象通常包括特定节点名或者一般的表达式。
- comment():寻找HTML注释节点。
from lxml import etree
html = """
<p>这是没有注释的内容</p>
<!--这是被注释的内容-->
"""
xp = etree.HTML(html)
print(xp.xpath('//comment()'))
[<!--这是被注释的内容-->]
- text():寻找某点的文字型别,例如在
<p>Hello</p>
节点中寻找Hello。
from lxml import etree
html = """
<p>这是没有注释的内容</p>
<!--这是被注释的内容-->
"""
xp = etree.HTML(html)
print(xp.xpath('//p/text()'))
['这是没有注释的内容']
- node():寻找所有节点。
from lxml import etree
html = """
<div><p id='content'>这是没有注释的内容</p>
<!--这是被注释的内容--></div>
"""
xp = etree.HTML(html)
print(xp.xpath('//div/node()'))
[<Element p at 0x2691d7d6300>, '\n', <!--这是被注释的内容-->]
节点描述
节点描述用于返回符合特定条件的节点,如//a[@href='help.php']
会选择所有href属性为help.php的a节点,//a[@href='help.php'][name(..)='div'][../@class='header']
会返回href属性为help.php、具有父元素div且它的class属性为header的a节点。
XPath运算符
运算符 | 描述 | 示例 | 返回值 |
---|---|---|---|
| | 两个节点集并集 | xpath('//div|//a') | 返回所有的div元素节点和a节点 |
+/-/*/div | 加减乘除 | xpath('//div[2-1]') | 返回所有div元素的第一个结果 |
= | 等于 | xpath('//a[href="baidu.com"]') | 返回所有href值为baidu.com的a元素 |
!= | 不等于 | ||
</<=/>/>= | 小于/小于等于/大于/大于等于 | xpath('//input[@value>2]') | 返回所有value属性大于2的input元素 |
or/and | 或/并且 | xpath('//input[@value=1 or @value=2]') | 返回所有value属性等于1或2的input元素 |
mod | 取余 |
练手:
from lxml import etree
html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Xpath教程</title>
</head>
<body>
<ul>
<li><a href="www.likeinlove.com/index.html">出师未捷身先死,长使英雄泪满襟。</a></li>
<li><a href="www.likeinlove.com/home.html">风急天高猿啸哀,渚清沙白鸟飞回。</a></li>
<li>仲夏苦夜短,开轩纳微凉。</li>
<!-- 以上是杜甫部分名句-->
</ul>
<p id="4">万里悲秋常作客,百年多病独登台。</p>
<p id="5">清江一曲抱村流,长夏江村事事幽。</p>
<div>
<ul>
<li>满月飞明镜,归心折大刀。</li>
<li>射人先射马,擒贼先擒王。</li>
<li>留连戏蝶时时舞,自在娇莺恰恰啼。</li>
<li>正是江南好风景,落花时节又逢君。</li>
</ul>
</div>
</body>
</html>
"""
xp = etree.HTML(html)
print('获取标题内容',xp.xpath('//title/text()'))
print('获取页面语言',xp.xpath('//html/@lang'))
print('获取页面超链接',xp.xpath('//a/@href'))
print('获取id为4的诗句',xp.xpath('//p[@id=4]/text()'))
获取标题内容 ['Xpath教程']
获取页面语言 ['en']
获取页面超链接 ['www.likeinlove.com/index.html', 'www.likeinlove.com/home.html']
获取id为4的诗句 ['万里悲秋常作客,百年多病独登台。']
XPath常用函数
函数 | 作用 | 示例 |
---|---|---|
contains(string1, string2) | 若string1包含string2,返回True | xpath('//a[contains(@href, "baidu")]') |
starts-with(string1, string2) | 若string1以string2看开始,返回True | xpath('//a[starts-with(@href, "baidu")]') |
substring(string, start, len) | 截取字符串string,start代表起始位置,第一个字符的下标为1,len可省略,表示截取到末尾 | xpath('//a[substring(@href,1,5)="https"]') |
string-length(string) | 返回指定字符串的长度,若string不填写则返回当前节点的字符串的长度 | xpath('//a[string-length(@href)=18]') |
position() | 返回当前正在被处理的节点的index位置 | xpath('//*[@value=3][posotion()=2]') |
last() | 返回在被处理的节点哄的项目数目 | xpath('//li[last()=3]') |
true() | 返回True | |
false() | 返回False | |
name(nodeset) | 指定节点集中的第一个节点的名称,若不传参数则为当前节点名称 | xpath('//*name()="div"') |
count(nodeset) | 返回节点的数量 | xpath(//p[count(//p)=3]) |
XPath常见错误
- Unregistered Function:未注册该函数。
- Invalid Predicate:无效谓词。
- Unfinished Literal:表达式不完整。
使用Python解析HTML
Python解析HTML的库有lxml、BeautifulSoup4、pyquery等。第一个主要提供XPath语法的选择器,第二个则依赖Python标准库,提供了节点选择器,第三个提供CSS选择器。
使用这以下的代码分别安装这三个模块。
pip install lxml
pip install bs4
pip install pyquery
下面的代码将尝试用这三个库提取如下HTML代码中的这段话。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标题</title>
</head>
<body>
<p>这是网页中的一段话。</p>
</body>
</html>
- lxml
#/html/body/p
from lxml import etree
html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标题</title>
</head>
<body>
<p>这是网页中的一段话。</p>
</body>
</html>"""
xp = etree.HTML(html)
items = xp.xpath('//html//body//p//text()')
print(items)
['这是网页中的一段话。']
- BeautifulSoup4
#/html/body/p
from bs4 import BeautifulSoup
html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标题</title>
</head>
<body>
<p>这是网页中的一段话。</p>
</body>
</html>"""
bs = BeautifulSoup(html, 'lxml')
items = bs.select('p')
print(items)
[<p>这是网页中的一段话。</p>]
- pyquery
from pyquery import PyQuery as query
html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标题</title>
</head>
<body>
<p>这是网页中的一段话。</p>
</body>
</html>"""
pq = query(html)
items = pq('p')
print(items)
<p>这是网页中的一段话。</p>