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中有多中節點關係分別是父節點、子節點、同胞節點、先輩節點、後代節點
在上面的例子中
- 父節點:每個元素以及屬性都有一個父節點,如:div 節點的父節點是 body 節點
- 子節點:元素節點可有零個、一個或多個子節點,如:第一個 ul 節點的子節點是4個 li 節點
- 同胞節點:擁有相同的父節點的節點,如:兩個 div 節點是同胞節點
- 先輩節點:某節點的父節點、父節點的父節點...,如:ul 節點的先輩節點有 div 節點、body 節點
- 後代節點:某個節點的子節點,子節點的子節點...,如: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
...
工具
- 在 google 瀏覽器開發者模式下,Elements 界面選擇元素後右鍵 Copy,可以 Copy 元素的 XPath 路徑
- XPath Helper 是一個 google 瀏覽器插件,可以驗證 XPath 是否正確
總結
- 學習了 XPAth 的知識,可以快速匹配單個或多個元素節點和屬性,在工作中大大加快了工作的效率。
- lxml 是一個 Python 中強大的 Xml 和 Html 處理模塊,結合 XPath 的使用在程序中快速、便捷的分析、修改網頁內容。
關注公衆號:python技術,回覆"python"一起學習交流