## **解析庫-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']
~~~