Xpath的使用
Xpath的使用
-
Xpath全称是XML Path Language,即XML路径语言,用来在XML文档中查找信息。
-
虽然最初是用来搜寻XML文档的,但是在HTML文档中也同样适用。
-
在做爬虫的时候,完全可以使用XPath实现相应的信息提取
-
Xpath提供了100多个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等
1. XPath常用规则
| 表达式 | 描述 |
|---|---|
| nodename | 选取此节点的所有子节点 |
| / | 从当前节点选取直接子节点 |
| // | 从当前节点选取子孙节点 |
| . | 选取当前节点 |
| .. | 选取当前节点的父节点 |
| @ | 选取属性 |
//title[@lang='eng']代表选择所有名称为title,lang属性值为eng的节点
2. 安装XPath
使用pip3 install lxml
3. 实例导入
from lxml import etree
text = '''
<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>
</ul>
</div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))
首先导入lxml模块中的etree模块,接着声明一段HTML代码,然后调用HTML类来进行初始化,这样就成功构造了一个XPath解析对象。
HTML代码最后的一个<li>标签没有闭合,etree模块可以自动修正HTML文本,之后调用tostring方法即可输出修正后的HTML代码,但是结果是bytes类型,需要使用decode方法将其转换成str格式。
另外也可以直接读取文本文件进行解析:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
4. 所有节点
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
-------------------
[<Element html at 0x1055cca40>, <Element body at 0x119084700>, <Element div at 0x119084740>, <Element ul at 0x119085dc0>, <Element li at 0x119085ec0>, <Element a at 0x119084840>, <Element li at 0x119085f80>, <Element a at 0x119085fc0>, <Element li at 0x119086000>, <Element a at 0x119085e80>, <Element li at 0x119086040>, <Element a at 0x119086080>, <Element li at 0x1190860c0>, <Element a at 0x119086100>]
-
一般都会使用
//开头的XPath规则,来选取所有符合要求的节点 -
*代表匹配所有节点,也就是获取整个HTML文档中的所有节点 -
从运行结果来看,
返回的是一个列表,列表中的每个元素都是Element类型,类型后面跟着节点名称 -
还可以指定名称:
from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//li') print(result) -------------------- [<Element li at 0x117a9f9c0>, <Element li at 0x117ac0dc0>, <Element li at 0x117ac0e40>, <Element li at 0x117ac0e80>, <Element li at 0x117ac1000>]//li用来获取整个HTML文档中所有li节点
5.子节点
通过/或//可以查找元素的子节点或者子孙节点
选择所有li节点下所有直接子节点a:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a')
for re in result:
print(re)
--------------------
<Element a at 0x12737b280>
<Element a at 0x127389600>
<Element a at 0x1273896c0>
<Element a at 0x127389700>
<Element a at 0x127389880>
<Element a at 0x127389940>
<Element a at 0x127389800>
<Element a at 0x127389a40>
<Element a at 0x127389a80>
<Element a at 0x127389980>
<Element a at 0x127389ac0>
<Element a at 0x127389b00>
<Element a at 0x127389b40>
<Element a at 0x127389b80>
<Element a at 0x127389bc0>
<Element a at 0x127389c00>
<Element a at 0x127389c40>
<Element a at 0x127389c80>
<Element a at 0x127389cc0>
<Element a at 0x127389d00>
<Element a at 0x127389d40>
<Element a at 0x127389d80>
<Element a at 0x127389dc0>
<Element a at 0x127389e00>
<Element a at 0x127389e40>
<Element a at 0x127389e80>
<Element a at 0x127389ec0>
<Element a at 0x127389f00>
<Element a at 0x127389f40>
<Element a at 0x127389f80>
<Element a at 0x127389fc0>
<Element a at 0x12738a000>
<Element a at 0x12738a040>
选择所有ul节点下所有子孙节点a:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//ul//a')
for re in result:
print(re)
如果写成//ul/a,这样就没有结果输出,因为ul节点的直接子节点是li,不是a
6.父节点
如果知道子节点,要通过..来寻找父节点
寻找属性href值为"https://www.douban.com/mine/"的a节点的父节点:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="https://www.douban.com/mine/"]/..')
print(result)
-----------
[<Element td at 0x11fd2cf80>]
另外也可以通过parent::获取父节点:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="https://www.douban.com/mine/"]/parent::*')
print(result)
----------------
[<Element td at 0x11f97e000>]
7.属性匹配
在选取节点的时候,可以使用@符号来实现属性过滤
如://a[@href="https://www.douban.com/mine/"],就是选取href属性值为"https://www.douban.com/mine/"的a标签
8.文本获取
XPath中的text方法可以获取节点中的文本

显示class属性值为"nav-user-account"的li节点下的span节点的文本
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]/a/span/text()')
print(result)
-------
['彧晖的帐号']
如果想要直接获取li节点下的所有子孙节点文本:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]//text()')
print(result)
------------
['\n ', '\n ', '彧晖的帐号', '\n ', '\n ', '\n ', '\n ', '\n ', '\n ', '\n ', '个人主页', '\n ', '\n ', '\n ', '\n ', '\n ', '我的订单', '\n ', '\n ', '\n ', '\n ', '\n ', '我的钱包', '\n ', '\n ', '\n ', '\n ', '\n ', '帐号管理', '\n ', '\n ', '\n ', '\n ', '\n ', '退出', '\n ', '\n ', '\n ', '\n ', '\n ', '\n ']
9. 属性获取
可以使用@符号获取属性
获取li节点的直接子节点a的href属性值:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]/a/@href')
print(result)
获取li节点下所有子孙节点的href属性值:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]//@href')
print(result)
10. 属性多值匹配
有时候节点的某个属性拥有多个属性值,这时就需要使用contains方法来进行匹配
from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class, li)]/a/text()')
print(result)
11.多属性匹配
- 有时候节点里面有多个属性,要通过匹配多个属性的属性值,才能确定唯一的节点,这时就需要将要匹配的属性通过
and连接起来
from lxml import etree
text = '''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class, li)and @name="item"]/a/text()')
print(result)
上述的代码中就是寻找li的class属性值为li,并且name属性值为item的li节点,然后将该li节点下a节点的文本打印出来
- XPath中除了and运算符外,还有其他的运算符
| 运算符 | 作用 |
|---|---|
| or | 或 |
| and | 与 |
| mod | 计算除法的余数 |
| | | 计算两个节点集,如//li|//a就是返回拥有li元素和a元素的节点集 |
| + | 加法 |
| - | 减法 |
| * | 乘法 |
| div | 除法 |
| = | 等于 |
| != | 不等于 |
| < | 小于 |
| <= | 小于或等于 |
| > | 大于 |
| >= | 大于或等于 |
12.按序选择
from lxml import etree
text = '''
<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>
</ul>
</div>
'''
html = etree.HTML(text)
result = html.xpath('//li[1]/a/text()') # 选择第一个li节点,开始是从1开始
print(result)
result = html.xpath('//li[last()]/a/text()') # last()选择最后一个节点
print(result)
result = html.xpath('//li[position()<4]/a/text()') # 选择节点小于4的节点,也就是1、2、3节点
print(result)
result = html.xpath('//li[last()-1]/a/text()') # 选择倒数第二个节点,使用最后一个节点-1
print(result)
--------------
['first item']
['fifth item']
['first item', 'second item', 'third item']
['fourth item']
13.节点轴选择
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html"><span>first item</span></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>
</ul>
</div>
'''
html = etree.HTML(text)
result = html.xpath('//li[1]/ancestor::*') # 选择第一个li节点的所有父节点,ancestor后面需要跟两个':',然后是选择器
print(result)
result = html.xpath('//li[1]/ancestor::div') # 选择第一个li节点的div父节点
print(result)
result = html.xpath('//li[1]/child::*') # 选择第一个li节点的所有直接子节点
print(result)
result = html.xpath('//li[1]/attribute::*') # 选择第一个li节点的所有属性值
print(result)
result = html.xpath('//li[1]/following::*') # 选择第一个li节点之后的所有节点
print(result)
result = html.xpath('//li[1]/following-sibling::') # 选择第一个li节点之后的所有同级节点
print(result)
result = html.xpath('//li[1]/following-sibling::li[@class="item-1"]') # 选择第一个li节点之后的所有class属性为"item-1"的同级节
# 点
print(result)

浙公网安备 33010602011771号