
目錄
- lxml庫
- lxml基本用法
- 決議XML檔案
- 決議HTML檔案
- XPath
- 什么是XPath
- XPath語法
- XPath實戰
- 選取某節點的所有子孫節點
- 選取某節點的所有子節點
- 通過屬性選取某節點的父節點
- 多屬性匹配
- XPath運算子規則
- 實戰:爬取CSDN個人博文
- XPath最簡單的玩法
lxml庫
lxml是Python的一個決議庫,專門用于決議XML與HTML,支持XPath決議方式,由于lxml庫的底層是使用C語言撰寫的,所以其決議效率非常的高,
在我們后面講解使用該庫之前,我們需要安裝該庫,一般通過如下命令進行安裝即可,代碼如下:
pip install lxml
lxml基本用法
既然,lxml庫支持決議XML以及HTML,那么肯定就需要學會這2種檔案的決議方式,下面,我們來分別講解,
決議XML檔案
首先,我們需要使用lxml庫決議XML檔案,這里XML檔案其實有很多種類,這里博主隨便定義一個XML進行決議,
XML代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<people>
<zhangsan class="法外狂徒">
<sex>男</sex>
<age>21</age>
</zhangsan>
<lisi class="法外狂徒的伙伴">
<sex>男</sex>
<age>21</age>
</lisi>
</people>
決議示例代碼如下所示:
from lxml import etree
tree = etree.parse("lxml_xml.xml")
print(str(etree.tostring(tree, encoding='utf-8'), 'utf-8'))
root = tree.getroot()
print("根節點", root.tag)
children = root.getchildren()
for child in children:
print("sex:", child[0].text)
print("class :", child.get('class'))
運行之后,效果如下所示:

決議HTML檔案
決議HTML比XML稍微復雜一點,它需要創建一個HTMLParser()物件傳入到parser()方法中,因為其默認是決議XML的,
HTML代碼如下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>我是一個測驗頁面</title>
</head>
<body>
</body>
</html>
決議代碼如下所示:
from lxml import etree
parser = etree.HTMLParser()
tree = etree.parse('demo.html', parser)
root = tree.getroot()
result = etree.tostring(root, encoding='UTF-8', pretty_print=True, method='html')
print(root.tag)
children = root.getchildren()
print("語言:", children[0].get('lang'))
print(root[0][1].text)
運行之后,效果如下所示:

XPath
估計細心的小伙伴,已經看出lxml庫直接使用的弊端了,因為這是小撰寫的一個簡單的HTML與XML,所以它的層級很低,
如果是真實的網頁,那么可能層級會很多,如果還按陣列這樣一層一層往下查找,估計能搞出個十幾維的陣列,這樣太復雜了,
所以,這里我們需要引入XPath進行輔助決議,
什么是XPath
XPath于1991年11月6日稱為W3C標準,它被設計為可以在XSLT、XPointer以及其他XML決議軟體中使用,其中文檔案為:
https://www.w3school.com.cn/xpath/index.asp
XPath全稱XML Path Language,中文叫XML路徑語言,它是一種在XML檔案中查詢資訊的語言,
最初雖然只支持XML檔案,但是后來隨著版本的迭代,已經可以支持HTML檔案的決議與搜索,因為HTML與XML同源,
XPath語法
XPath語言的基本語法就是多級目錄的層級結構,但比陣列那種容易理解的多,下表是博主歸納總結的XPath語法規則:
| 語法 | 意義 |
|---|---|
| nodename | 選取此節點的所有子節點 |
| / | 從當前節點選取直接子節點 |
| // | 從當前節點選取子孫節點 |
| . | 選取當前節點 |
| … | 選取當前節點的父親節點 |
| @ | 選取屬性 |
XPath實戰
既然,我們已經了解XPath具體的語法結構,那么我們將一一實戰這些語法,讓讀者更清晰,更快捷的掌握,
測驗HTML檔案如下所示:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>我是一個測驗頁面</title>
</head>
<body>
<ul>
<li><a href="https://liyuanjinglyj.blog.csdn.net/">我的主頁</a></li>
<li><a href="https://www.csdn.net/">CSDN首頁</a></li>
<li class="li"><a href="https://www.csdn.net/nav/python" class="aaa">Python板塊</a></li>
</ul>
</body>
</html>
選取某節點的所有子孫節點
假設我們需要獲取上面HTML檔案中<ul>標簽的所有<a>子節點的鏈接與文本,我們需要如果去操作呢?示例代碼如下:
from lxml import etree
parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//ul//a")
for index in range(0, len(nodes)):
print("網址:", nodes[index].get('href'), " 文本:", nodes[index].text)
運行之后,效果如下:

雙斜杠“//”代表獲取當前節點下的子孫節點,也就是說,直接在根節點操作,就是獲取根節點下面的所有該標簽,
選取某節點的所有子節點
還是上面這個例子,我們如果使用單斜杠“/”獲取所有的<a>標簽呢?
因為<a>標簽是<li>標簽的子節點,所以我們需要獲取<li>,再通過單斜杠"/"獲取<a>標簽,示例代碼如下:
from lxml import etree
parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//li/a")
for index in range(0, len(nodes)):
print("網址:", nodes[index].get('href'), " 文本:", nodes[index].text)
如上面代碼所示,我們把xpath語法改成“//li/a”即可,運行之后,效果與上面一模一樣,
通過屬性選取某節點的父節點
對于當前節點來說,我們只需要通過其標簽與屬性確認,自然就可以獲取當前節點,所以.這里就不贅述了,
我們直接介紹后兩種語法,通過"@“查找屬性,然后通過”.."查找其父節點,
實戰,通過class等于aaa的節點獲取父親節點,然后獲取其屬性class的值,示例代碼如下所示:
from lxml import etree
parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//a[@class='aaa']/../@class")
print(nodes)
運行之后,效果如下:

除了通過/…獲取父節點之外,我們還可以通過parent::*獲取父節點,那么同樣的轉換語法也可以得到如上圖所示的結果,(把…替換成即可)
多屬性匹配
我們還是來獲取那個有class的<a>標簽,這里使用多屬性匹配原則,
也就是,我們匹配其父節點class等于li以及class等于aaa的標簽<a>,那么如何首先呢?示例如下:
from lxml import etree
parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//a[contains(@class,'aaa') and ../@class='li']")
print(nodes[0].text)
運行之后,效果如下:

看看上面的輸出圖,是不是最后一個<a>標簽的文本內容?這里通過and進行多屬性條件判斷,
XPath運算子規則
不過,這里就涉及XPath運算子規則了,博主這里,也列出了一個專門的運算子規則的表格,方便讀者查閱參考,
| 運算子 | 描述 | 示例 | 意義 |
|---|---|---|---|
| and | 與 | class=‘name’ and href=‘www’ | 如果class等于name并且href等于www,則回傳true,否則false |
| mod | 取余 | 10 mod 3 | 1 |
| or | 或 | class=‘name’ or class=‘www’ | 如果class等于name或者www,則回傳true,都不等于則回傳false |
| div | 除法 | 10 div 5 | 2 |
| + | 加法 | 10+5 | 15 |
| - | 減法 | 10-5 | 5 |
| * | 乘法 | 10*5 | 50 |
| = | 等于 | value=520 | 如果value等于520,回傳true,否則回傳false |
| != | 不等于 | value!=520 | 如果value不等于520,回傳true,否則回傳false |
| < | 小于 | value<520 | 如果value小于520,回傳true,否則回傳false |
| > | 大于 | value>520 | 如果value大于520,回傳true,否則回傳false |
| <= | 小于等于 | value<=520 | 如果value小于等于520,回傳true,否則回傳fals |
| >= | 大于等于 | value>=520 | 如果value大于等于520,回傳true,否則回傳false |
實戰:爬取CSDN個人博文
我們先通過chrome,或者任意瀏覽器按F12打開查看CSDN個人主頁的元素,可以看到,這里的div是整個主頁內容的div,

同時,其下邊的所有子div都是一篇篇作者的博文內容,那么我們就可以先通過class="article-list"找到主頁博文串列,
然后,在一條一條的遍歷子div獲取里面的每篇博文資訊即可,不過,我們首先需要獲取網頁的HTML文本,通過requests進行獲取,
然后,我們再來看看其標題與鏈接到底在哪里?如下圖所示:

可以看到,標題與鏈接都在每個div的<h4>標簽中,而描述資訊在class='content’的<p>標簽中,知道了這些,我們來獲取主頁的所有博文,
示例代碼如下:
from lxml import etree
import requests
url = "https://blog.csdn.net/liyuanjinglyj"
session = requests.session()
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'cache-control': 'max-age=0',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
}
result = requests.get(url, headers=headers)
result.encoding = result.apparent_encoding
html = etree.HTML(result.text)
urlStr = html.xpath("//div[@class='article-list']//div/h4/a/@href")
titleStr = html.xpath("//div[@class='article-list']//div/h4/a/text()")
titleStr = [i for i in titleStr if i.strip() != '']
contentStr = html.xpath("//div[@class='article-list']//div/p[@class='content']/text()")
for url, title, content in zip(urlStr, titleStr, contentStr):
print("博文鏈接:", url)
print("博文標題:", title.strip())
print("博文描述:", content.strip())
運行之后,效果如下:

這里有一個很奇怪的問題,相信大家也發現了,我們titleStr遍歷了2遍,其他的只遍歷的一遍這是為什么呢?我們先來看一張圖:

這里獲取<a>標簽文本的時候,默認是獲取了2個,一個是空,一個才是下面的標題,所以,這里每次獲取<a>標簽文本標題時,都是一個空白,一個標題,
所以,我們在后續遍歷的時候,應該去除掉空白字串,只要標題,
XPath最簡單的玩法
如果你是安裝的Chrome,那么XPath語法,你可以不必學,因為這個瀏覽器可以直接生成XPath,
比如,我們獲取上面的<a>標簽,那么如何獲取呢?只要選中<a>標簽,然后按住右鍵選擇Copy-Copy XPath即可,如下圖所示:

不過,博主不建議這么做,因為這里Copy的XPath僅僅只是針對當前的標簽,而我們上面獲取的標簽是一個有規則的標簽串列,而你不學習的XPath語法的話,這要是有100個串列標簽,你難道還復制XPath語法100次不成?而學習過XPath只需要一行代碼,然后遍歷即可,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/289597.html
標籤:python
上一篇:python:合唱隊
