## **解析库-XPath的基本使用(1)**
#### **1.XPth常用规则**
表中列举了XPath的几个常用规则。
|名称|参数描述|
|:---- |:---|
| nodename | 选取此节点的所有子节点
| / |从当前节点选取直接子节点
|//|从当前节点选取子孙节点
|.|选取当前节点
|..|选取当前节点的父节点
|@|选取属性
示例:
//title[@lang=' eng']
这就是一个XPath规则,它代表选择所有名称为title,同时属性lang 的值为eng的节点。
后面会通过Python的lxml库,利用XPath进行HTML的解析。
#### **2.实例**
~~~
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) #构造XPath 解析对象
#这里我们调用tostring()方法即可输出修正后的HTML代码,但是结果是bytes类型。这里利用decode()方法将其转成str类型,结果如下:
result = etree.tostring(html)
print(result.decode('utf-8'))
~~~
#### **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) #构造XPath 解析对象
#这里我们调用tostring()方法即可输出修正后的HTML代码,但是结果是bytes类型。这里利用decode()方法将其转成str类型,结果如下:
result = etree.tostring(html)
result.decode('utf-8')
result = html.xpath(' //*') #匹配所有的节点
print(result)
result = html.xpath('//li') #匹配所有的li节点
print(result)
~~~
#### **4.子节点**
我们通过/或//即可查找元素的子节点或子孙节点。假如现在想选择li节点的所有直接a子节点,
可以这样实现:
~~~
result = html.xpath('//li/a')
print(result)
~~~
注意:如果使用
~~~
result = html.xpath('//ul/a')
print(result)
~~~
这样是无法获取任何到输出结果的
#### **5.父节点**
我们知道通过连续的/或//可以查找子节点或子孙节点,那么假如我们知道了子节点,怎样来查
找父节点呢?这可以用..来实现。
~~~
#比如,现在首先选中href 属性为link4.html的a节点,然后再获取其父节点,然后再获取其class属性,相关代码如下:
result = html.xpath('//a[@href="link4.html"]/../@class')
print(result)
运行结果如下:
[' item-1']
~~~
#### **6.节点属性**
在选取的时候,我们还可以用@符号进行属性过滤。比如,这里如果要选取class为item-1 的li
节点,可以这样实现:
~~~
result = html.xpath('//li[@class="item-0"]')
print(result)
~~~
这里我们通过加入[@class=" item-0"],限制了节点的class属性为item-0,而HTML文本中符合
条件的li节点有两个,所以结果应该返回两个匹配到的元素。结果如下:
[<Element li at 0x10a399288>, <Element li at 0x10a3992c8>]
可见,匹配结果正是两个。
#### **7.文本获取**
我们用XPath中的text()方法获取节点中的文本,接下来尝试获取前面li节点中的文本,相关
代码如下:
~~~
result = html .xpath('//li[@class="item-0"]/text()')
print(result)
输出结果为:
['\n']
~~~
为什么会只匹配一个到换行符???
因为XPath中
text()前面是/,而此处/的含义是选取直接子节点,很明显li的直接子节点都是a节点,文本都是在
a节点内部的,所以这里匹配到的结果就是被修正的li节点内部的换行符,因为自动修正的li节点
的尾标签换行了。
即选中的是这两个节点:
```
<li class=" item-0" ><a href="link1.html">first item</a></li>
<li class="item-0"><a href= "link5.html">fifth item</a>
```
其中一个节点因为自动修正,li节点的尾标签添加的时候换行了,所以提取文本得到的唯一结果
就是li节点的尾标签和a节点的尾标签之间的换行符。
<span style="color:red;">正确的操作方法:</span>
~~~
因此,如果想获取li节点内部的文本,就有两种方式,一种是先选取a节点再获取文本,另一
种就是使用//。接下来,我们来看下二者的区别。
首先,选取到a节点再获取文本,代码如下:
result = html.xpath(' //li[@class="item-0" ]/a/text()')
print(result)
运行结果如下:
['first item', 'fifth item']
~~~
~~~
这里我们是逐层选取的,先选取了li节点,又利用/选取了其直接子节点a,然后再选取其文本,
得到的结果恰好是符合我们预期的两个结果。
再来看下用另一种方式(即使用// )选取的结果,代码如下:
result = html.xpath('//li[@class="item-0" ]//text()')
print(result)
运行结果如下:
['first item', 'fifth item', '\n']
~~~