首頁(yè)技術(shù)文章正文

python beautifulsoup庫(kù)的作用和用法詳細(xì)介紹

更新時(shí)間:2021-06-21 來(lái)源:黑馬程序員 瀏覽量:

使用lxml庫(kù)時(shí)需要編寫(xiě)和測(cè)試XPath語(yǔ)句,顯然降低了開(kāi)發(fā)效率。除了lxml庫(kù)之外,還可以使用Beautiful Soup來(lái)提取HTML/XML數(shù)據(jù)。雖然這兩個(gè)庫(kù)的功能相似,但是Beautiful Soup使用起來(lái)更加簡(jiǎn)潔方便,受到開(kāi)發(fā)人員的推崇。

Beautiful Soup概述

截止到目前,BeautifulSoup(3.2.1版本)已經(jīng)停止開(kāi)發(fā),官網(wǎng)推薦現(xiàn)在的項(xiàng)目使用beautifulsoup4( Beautiful Soup4版本,簡(jiǎn)稱bs4)開(kāi)發(fā)。

bs4是一個(gè)HTML/XML的解析器,其主要功能是解析和提取HTML/XML數(shù)據(jù)。它不僅支持CSS選擇器,而且支持Python標(biāo)準(zhǔn)庫(kù)中的HTML解析器,以及l(fā)xml的XML解析器。通過(guò)使用這些轉(zhuǎn)化器,實(shí)現(xiàn)了慣用的文檔導(dǎo)航和查找方式,節(jié)省了大量的工作時(shí)間,提高了開(kāi)發(fā)項(xiàng)目的效率。

bs4庫(kù)會(huì)將復(fù)雜的HTML文檔換成樹(shù)結(jié)構(gòu)(HITML DoM),這個(gè)結(jié)構(gòu)中的每個(gè)節(jié)點(diǎn)都是一個(gè)Pyhon對(duì)象。這些對(duì)象可以歸納為如下4種:

(1) bs4.element.Tag類:表示HTML中的標(biāo)簽,是最基本的信息組織單元,它有兩個(gè)非常重要的屬性,分別是表示標(biāo)簽名字的name屬性和表示標(biāo)簽屬性的attrs屬性。

(2) bs4.element.NavigableString類:表示HTML中標(biāo)簽的文本(非屬性字符串)。

(3) bs4.BeautifulSoup類:表示HTML DOM中的全部?jī)?nèi)容,支持遍歷文檔樹(shù)和搜索文檔樹(shù)的大部分方法。

(4) bs4.element.Comment類:表示標(biāo)簽內(nèi)字符串的注釋部分,是一種特殊的Navigable String對(duì)象。


使用bs4的一般流程如下:

(1)創(chuàng)建一個(gè)BeautifulSoup類型的對(duì)象。

根據(jù)HTML或者文件創(chuàng)建BeautifulSoup 對(duì)象。

(2)通過(guò)BeautifulSoup對(duì)象的操作方法進(jìn)行解讀搜索。

根據(jù)DOM樹(shù)進(jìn)行各種節(jié)點(diǎn)的搜索( 例如,find_all()方法可以搜索出所有滿足要求的節(jié)點(diǎn),find()方法只會(huì)搜索出第一個(gè)滿足要求的節(jié)點(diǎn)),只要獲得了一個(gè)節(jié)點(diǎn),就可以訪問(wèn)節(jié)點(diǎn)的名稱、屬性和文本。

(3)利用DOM樹(shù)結(jié)構(gòu)標(biāo)簽的特性,進(jìn)行更為詳細(xì)的節(jié)點(diǎn)信息提取。

在搜索節(jié)點(diǎn)時(shí),也可以按照節(jié)點(diǎn)的名稱、節(jié)點(diǎn)的屬性或者節(jié)點(diǎn)的文字進(jìn)行搜索。上述流程如下圖所示。

1624258199895_bs4庫(kù)的使用流程.png


構(gòu)建BeautifulSoup對(duì)象

通過(guò)一個(gè)字符串或者類文件對(duì)象(存儲(chǔ)在本地的文件句柄或Web網(wǎng)頁(yè)句柄)可以創(chuàng)建BauifulSoup類的對(duì)象。 BeautifulSoup類中構(gòu)造方法的語(yǔ)法如下:

def_init_(self, markup="", features=None, builder=None, parse_only=None, 
from_encoding=None, exclude_encodings=None, **kwargs)

上述方法的一些參數(shù)含義如下:
(1) markup:表示要解析的文檔字符串或文件對(duì)象。
(2) features:表示解析器的名稱。
(3) builder:表示指定的解析器。
(4) from_encoding:表示指定的編碼格式。
(5) exclude _encodings:表示排除的編碼格式。 例如,根據(jù)字符串html_doc創(chuàng)建一個(gè)BeautifulSoup對(duì)象:

from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc, 'lxml')

上述示例中,在創(chuàng)建BeautifulSoup實(shí)例時(shí)共傳入了兩個(gè)參數(shù)。其中,第一個(gè)參數(shù)表示包含被解析HTML文檔的字符串;第二個(gè)參數(shù)表示使用lxml解析器進(jìn)行解析。

目前,bs4 支持的解析器包括Python標(biāo)準(zhǔn)庫(kù)、lxml和html5lib。為了讓用戶更好地選擇合適的解析器,下面列舉它們的使用方法和優(yōu)缺點(diǎn),如表所示。

解析器使用方法優(yōu)勢(shì)劣勢(shì)
lxml HTML解析器BeautifulSoup(markup,"lxml")(1)速度快;
(2)文檔容錯(cuò)能力強(qiáng)
需要安裝C語(yǔ)言庫(kù)
Python標(biāo)準(zhǔn)庫(kù)BeautifulSoup(markup, "html.parser")(1) Python的內(nèi)置標(biāo)準(zhǔn)庫(kù);
(2)執(zhí)行速度適中;
(3)文檔容錯(cuò)能力強(qiáng)
Python 2.7.3或3.2.2之前的版本中文檔容錯(cuò)能力差
lxml XML解析器BeautifulSoup(markup, [<<lxml-xml>>])
BeautifulSoup(markup, "xml")
(1)速度快;
(2)唯一支持XML的解析器
需要安裝C語(yǔ)言庫(kù)
html5libBeautifulSoup(markup, "html5lib")(1)最好的容錯(cuò)性;
(2)以瀏覽器的方式解析文檔
(3)生成HTML5格式的文檔
(1)速度慢;
(2)不依賴外部擴(kuò)展

在創(chuàng)建BeautifulSoup對(duì)象時(shí),如果沒(méi)有明確地指定解析器,那么BeautifulSoup對(duì)象會(huì)根據(jù)當(dāng)前系統(tǒng)安裝的庫(kù)自動(dòng)選擇解析器。解析器的選擇順序?yàn)椋簂xml、html5lib、Python標(biāo)準(zhǔn)庫(kù)。在下面兩種情況下,選擇解析器的優(yōu)先順序會(huì)發(fā)生變化:

(1)要解析的文檔是什么類型,目前支持html、xml和html5。

(2)指定使用哪種解析器。

如果明確指定的解析器沒(méi)有安裝,那么BeautifulSoup對(duì)象會(huì)自動(dòng)選擇其他方案。但是,目前只有l(wèi)xml解析器支持解析XML文檔,一且沒(méi)有安裝lxml庫(kù),就無(wú)法得到解析后的對(duì)象。

使用print()函數(shù)輸出剛創(chuàng)建的BeantifulSoup對(duì)象soup,代碼如下:

print(soup.prettify())

上述示例中調(diào)用了petif()方法進(jìn)行打印,既可以為HTML標(biāo)簽和內(nèi)容增加換行符,又可以對(duì)標(biāo)簽做相關(guān)的處理,以便于更加友好地顯示HTML內(nèi)容。為了直觀地比較這兩種情況,下面分別列出直接打印和調(diào)用prettify()方法后打印的結(jié)果。直接使用print()函數(shù)進(jìn)行輸出,示例結(jié)果如下:

<html><head><title>The Dormouse's story</title></head>
<body>
</body></html>

調(diào)用prettify()方法后進(jìn)行輸出,示例結(jié)果如下:

<html>
	<head>
		<title>
			The Dormouse's story
		</title>
	</head>
	<body>
	</body>
</html>

通過(guò)操作方法進(jìn)行解讀搜索

實(shí)際上,網(wǎng)頁(yè)中有用的信息都存在于網(wǎng)頁(yè)中的文本或者各種不同標(biāo)簽的屬性值,為了能獲得這些有用的網(wǎng)頁(yè)信息,可以通過(guò)一些查找方法獲取文本或者標(biāo)簽屬性。因此,bs4庫(kù)內(nèi)置了一些查找方法,其中常用的兩個(gè)方法功能如下:

(1) find()方法:用于查找符合查詢條件的第一 個(gè)標(biāo)簽節(jié)點(diǎn)。

(2) find_all()方法:查找所有符合查詢條件的標(biāo)簽節(jié)點(diǎn),并返回一個(gè)列表。

這兩個(gè)方法用到的參數(shù)是一樣的,這里以find_all()方法為例,介紹在這個(gè)方法中這些參數(shù)的應(yīng)用。find_all()方法的定義如下:

find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)

上述方法中一些重要參數(shù)所表示的含義如下:

1.name參數(shù)

在找所有名字為name的標(biāo)簽,但字符串會(huì)被自動(dòng)忽略。下面是 name參數(shù)的幾種情況:
(1) 傳人字符串:在搜索的方法中傳入一個(gè)字符串,BeautifuSoup對(duì)象會(huì)查找與字符事無(wú)全匹配的內(nèi)容。例如:

soup.find_all('b')

 上述示例用于查找文檔中所有的<b>標(biāo)簽。

 (2)傳人正則表達(dá)式:如果傳入一個(gè)正則表達(dá)式,那么BautifulSoup對(duì)象會(huì)通過(guò)re模塊的match()函數(shù)進(jìn)行匹配。下面的示例中,使用正則表達(dá)式"^b"匹配所有以字母b開(kāi)頭的標(biāo)簽。

import re
for tag in soup.find_all(re.compile("^b")) :
	print(tag.name)
#輸出結(jié)果如下
body

(3)傳人列表:如果傳入一個(gè)列表,那么BeautifulSoup對(duì)象會(huì)將與列表中任一元索匹配的內(nèi)容返回。在下面的示例中,找到了文檔中所有的<a>標(biāo)簽和<b>標(biāo)簽。

soup.find_all(["a", "b"])
# 部分輸出結(jié)果如下:
[<b>The Dormouse's story</b>,
<a classm"sister" href="http://example.com/elsie" 1d="link1">E1sle</a>,

2.attrs參數(shù)
如果某個(gè)指定名字的參數(shù)不是搜索方法中內(nèi)置的參數(shù)名,那么在進(jìn)行搜索時(shí),會(huì)把該參數(shù)當(dāng)作指定名稱的標(biāo)簽中的屬性來(lái)搜索。在下面的示例中,在find_all()方法中傳人名稱為id的參數(shù),這時(shí)BeautiflSoup對(duì)象會(huì)搜索每個(gè)標(biāo)簽的id屬性。

soup.find_all(id='link2')
# 輸出的結(jié)果可能是:
[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

若傳入多個(gè)指定名字的參數(shù),則可以同時(shí)過(guò)濾出標(biāo)簽中的多個(gè)屬性。在下面的示例中,既可以搜索每個(gè)標(biāo)簽的id屬性,同時(shí)又可以搜索href屬性。

import re
soup.find_all(href=re.compile("elsie"), id='link1')
# 輸出的結(jié)果可能是:
[<a class="sister" href="http://example.com/elsie" id="linkl">Elsie</a>]


如果要搜索的標(biāo)簽名稱為class,由于class屬于Python的關(guān)鍵字,所以可在class的后面加上一個(gè)下畫(huà)線。例如:

soup.find_all("a", class_="sister")
# 部分輸出結(jié)果如下:
# [<a href="http: //example.com/elsie" id="link1">Elsie</a>,

但是,有些標(biāo)簽的屬性名稱是不能使用的,例如HTML5中的“data-”屬性,在程序中使用時(shí),會(huì)出現(xiàn)SyntaxError異常信息。這時(shí),可以通過(guò)find_all()方法的attrs參數(shù)傳入一個(gè)字典來(lái)搜索包含特殊屬性的標(biāo)簽。例如:

data_soup=BeautifulSoup('<div data-foo="value">foo!</div>', 'lxml')
data_soup.find_all(data-foo="value")
# 程序輸出如下報(bào)錯(cuò)信息:
# SyntaxError: keyword can't be an expression
data_soup.find_all(attrs={"data-foo": "value"})
# 程序可匹配的結(jié)果
# [<div data-foo="value">foo!</div>]

3.text參數(shù)
通過(guò)在find_all()方法中傳人text參數(shù),可以搜索文檔中的字符串內(nèi)容。與name參數(shù)的可選值一樣,text參數(shù)也可以接受字符串、正則表達(dá)式和列表等。例如:

soup.find_all(text="Elsie")
# [u'Elsie']
soup.find_all(text=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']

limit參數(shù) 在使用find_all()方法返回匹配的結(jié)果時(shí),倘若DOM樹(shù)非常大,那么搜索的速度會(huì)相當(dāng)慢。這時(shí),如果不需要獲得全部的結(jié)果,就可以使用limit參數(shù)限制返回結(jié)果的數(shù)量,其效果與SQL語(yǔ)句中的limit關(guān)鍵字所產(chǎn)生的效果類似。一旦搜索到結(jié)果的數(shù)量達(dá)到了limit的限制,就會(huì)停止搜索。例如:

soup.find_all("a", limit=2)

上述示例會(huì)搜索到最多兩個(gè)符合搜索條件的標(biāo)簽。

recursive參數(shù) 在調(diào)用find_all()方法時(shí),Beutifuloup對(duì)象會(huì)檢索當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)。這時(shí),如果只想搜索當(dāng)前節(jié)點(diǎn)的直接子節(jié)點(diǎn),就可以使用參數(shù)recursive=False。例如:

soup.html.find_all("title")
# [<title>The Dormouse's story</title>]
soup.html.find_all("titile", recursive=False)
# []

除了上述兩個(gè)常用的方法以外,bs4庫(kù)中還提供了一些通過(guò) 節(jié)點(diǎn)間的關(guān)系進(jìn)行查我的方法。由于這些方法的參數(shù)和用法跟fnd, alll 方法類似,這里就不再另行介紹。

通過(guò)CSS選擇器進(jìn)行搜索


除了bs4庫(kù)提供的操作方法以外,還可以使用CSS選擇器進(jìn)行查找。什么是CSS呢? CSS (Cascading Style Sheets,層疊樣式表)是一種用來(lái)表現(xiàn)HTML或XML等文件樣式的計(jì)算機(jī)語(yǔ)言,它不僅可以靜態(tài)地修飾網(wǎng)頁(yè),而且可以配合各種腳本語(yǔ)言動(dòng)態(tài)地對(duì)網(wǎng)頁(yè)各元索進(jìn)行格式化。

要想使用Css對(duì)HTML頁(yè)面中的元素實(shí)現(xiàn)一對(duì)一、一對(duì)多或多對(duì)一的控制,需要用到CSS選擇器。 每一條CSS樣式定義均由兩部分組成,形式如下:

[code]選擇器{樣式}[/code]

其中,在{} 之前的部分就是“選擇器”。選擇器指明了}中樣式的作用對(duì)象,也就是“樣式”作用于網(wǎng)頁(yè)中的哪些元素。

為了使用CSS選擇器達(dá)到篩選節(jié)點(diǎn)的目的,在bs4庫(kù)的BeautifulSoup類中提供了一個(gè)select()方法,該方法會(huì)將搜索到的結(jié)果放到列表中。 CSS選擇器的查找方式可分為如下幾種:

1.通過(guò)標(biāo)簽查找

在編寫(xiě)CSS時(shí),標(biāo)簽的名稱不用加任何修飾。調(diào)用select0方法時(shí),可以傳人包含某個(gè)標(biāo)簽的字符串。使用CSS選擇器查找標(biāo)簽的示例如下:

soup.select("title")
# 查找的結(jié)果可能為
# [<title>The Dormouse's story</title>]


2.通過(guò)類名查找


在編寫(xiě)CSS時(shí),需要在類名的前面加上“.” 。例如,查找類名為sister的標(biāo)簽,示例如下:

soup.select('.sister')
# 并查找的結(jié)果可能為
# [<a href="http://example.com/elsie" id="linkl"><!-- Elsie --></a>, <a href="http://example.com/lacie" id="link2">Lacie</a>, <a href="http://example.com/tillie" id="link3">Tillie</a> ]


3.通過(guò)id名查找

在編寫(xiě)CSS時(shí),需要在id名稱的前面加上“#”。例如,查找id名為link1的標(biāo)簽,具體示例如下;

soup.select("#link1")
# 查找的結(jié)果可能為
# [<a href-"http://example.com/elsie" id="link1">Elsie</a>]


4.通過(guò)組合的形式查找

組合查找與編寫(xiě)CLASS文件時(shí)標(biāo)簽名、類名、id 名的組合原理一樣,二者需要用空格分開(kāi)。例如,在標(biāo)簽p中,查找id值等于link1的內(nèi)容。

soup.select('p #link1')
# 手查找的結(jié)果可能為
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

可以使用“>”將標(biāo)簽與子標(biāo)簽分隔,從而找到某個(gè)標(biāo)簽下的直接子標(biāo)簽。例如:

soup.select("head > title")
# 查找的結(jié)果可能為
# [<title>The Dormouse's story</title>]


5.通過(guò)屬性查找

可以通過(guò)屬性元素進(jìn)行查找,屬性需要用中括號(hào)括起來(lái)。但是,屬性和標(biāo)簽屬于同一個(gè)結(jié)點(diǎn),它們中間不能加空格,否則將無(wú)法匹配到。例如:

soup.select('a[href="http://example.com/elsie"]')
# 查找的結(jié)果可能為
# [<a href="http: //example. com/elsie" id="link1">Elsie</a>]

同樣,屬性仍然可以與上述查找方式組合,即不在同一節(jié)點(diǎn)的屬性使用空格隔開(kāi),同一節(jié)點(diǎn)的屬性之間不加空格。例如:

soup.select('P a[href="http://example.com/elsie"]')
# 查找的結(jié)果可能為
# [<a href="http://example.com/elsie" id="link1">Elsie</a>]

上述這些查找方式都會(huì)返回一個(gè)列表。遍歷這個(gè)列表,可以調(diào)用get _text() 方法來(lái)獲取節(jié)點(diǎn) 的內(nèi)容。例如:

<br class="Apple-interchange-newline"><div></div>
soup=BeautifulSoup(html_doc, 'lxml')
for element in soup.select('a'):
    print(element.get_text())    # 獲取節(jié)點(diǎn)的內(nèi)容
# 獲取到節(jié)點(diǎn)的內(nèi)容可能為
Elsie
Lacie                                                        
Tillie


分享到:
在線咨詢 我要報(bào)名
和我們?cè)诰€交談!