解析庫
對於網頁的節點來說,它可以定義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簡介:
-
XML路徑語言,擁有在數據結構樹中查找節點的能力
-
被開發者當作小型查詢語言來使用
-
XPath通過元素和屬性進行導航
-
通常來說,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 路徑表達式
3.3 謂語
謂語(Predicates)
謂語用來查找某個特定的節點或者包含某個指定的值的節點
謂語被嵌在方括號中
3.4 選取未知節點,通配符
3.5 選取若干路徑
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操作符相連,相連之後置於中括號內進行條件篩選。
# 父節點
還有 按序選擇,屬於謂語
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