第64天: XPath 和 lxml

by 某某白米飯

XPath 和 lxml

XPath 全稱爲 Xml Path Language,即 Xml 路徑語言,是一種在 Xml 文檔中查找信息的語言。它提供了非常簡潔的路徑選擇表達式,幾乎所有的節點定位都可以用它來選擇。

XPath 可以用於 Xml 和 Html,在爬蟲中經常使用 XPath 獲取 Html 文檔內容。

lxml 是 Python 語言用 Xpath 解析 XML、Html文檔功能最豐富的、最容易的功能模塊。

XPath 術語

節點

在 XPath 中有七種節點分別是元素、屬性、文本、文檔、命名空間、處理指令、註釋,前3種節點爲常用節點

請看下面的 Html 例子,(注:這個例子全文都需要使用)

<!DOCTYPE html>
<html>
    <body>
        <div>
            <!-- 這裏是註釋 -->
            <h4>手機品牌商<span style="margin-left:10px">4</span></h4>
            <ul>
               <li>小米</li>
               <li>華爲</li> 
               <li class='blank'> OPPO </li>
               <li>蘋果</li>
            </ul>
        </div>
        <div>
            <h4>電腦品牌商<span style="margin-left:10px">3</span></h4>
            <ul class="ul" style="color:red">
                <li>戴爾</li> 
                <li>機械革命</li> 
                <li>ThinkPad</li>
            </ul>
        </div>
    </body>
</html>

在上面的例子中

<html> 爲文檔節點
<li>小米</li> 爲元素節點
class='blank' 爲屬性節點
<!-- 這裏是註釋 --> 爲註釋節點

節點關係

在 XPath中有多中節點關係分別是父節點、子節點、同胞節點、先輩節點、後代節點

在上面的例子中

  1. 父節點:每個元素以及屬性都有一個父節點,如:div 節點的父節點是 body 節點
  2. 子節點:元素節點可有零個、一個或多個子節點,如:第一個 ul 節點的子節點是4個 li 節點
  3. 同胞節點:擁有相同的父節點的節點,如:兩個 div 節點是同胞節點
  4. 先輩節點:某節點的父節點、父節點的父節點...,如:ul 節點的先輩節點有 div 節點、body 節點
  5. 後代節點:某個節點的子節點,子節點的子節點...,如:body 節點的後代節點有div 節點、ul 節點、li 節點

XPath 語法

基本語法

表達式 描述
nodeName 選擇nodeName節點的所有子節點
/ 從根節點開始
// 從匹配的節點開始選擇節點
. 選擇當前節點
.. 選擇當前節點的父節點
@ 選擇元素
* 匹配任意元素節點
@* 匹配任意屬性節點

用上面的 Html 文檔舉個例子

路徑表達式 描述
body 選取 body 的所有子節點
/html 選取 html 節點
//div 選取所有 div 節點
//div/./h4 div 節點下的 h4 節點
../div 選取當前節點的父節點下的所有 div 節點
//@class 所有帶有 class 元素的節點
//* 選擇所有節點
//@* 選擇所有屬性節點

常用函數

表達式 描述
position() 返回節點的 index 位置
last() 返回節點的個數
contains(string1,string2) string1 是否包含 string2
text() 返回文本節點
comment() 返回註釋節點
normalize-space(string) 去除首位空格,中間多個空格用一個空格代替
substring(string,start,len) 返回從 start 位置開始的指定長度的子字符串,第一個字符下標爲1
substring-before(string1,string2) 返回string1中位於第一個string2之前的部分
substring-after(string1,string2) 返回string1中位於第一個string2之後的部分

同樣用上面的Html文檔舉個例子

路徑表達式 描述
//div[position()>1] 選擇第二個 div 節點
//div[last()] 選擇最後一個 div 節點
contains(//h4[2],'手機') 第二個 h4 標籤是否包含手機字符串
//li/text() li 節點中的文本內容
//div/comment() div 節點下的 html 註釋
normalize-space(//li[@class='blank']) li 節點下 class屬性爲 blank 的文本去掉空格
substring(//h4[1],1,2) 第一個 h4 節點的前2個字
substring-before(//h4[1],'品牌商') 第一個 h4 節點的品牌商字符串之前的字符串
substring-after(//h4[1],'品牌商') 第一個 h4 節點的品牌商字符串之後的字符串

謂語

XPath 中的謂語就是刪選表達式,相當於 SQL 中的 Where 條件,謂語被嵌在 [ ] 中

路徑表達式 描述
//div[1] 選擇第一個 div 節點
//div[2]/ul/li[last()] 選擇第二個 div 節點下的最後一個 li 節點
//div[2]/ul/li[position()>3] 選擇第二個 div 節點下的前兩個 li 節點
//ul[@class] 選擇所有帶 class 屬性的 ul 節點
//ul[@class='computer'] 選擇 class 屬性爲 computer 的 ul 節點
//h4[span = 4] 選擇 h4 節點下 span 值等於4的節點

Xpath 結語

以上內容介紹了 XPath 的基本語法,下面將介紹 XPath 如何在 Python 中使用。

lxml 模塊

安裝

sudo pip3 install lxml==4.4.1

解析 HTML 文檔

lxml.etree 一個強大的 Xml 處理模塊,etree 中的 ElementTree 類是一個主要的類,用於對XPath的解析、增加、刪除和修改節點。

from lxml import etree

etree.parse() 函數可以解析一個網頁文件還可以解析字符串, 在網頁中下載的數據一般都是字符串形式的,使用 parse(StringIO(str)) 將整個頁面內容解析加載構建一個 ElementTree 對象,ElementTree 可以使用 XPath 語法精準找到需要的數據。

1.加載頁面到內存

from lxml import etree
from io import StringIO

test_html = '''
<html>
    <body>
        <div>
            <!-- 這裏是註釋 -->
            <h4>手機品牌商<span style="margin-left:10px">4</span></h4>
            <ul>
               <li>小米</li>
               <li>華爲</li>
               <li class='blank'> OPPO </li>
               <li>蘋果</li>
            </ul>
        </div>
        <div>
            <h4>電腦品牌商<span style="margin-left:10px">3</span></h4>
            <ul class="ul" style="color:red">
                <li>戴爾</li>
                <li>機械革命</li>
                <li>ThinkPad</li>
            </ul>
        </div>
    </body>
</html>'''

html = etree.parse(StringIO(test_html))
print(html)

結果:

<lxml.etree._ElementTree object at 0x10bd6b948>

2.獲取所有 li 標籤數據

li_list = html.xpath('//li')

print("類型:")
print(type(li_list))

print("值:")
print(li_list)

print("個數:")
print(len(li_list))


for l in li_list:
    print("li文本爲:" + l.text)

結果:

類型:
<class 'list'>
值:
[<Element li at 0x10543c9c8>, <Element li at 0x10543ca08>, <Element li at 0x10543ca48>, <Element li at 0x10543ca88>, <Element li at 0x10543cac8>, <Element li at 0x10543cb48>, <Element li at 0x10543cb88>]
個數:
7
li文本爲:小米
li文本爲:華爲
li文本爲: OPPO 
li文本爲:蘋果
li文本爲:戴爾
li文本爲:機械革命
li文本爲:ThinkPad

3.獲取帶 class='blank' 屬性數據

blank_li_list = html.xpath('//li[@class="blank"]')

print("類型:")
print(type(blank_li_list))

print("值:")
print(blank_li_list)

print("個數:")
print(len(blank_li_list))


for l in blank_li_list:
    print("li文本爲:" + l.text)

結果:

類型:
<class 'list'>
值:
[<Element li at 0x105253a48>]
個數:
1
li文本爲: OPPO 

4.屬性操作

ul = html.xpath('//ul')[1]
#遍歷屬性
for name, value in ul.attrib.items():
    print('{0}="{1}"'.format(name, value))

#添加新的屬性
ul.set("new_attr", "true")
# 獲取單個屬性
new_attr = ul.get('new_attr')
print(new_attr)

結果:

class="ul"
style="color:red"
true

5.獲取最後一個div標籤數據

last_div = html.xpath('//div[last()]')
print("TAG:")
print(last_div.tag)
print("值:")
print(last_div.text)

結果

div
值:

   

6.添加子節點

child = etree.Element("child")
child.text = "這裏是新的子元素"
last_div.append(child)
# 在最後一個 div 標籤查找新的子元素
clild_text = last_div.find("child").text
print(clild_text)

7.刪除子元素

# 查找並設置第一個查詢到的元素
first_ul = html.find("//ul")
ul_li = first_ul.xpath("li")
for li in ul_li:
    # 刪除元素
    first_ul.remove(li)

ul_li = first_ul.xpath("li")
if len(ul_li) == 0:
    print("元素被刪除了")

8.遍歷元素後代

body = html.find("body")
for sub in body.iter():
    print(sub.tag)
    print(sub.text)

結果

body

        
div

            
<cyfunction Comment at 0x10c374b10>
 這裏是註釋 
h4
手機品牌商
span
4
ul
...

工具

  1. 在 google 瀏覽器開發者模式下,Elements 界面選擇元素後右鍵 Copy,可以 Copy 元素的 XPath 路徑
  2. XPath Helper 是一個 google 瀏覽器插件,可以驗證 XPath 是否正確

總結

  1. 學習了 XPAth 的知識,可以快速匹配單個或多個元素節點和屬性,在工作中大大加快了工作的效率。
  2. lxml 是一個 Python 中強大的 Xml 和 Html 處理模塊,結合 XPath 的使用在程序中快速、便捷的分析、修改網頁內容。

示例代碼:Python-100-days-day064

關注公衆號:python技術,回覆"python"一起學習交流

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章