3.3、xpath
xpath在Python的爬蟲學習中,起著舉足輕重的地位,對比正則表達式 re兩者可以完成同樣的工作,實現的功能也差不多,但xpath明顯比re具有優勢,在網頁分析上使re退居二線。
xpath 全稱為XML Path Language 一種小型的查詢語言
xpath的優點:
- 可在XML中查找信息
- 支持HTML的查找
- 通過元素和屬性進行導航
python開發使用XPath條件: 由于XPath屬于lxml庫模塊,所以首先要安裝庫lxml。
from lxml import etree
selector=etree.HTML(源碼) #將源碼轉化為能被XPath匹配的格式
selector.xpath(表達式) #返回為一列表
【1】路徑表達式
表達式 | 描述 | 實例 | 解析 |
---|---|---|---|
/ | 從根節點選取 | /body/div[1] | 選取根結點下的body下的第一個div標簽 |
// | 從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置 | //a | 選取文檔中所有的a標簽 |
./ | 當前節點再次進行xpath | ./a | 選取當前節點下的所有a標簽 |
@ | 選取屬性 | //@calss | 選取所有的class屬性 |
【2】謂語(Predicates)
謂語用來查找某個特定的節點或者包含某個指定的值的節點。
謂語被嵌在方括號中。
在下面的表格中,我們列出了帶有謂語的一些路徑表達式,以及表達式的結果:
路徑表達式 | 結果 |
---|---|
/ul/li[1] | 選取屬于 ul子元素的第一個 li元素。 |
/ul/li[last()] | 選取屬于 ul子元素的最后一個 li元素。 |
/ul/li[last()-1] | 選取屬于 ul子元素的倒數第二個 li元素。 |
//ul/li[position()??] | 選取最前面的兩個屬于 ul元素的子元素的 li元素。 |
//a[@title] | 選取所有擁有名為 title的屬性的 a元素。 |
//a[@title=‘xx’] | 選取所有 a元素,且這些元素擁有值為 xx的 title屬性。 |
//a[@title>10] > < >= <= != | 選取 a元素的所有 title元素,且其中的 title元素的值須大于 10。 |
/body/div[@price>35.00] | 選取body下price元素值大于35的div節點 |
【3】選取未知節點
XPath 通配符可用來選取未知的 XML 元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素節點。 |
@* | 匹配任何屬性節點。 |
node() | 匹配任何類型的節點。 |
實例
在下面的表格中,我們列出了一些路徑表達式,以及這些表達式的結果:
路徑表達式 | 結果 |
---|---|
/ul/* | 選取 bookstore 元素的所有子元素。 |
//* | 選取文檔中的所有元素。 |
//title[@*] | 選取所有帶有屬性的 title 元素。 |
//node() | 獲取所有節點 |
【4】選取若干路徑
通過在路徑表達式中使用“|”運算符,您可以選取若干個路徑。
實例
在下面的表格中,我們列出了一些路徑表達式,以及這些表達式的結果:
路徑表達式 | 結果 |
---|---|
//book/title | //book/price | 選取 book 元素的所有 title 和 price 元素。 |
//title | //price | 選取文檔中的所有 title 和 price 元素。 |
/bookstore/book/title | //price | 選取屬于 bookstore 元素的 book 元素的所有 title 元素,以及文檔中所有的 price 元素。 |
-
邏輯運算
//div[@id="head" and @class="s_down"] # 查找所有id屬性等于head并且class屬性等于s_down的div標簽 //title | //price # 選取文檔中的所有 title 和 price 元素,“|”兩邊必須是完整的xpath路徑
-
屬性查詢
//div[@id] # 找所有包含id屬性的div節點 //div[@id="maincontent"] # 查找所有id屬性等于maincontent的div標簽 //@class //li[@name="xx"]//text() # 獲取li標簽name為xx的里面的文本內容
-
獲取第幾個標簽 索引從1開始
tree.xpath('//li[1]/a/text()') # 獲取第一個 tree.xpath('//li[last()]/a/text()') # 獲取最后一個 tree.xpath('//li[last()-1]/a/text()') # 獲取倒數第二個
-
模糊查詢
//div[contains(@id, "he")] # 查詢所有id屬性中包含he的div標簽 //div[starts-with(@id, "he")] # 查詢所有id屬性中包以he開頭的div標簽 //div/h1/text() # 查找所有div標簽下的直接子節點h1的內容 //div/a/@href # 獲取a里面的href屬性值 //* #獲取所有 //*[@class="xx"] #獲取所有class為xx的標簽# 獲取節點內容轉換成字符串 c = tree.xpath('//li/a')[0] result=etree.tostring(c, encoding='utf-8') print(result.decode('UTF-8'))
【5】案例
豆瓣Top250基于xpath解析:
import requests
from lxml import etreeurl = "https://movie.douban.com/top250?start=0"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36"
}
resp = requests.get(url, headers=headers)tree = etree.HTML(resp.text) # 加載頁面源代碼items = tree.xpath('//li/div[@class="item"]/div[@class="info"]')for item in items:title = item.xpath('./div[@class="hd"]/a/span[1]/text()')[0]rating_num = item.xpath('./div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()')[0]comment_num = item.xpath('./div[@class="bd"]/div[@class="star"]/span[4]/text()')[0]print(title, rating_num, comment_num)
練習:基于xpath完成解析練習
import requests
from lxml import etreeres = requests.get("https://top.baidu.com/board?platform=pc&sa=pcindex_entry", )selector = etree.HTML(res.text)rets = selector.xpath('//div[@theme="car"]//div[contains(@class,"item-wrap_Z0BrP ")]')info = {}
for i in rets:name = i.xpath('./div[@class="normal_1glFU"]/a/text()')link = i.xpath('./div[@class="normal_1glFU"]/a/@href')info[name[0]] = link[0]print(info)
print(len(info))