解析庫XPath【全】

解析庫

對於網頁的節點來說,它可以定義id、 class或其他屬性。而且節點之間還有層次關係,在網頁中可以通過XPath或CSS選擇器來定位(或提取)一個或多個節點。然後再調用相應方法獲取它的正文內容或者屬性,就可以提取我們想要的信息。

[第四部分 解析庫的使用(XPath、Beautiful Soup、PyQuery][https://www.cnblogs.com/Micro0623/p/10496376.html]

XPath

全稱 XML Path Language,XML 路徑語言

最初是設計用來搜尋XML文檔,同樣適用於HTML文檔的搜索(也可以說用於信息抽取),所以爬蟲也可以適用

XPath對應的庫是lxml庫

XPath的選擇功能十分強大,提供了非常簡潔明瞭的路徑選擇表達式。另外,還提供了超過100個內建函數,用於字符串、數值、時間的匹配以及節點、序列的處理等。幾乎所有想要定位的節點,都可以用XPath來選擇。

1. 概念

xpath簡介:

  1. XML路徑語言,擁有在數據結構樹中查找節點的能力

  2. 被開發者當作小型查詢語言來使用

  3. XPath通過元素和屬性進行導航

  4. 通常來說,lxml是抓取數據的最好選擇,因爲該方法既快速又健壯。

爲什麼學習Xpath:

1、支持html

2、比正則表達式簡單,強大

3、scrapy

Xpath的基本概念:

節點:

​ Parent(父)Children(子)Sibling(同胞)Ancestor(先輩)Descendant(後代)

​ 路徑表達式:XPath 使用路徑表達式來選取 XML 文檔中的節點或節點集。節點是通過沿着路徑 (path) 或者步 (steps) 來選取的。

2. 官方學習網址

https://www.w3school.com.cn/xpath/index.asp

3. XPath組成

3.1 表達式

在這裏插入圖片描述

3.2 路徑表達式

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1vwIMhX5-1592754095075)(C:\Users\sevier_yang_laptop\Documents\WXWork\1688852877258705\WeDrive\正研數據\3. 爬蟲\images\1585204947444.png)]

3.3 謂語

謂語(Predicates)

謂語用來查找某個特定的節點或者包含某個指定的值的節點
謂語被嵌在方括號中

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-36Cjjgwn-1592754095076)(C:\Users\sevier_yang_laptop\Documents\WXWork\1688852877258705\WeDrive\正研數據\3. 爬蟲\images\1585206905191.png)]

3.4 選取未知節點,通配符

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-5KPauMLH-1592754095078)(C:\Users\sevier_yang_laptop\Documents\WXWork\1688852877258705\WeDrive\正研數據\3. 爬蟲\images\1585206914752.png)]

3.5 選取若干路徑

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-AoVAajVK-1592754095081)(C:\Users\sevier_yang_laptop\Documents\WXWork\1688852877258705\WeDrive\正研數據\3. 爬蟲\images\1585206934860.png)]

4. 實操項目

常用方法

#!/usr/bin/env python
#-- coding:utf-8 --

# 可以考慮借鑑這種署名格式

"""
    @Author  : LiuZhian
    @Time    : 2019/4/24 0024 上午 9:19
    @Comment :
"""


import lxml.etree as etree

html = """
<!DOCTYPE html>
<html>
<head lang="en">
    <title>xpath測試</title>
</head>
<body>
<div id="content">
    <ul id="ul">
        <li>NO.1</li>
        <li>NO.2</li>
        <li>NO.3</li>
    </ul>
    <ul id="ul2">
        <li>one</li>
        <li>two</li>
    </ul>
</div>
<div id="url">
    <a href="http:www.58.com" title="58">58</a>
    <a href="http:www.csdn.net" title="CSDN">CSDN</a>
</div>
</body>
</html>

"""

print(type(html))

# 傳入參數,調用etree模塊下的HTML方法,生成選擇器對象selector
selector = etree.HTML(html)

# 截取內容 no.1 # 運用路徑表達式(節點+表達式)和謂語 用 @ 符號進行屬性過濾
str = selector.xpath('//div[@id="content"]/ul[@id="ul"]/li/text()')[0] # 之所以用索引,是因爲返回的是列表
print(str)

# 截取內容 no.2
str2 = selector.xpath('//div[@id="content"]/ul[@id="ul"]/li/text()')[1]
print(str2)

# 截取內容 one
str3 = selector.xpath('//div[@id="content"]/ul[@id="ul2"]/li/text()')[0]
print(str3)

# 第二種方法截取內容 one,也可以的,是利用//獲取子孫節點
str3_1 = selector.xpath('//ul[@id="ul2"]/li/text()')[0]
print(str3_1)

# 測試,不加屬性=值
str3_2 = selector.xpath('//ul/li/text()') # 獲取所有ul裏面的li的文本,返回列表
print(str3_2)
# ['NO.1', 'NO.2', 'NO.3', 'one', 'two']

# 截取內容 CSDN
str4 = selector.xpath('//div[@id="url"]/a/text()')
print(str4)
# ['58', 'CSDN']

# 截取標題 測試
str5 = selector.xpath('//title/text()')
print(str5)
# ['xpath測試']

# 重要:獲取文件中div的屬性id爲”url“裏面的所有a標籤的href屬性  # a[@href=] 獲取到哪個a ,不對!!!/text() 這個也不對
# 屬性匹配是中括號加屬性名和值來限定某個屬性,如 [@href="link1.html"],而此處的 @href 指的是獲取節點的某個屬性值,注意二者的區分。

str6 = selector.xpath('//div[@id="url"]/a/@href')
print(str6)
# ['http:www.58.com', 'http:www.csdn.net']

# 試驗 // 和 *
# 讀取所有節點, 整個HTML文本中所有節點都會被獲取,每個原始都是element對象
str7 = selector.xpath('//*') # // Invalid expression
print(str7)

# 沒讀文本,text()和 屬性,肯定就沒有啊,傻逼


### 多個可以按序選擇,就是使用方括號[],謂語

result21 = selector.xpath('//ul/li[3]/@class') # 注意 @之後是 屬性名
print(result21)
# [<Element html at 0x1ed2eff4d88>, <Element body at 0x1ed2eff4ec8>, <Element div at 0x1ed2eff4f08>]

# 一個屬性 多值
# contains()函數,修改代碼如下:
from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class, "li")]/a/text()')
print(result)           # 輸出結果是:['first item']

通過contains()方法,第一個參數傳入屬性名稱,第二個參數傳入屬性值,只要此屬性包含所傳入的屬性值,就可以完成匹配。

# # 多個屬性 多值
根據多個屬性確定一個節點,需要同時匹配多個屬性,可使用運行符 and 來連接。示例如下:
from lxml import etree
text = '''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
'''
html = etree.HTML(text)
result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
print(result)               # 輸出是:['first item']

這裏的li節點增加了一個屬性name。要確定這個節點,需要同時根據class和name屬性來選擇,一個條件是class屬性裏面包含li字符串,另一個條件是name屬性爲item字符串,二者需要同時滿足,需要用and操作符相連,相連之後置於中括號內進行條件篩選。




# 父節點


img

還有 按序選擇,屬於謂語

li[last()]
li[position<3]
li[last()-2]

在一些匹配任務中,可能在某些屬性會同時匹配多個節點,但是隻想要其中的某個節點,如第二個節點或第一個節點,這時可利用中括號傳入索引的方法獲取特定次序的節點。注意這裏的索引是從1開始


text = """
<div>
<ul>
<li class="item-0"><a href="link1.html"><span>first item</span></a></lis>
<li class="item-1"><a href="link2.html"><span>second item</span></a></lis>
<li class="item-inactive"><a href="link3.html"><span>third item</span></a></lis>
<li class="item-1"><a href="link4.html"><span>fourth item</span></a></lis>
<li class="item-0"><a href="link5.html"><span>fifth item</span></a></lis>


</ul>
</div>

"""

其他方法

如果節點需要修正,補充?etree.tostring()
# 針對:li節點沒有閉合,需要自動修正

from lxml import etree

html = etree.HTML(text)         # 調用HTML類初始化,自動修正HTML文本
result = etree.tostring(html)   # 輸出修正後的HTML代碼,是bytes類型print(result.decode("utf-8"))

調用tostring()方法即可輸出修正後的HTML代碼,結果是bytes類型。利用decode()方法將其轉成str類型,在輸出結果中,經過處理後,li節點的標籤被補全,並且還自動添加了body、html節點
如何直接讀取文本文件進行解析? etree.parse()
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
節點軸的各種選擇方法

[XPath 教程][https://www.runoob.com/xpath/xpath-tutorial.html]

[XPath 軸(Axes)][https://www.runoob.com/xpath/xpath-axes.html]

### 節點軸選擇方法,選取 兄弟元素,父元素,子元素,祖先元素
## 調用ancestor軸
# 選取所有祖先節點 ancestor::*
result3 = selector.xpath('//ul/ancestor::*')
# print(result3)
# [<Element html at 0x1ed2eff4d88>, <Element body at 0x1ed2eff4ec8>, <Element div at 0x1ed2eff4f08>]

# 選取ancestor裏面的div
result4 = selector.xpath('//ul/ancestor::div')
# print(result4)
# [<Element div at 0x1631b1e4f08>]

## 調用attribute軸
# 選取某節點裏的所有屬性
result5 = selector.xpath('//ul/li/attribute::*') # ul沒有屬性,li有屬性
# print(result5)


# 選取某節點裏的所有屬性
result6 = selector.xpath('//ul/li[@class="item-0"]/a/span/text()') # /子節點子節點,子孫節點不行 都是['\n', '\n\n\n']
# print(result6)
# ['first item', 'fifth item']


## 調用child軸
# 選取 屬性爲link4的 a節點
result7 = selector.xpath('//ul/li/child::a[@href="link4.html"]')
# print(result7)

## 調用descendant軸
# 選取 屬性爲link4的 a節點
# result8 = selector.xpath('//ul/li/descendant::a')
result8 = selector.xpath('//ul/li/descendant::*')
# print(result8)

## 調用following軸
# following 相當於 當前節點,下面的所有節點
# result9 = selector.xpath('//ul/li[@class="item-inactive"]/following::*')
# result9 = selector.xpath('//li[1]/following::*[1]')
# result10 = selector.xpath('//li[1]/following::*[2]')
# result11 = selector.xpath('//li[1]/following::*[3]')
# print(result9, result10, result11)
# [<Element li at 0x1da39fa4fc8>] [<Element a at 0x1da39fe7508>] [<Element span at 0x1da39fe7548>]
#

## following-sibling節點,當前節點之後的所有兄弟節點
# result9 = selector.xpath('//ul/li[@class="item-inactive"]/following::*')
result9 = selector.xpath('//li[1]/following-sibling::*')
result10 = selector.xpath('//li[1]/following::*[1]')
result11 = selector.xpath('//li[1]/descendant::*')
print(result9)
print(result10)
print(result11)
# [<Element li at 0x1da39fa4fc8>] [<Element a at 0x1da39fe7508>] [<Element span at 0x1da39fe7548>]

有用的鏈接
https://blog.csdn.net/qq_25343557/article/details/81912992?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

https://blog.csdn.net/baidu_32542573/article/details/79675420

https://www.cnblogs.com/dxqNet/p/10136665.html

http://www.voidcn.com/article/p-cjeefole-btv.html

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