XPath , 全称 XML Path Language ,即 XML 路径语言,它是一门在 XML 文档中查找信息的语言 。它最初是用来搜寻 XML 文档的,但是它同样适用于 HTML 文档的搜索。
需要安装lxml,命令:pip install lxml
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
//title[@lang=‘eng’] 这就是一个 XPath 规则 ,它代表选择所有名称为 title ,同时属性 lang 的值为 eng 的节点 。
调用etree.HTML(str:) 可以构造一个 XPath 解析对象(html) ;调用 tostring()方法可输出修正后的 HTML 代码(自动补齐html的头,尾),但是结果是 bytes 类型 。
from lxml import etree
text = '''
'''
html = etree.HTML(text)
result= etree.tostring(html)
print(result.decode('utf-8'))
解析html文件
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result= etree.tostring(html)
print(result.decode('utf-8'))
// 开头的 XPath 规则来选取所有符合要求的节 ,返回list集合;元素类型为lxml.etree._Element,与 etree.parse()返回的的格式一致
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
### 输出对象列表
# [, , , , , , ...]
# print(type(result[0])) == print(type(html)) 都是
通过 / 或 // 即可查找元素的子节点或子孙节点 。假如现在想选择 li 节点的所有直接 a 子节点,可以这样实现:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a')
print(result)
## [, , , ,]
/ 用于选取直接子节点 ,如果要获取所有子孙节点,就可以使用//。例如,要获取 ul 节点下的所有子孙 a 节点 :
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//ul//a')
print(result)
## [, , , ,]
用 … 表示选择父级节点。首先选中 href 属性为 link4.html 的 a 节点,然后再获取其父节点,然后再获取其 class属性
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)
## ['item-1']
也可以通过 parent:: 来获取父节点
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="link4.html"]/parent::*/@class')
print(result)
## ['item-1']
在选取的时候,可以用@符号进行属性过滤 。 比如,这里如果要选取 class 为 item-0 的 li节点
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]')
print(result)
##[, ]
用 XPath 中的 text()方法获取节点中的文本
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)
## ['first item','fifth item']
注意 // 与 / 的区别
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]//text()')
print(result)
## ['first item','fifth item','\n ']
通过@href 即可获取节点的 href 属性。 注意,此处和属性匹配的方法不同,属性匹配是中括号加属性名和值来限定某个属性,如[@href=“linkl.html”],而此处的@href 指的是获取节点的某个属性,二者需要做好区分。
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a/@href')
print(result)
# [ 'linkl. html', 'link2. html','link3.html','link4 .html','links.html']
from lxml import etree
text ='''
first item
'''
html = etree.HTML(text)
result = html.xpath('//li[@class="li"]/a/text()')
print(result)
# []
上例无法获取 a 标签内的文本,因为li 的class 属性有多个值,而 //li[@class=“li”] 匹配不到li对象。通过contains ()函数可以解决这个问题
from lxml import etree
text ='''
first item
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class,"li")]/a/text()')
print(result)
# ['first item']
result = html.xpath('//li[contains(@class,"li li-first")]/a/text()')
print(result)
# ['first item']
同时匹配多个属性,可以使用and运算符连接
from lxml import etree
text ='''
first item
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
print(result)
# ['first item']
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
or | 或 | age=19 or age=20 | 如果 age 是 19 ,则返回 true 。 如果 age 是 21 ,则返回 false |
and | 与 | age>19 and age<21 | 如果 age 是 20 ,则返回 true 。 如果 age 是 18 ,则返回 false |
mod | 除法的余数 | 5 mod 2 | 1 |
| | 计算两个节点集 | //book | //cd | 返回所有拥有 book 和 cd 元素的节点集 |
+ | 加法 | 6+4 | 10 |
- | 减法 | 6-4 | 2 |
* | 乘法 | 6*4 | 24 |
div | 除法 | 8 dev 4 | 2 |
= | 等于 | age=19 | 如果 age 是 19 ,则返回 true 。如果 age 是 20 ,则返回false |
!= | 不等于 | age!=19 | 如果 age 是 18 ,则返回 true 。如果 age 是 19,则返回false |
< | 小于 | age<19 | 如果 age 是 18 ,则返回 true 。 如果 age 是 19 ,则返回 false |
<= | 不大与 | age=<19 | 如果 age 是 19,则返回 true 。 如果 age 是 20 ,则返回 false |
> | 大于 | age>19 | 如果 age 是 20 ,则返回 true 。 如果 age 是 19 ,则返回 false |
>= | 不小于 | age>=19 | 如果 age 是 19 ,则返回 true 。 如果 age 是 18,则返回 false |
在选择的时候某些属性可能同时匹配了多个节点,但是只想要其中的某个节点
from lxml import etree
text = '''
'''
html = etree.HTML(text)
# 取第一个 li 节点(注意序号从1开始,不是0)
result = html.xpath('//li[1]/a/text()')
print(result)
# 取第最后一个 li 节点
result = html.xpath('//li[last()]/a/text()')
print(result)
# 取前2个 li 节点
result = html.xpath('//li[position()<3]/a/text()')
print(result)
# 取倒数第3个 li 节点
result = html.xpath('//li[last()-2]/a/text()')
print(result)
XPath 提供了很多节点轴选择方法,包括获取子元素 、兄弟元素、父元素、祖先元素等
from lxml import etree
text = '''
'''
html = etree.HTML(text)
# ancestor 轴,可以获取所有祖先节点
result = html.xpath('//li[1]/ancestor::*')
print(result)
#加了限定条件,这次在冒号后面加了 div ,这样得到的结果就只有 div 这个祖先节点
result = html.xpath('//li[1]/ancestor::div')
print(result)
# attribute 轴,可以获取所有属性值,其后跟的选择器还是*,这代表获取节点的所有属性
result = html.xpath('//li[1]/attribute::*')
print(result)
#child 轴,可以获取所有直接子节点 。 这里加了限定条件,选取 href 属性为 link1.html 的 a 节点
result = html.xpath('//li[1]/child::a[@href="link1.html"]')
print(result)
#descendant 轴,可以获取所有子孙节点 。 这里我们又加了限定条件获取 span 节点
result = html.xpath('//li[1]/descendant::span')
print(result)
#following 轴,可以获取当前节点之后的所有节点。这里虽然使用的是*匹配,但又加了索引选择,所以只获取了第二个后续节点
result = html.xpath('//li[1]/following::*[2]')
print(result)
#following-sibling 轴,可以获取当前节点之后的所有同级节点。这里我们使用*匹配,所以获取了所有后续同级节点
result = html.xpath('//li[1]/following-sibling::*')
print(result)