三:數據解析之xpath
解析
1.xpath
介紹:
- ?
xpath
是XML
路徑語言,它可以用來確定xml
文檔中的元素位置,通過元素路徑來完成對元素的查找,HTML
就是XML
的一種實現方式,所以xpath
是一種非常強大的定位方式 - ?
XPath
(XML Path Language
)是一種XML
的查詢語言,它能在XML
樹狀圖中尋找節點。XPath
用于在XML
文檔中通過元素和屬性進行導航 - ?
xml
是一種標記語法的文本格式,xpath
可以方便的定位xml
中的元素和其中的屬性值。lxml
是Python中的一個第三方模塊,它包含了將html
文本轉成xml
對象,和對對象執行xpath
的功能
lxml
的安裝:
#在終端輸入
pip install lxml
xpath
的弊端:
? 當我們在批量獲取數據的時候,如果存在的特別數據比較多,這個時候只用xpath
的話,會無法滿足用戶的需求,所以針對于不同的網頁,我們要靈活的去運用我們的數據解析方式
(1)HTML
樹狀結構圖:
? HTML
的結構就是樹形結構,HTML
是根節點,所有的其它元素節點都是從根節點發出的,其它的元素都是這棵樹上的節點,每個節點還可能有屬性和文本值,而路徑就是指某個節點到另一個節點的路線
(2)節點之間的關系:
- ? 父節點:
HTML
是 body 和head
節點的父節點 - ? 子節點:
head
和body
是HTML
的子節點 - ? 兄弟節點:擁有相同的父節點,
head
和body
就是兄弟節點,title
和div
不是兄弟,因為他們不是同一個父節點 - ? 祖先節點:
body
是form
的祖先節點,爺爺輩及以上 - ? 后代節點:
form
是HTML
的后代節點,孫子輩及以下
2.Xpath
中的絕對路徑與相對路徑 :
? Xpath
中的絕對路徑是從HTML
根節點開始算的;而相對路徑(使用的更多)則是從任意節點開始的。通過開發者工具,我們可以拷貝到Xpath
的絕對路徑和相對路徑代碼:
? 注意: 絕對路徑是以 Elements
為基準去尋找的,我們爬蟲獲取的是右鍵的網頁源代碼;右鍵的網頁源代碼 != Elements
,Elements
是前端頁面最終渲染的結果,它與網頁源代碼是有屬性上的差異的;但右鍵的網頁源代碼與Elements
是非常相似的,但是在某些元素或者元素屬性上會存在不同。這就會導致我們直接右鍵復制的xpath
獲取不到真正的數據;所以說只能手寫,不能復制(把數據解析全部學會之后,可以復制,因為到那個時候就有能力對復制到的內容進行微調了)
(1)絕對路徑(了解即可):
? 在Xpath
中最直觀的定位策略就是絕對路徑,絕對路徑是從根節點/html
開始往下一層層的表示,直到出來需要的節點為止
(2)相對路徑(常用):
? 在Xpath
中相對路徑方法以 “//” 開頭,相對路徑可以從任意的節點開始,一般會選取一個可以唯一定位到的元素開始寫,這樣可以增加查找的準確性
相對路徑的定位語法:
(1)基本定位語法:
表達式 | 說明 | 舉例 |
---|---|---|
/ | 從根節點開始選取 | /html/div/span |
// | 從任意節點開始選取 | //input |
. | 選取當前節點 | |
.. | 選取當前節點的父節點 | //input/.. 選取input 的父節點 |
@ | 選取屬性或者根據屬性選取 | //input[@data] 選取具備data 屬性的input 元素 //@data 選取所有data 屬性 |
* | 通配符,表示任意節點或任意屬性 |
(2)元素屬性定位:
(3)層級屬性結合定位:
? 遇到某些元素無法精確定位的時候,可以查找其父級及其祖先節點,找到有確定的祖先節點后通過層級依次向下定位
示例:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="search" id="form" method="post"><span class="bg"><span class="soutu">搜索</span></span><span class="soutu"><input type="text" name="key" id="su"></span><div></div>
</form>
</body>
</html>
圖片解析:
(4)使用謂語定位:
? 謂語是Xpath
中用于描述元素位置,主要有數字下標、最后一個子元素last()
、元素下標函數position()
? 注意: Xpath
中的下標從 1 開始
圖片解析:
1、使用下標的方式,從form找到input:
//form[@id="form"]/span[2]/input2、查找最后一個子元素,選取form下的最后一個span:
//form[@id="form"]/span[last()]3、查找倒數第幾個子元素,選取 form下的倒數第二個span:
//form[@id="form"]/span[last()-1]4、使用 position() 函數,選取 from 下第二個span:
//form[@id="form"]/span[position()=2]5、使用 position() 函數,選取下標大于 2 的span:
//form[@id="form"]/span[position()>2]
(5)使用邏輯運算符定位:
? 用于嵌套的標簽,如果元素的某個屬性無法精確定位到這個元素,還可以用邏輯運算符and
連接多個屬性進行定位
以百度首頁為例:
使用and:
//*[@name='wd' and @class='s_ipt']
#查找 name 屬性為 wd 并且 class 屬性為 s_ipt 的任意元素使用or:
//*[@name='wd' or @class='s_ipt']
#查找 name 屬性為 wd 或者 class 屬性為 s_ipt 的任意元素,取其中之一滿足即可
以上述示例代碼為例:
使用|同時查找多個路徑,取或:
//form[@id="form"]//span | //form[@id="form"]//input
(6)使用文本定位:
? 我們在爬取網站使用Xpath
提取數據的時候,最常使用的就是Xpath
的text()
方法,該方法可以提取當前元素的信息,但是某些元素下包含很多嵌套元素,這時候就用到了string()
方法
爬取別逗了網站示例代碼:
import requests
from lxml import etreeurl = 'https://www.biedoul.com/article/180839'headers= {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
}response = requests.get(url,headers=headers)
response.encoding = 'utf-8' # 在requests.get的時候,會默認指定一個編碼,但默認指定的編碼不一定會是utf-8,是隨機的# 將獲取的網頁源代碼html文件轉換成xml對象,方便后續執行xpath語法
html = etree.HTML(response.text)
data = html.xpath('//div[@class="cc2"]//text()') # //text()指的是取標簽中的文本值,不是屬性值
# print(data)
#
# data = [i.replace('\r\n','') for i in data]
# print('\n'.join(data))data1 = html.xpath('//div[@class="cc2"]')[0].xpath('string(.)')
print(data1)
? 注意: xpath
對象獲取的數據返回的是一個列表
(7)使用部分匹配函數:
函數 | 說明 | 示例 |
---|---|---|
contains | 選取屬性或者文本包含某些字符 | //div[contains(@id, 'data')] 選取id 屬性包含data 的div 元素 |
starts-with | 選取屬性或者文本以某些字符開頭 | //div[starts-with(@id, 'data')] 選取id 屬性以data 開頭的div 元素 |
ends-with | 選取屬性或者文本以某些字符結尾 | //div[ends-with(@id, 'require')] 選取id 屬性以require 結尾的div 元素 |
3.lxml
的使用與xpath
實戰:
(1)lxml
的基本使用:
# 導入模塊
from lxml import etree
# html源代碼
web_data = """<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轉成xml對象
element = etree.HTML(web_data)
# print(element)
# 獲取li標簽下面的a標簽的href
links = element.xpath('//ul/li/a/@href')
print(links) # 列表
# 獲取li標簽下面的a標簽的文本數據
result = element.xpath('//ul/li/a/text()')
print(result)
(2)xpath
實戰 – 豆瓣top250示例代碼:
import requests
from lxml import etree
'''
目標:熟悉xpath解析數的方式
需求:爬取電影的名稱 評分 引言 詳情頁的url 翻頁爬取1-10頁 保存到列表中如何實現?
設計技術與需要的庫 requests lxml(etree)實現步驟
1 頁面分析(一般講數據解析模塊 都是靜態頁面)1.1 通過觀察看網頁源代碼中是否有我們想要的數據 如果有就分析這個url如果沒有再通過ajax尋找接口 通過分析數據在網頁源代碼中1.2 確定目標urlhttps://movie.douban.com/top250?start=0&filter= 第一頁通過頁面分析發現所有我們想要的數據都在一個div[class="info"]里面具體實現步驟
1 獲取整個網頁的源碼 html
2 將獲取的數據源碼轉成一個element對象(xml)
3 通過element對象實現xpath語法 對數據進行爬取(標題 評分 引言 詳情頁的url)
4 保存數據 先保存到字典中-->列表中'''# 定義一個函數用來獲取網頁源代碼
def getsource(pagelink):# 請求頭headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'}# 獲取源碼response = requests.get(pagelink, headers=headers)response.encoding = 'utf-8'html = response.textreturn html# 定義一個函數用于解析我們的網頁源代碼并獲取我們想要的數據
def geteveryitem(html):element = etree.HTML(html)# 拿到[class="info"]的所有divmovieitemlist = element.xpath('//li//div[@class="info"]')# print(movieitemlist,len(movieitemlist))# 定義一個列表itemlist = []for item in movieitemlist:# 定義一個字典itemdict = {}# 標題title = item.xpath('./div[@class="hd"]/a/span[@class="title"]/text()')title = "".join(title).replace("\xa0", "")# print(title)# 副標題othertitle = item.xpath('./div[@class="hd"]/a/span[@class="other"]/text()')[0].replace("\xa0", "")# print(othertitle)# 評分grade = item.xpath('./div[@class="bd"]/div[@class="star"]/span[2]/text()')[0]# print(grade)# 詳情頁的urllink = item.xpath('div[@class="hd"]/a/@href')[0]# print(link)# 引言quote = item.xpath('div[@class="bd"]/p[@class="quote"]/span/text()')# print(quote)# list index out of range# 處理方式1 非空處理if quote:quote = quote[0]else:quote = ""# 將數據存放到字典中itemdict['title'] = ''.join(title + othertitle)itemdict['grade'] = gradeitemdict['link'] = linkitemdict['quote'] = quote# print(itemdict)itemlist.append(itemdict)# print(itemlist)return itemlistif __name__ == '__main__':url = 'https://movie.douban.com/top250?start=0&filter='html = getsource(url)itemlist = geteveryitem(html)print(itemlist)
dict['quote'] = quote# print(itemdict)itemlist.append(itemdict)# print(itemlist)return itemlistif __name__ == '__main__':url = 'https://movie.douban.com/top250?start=0&filter='html = getsource(url)itemlist = geteveryitem(html)print(itemlist)