Beautiful Soup入門(mén)
Beautiful Soup是一個(gè)Python庫(kù)埠胖,用來(lái)解析html和xml結(jié)構(gòu)的文檔析既。具體關(guān)于Beautiful Soup的介紹與使用笆焰,可以參考以下資料:
下面是我在使用Beautiful Soup時(shí)遇到的小問(wèn)題韩脑。
解析器選擇
官方對(duì)各個(gè)解析器的比較如下:
解析器 | 使用方法 | 優(yōu)勢(shì) | 劣勢(shì) |
---|---|---|---|
Python標(biāo)準(zhǔn)庫(kù) | BeautifulSoup(markup, "html.parser") | Python的內(nèi)置標(biāo)準(zhǔn)庫(kù) 執(zhí)行速度適中 文檔容錯(cuò)能力強(qiáng) |
Python 2.7.3 or 3.2.2前的版本中文檔容錯(cuò)能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, "lxml") | 速度快 文檔容錯(cuò)能力強(qiáng) |
需要安裝C語(yǔ)言庫(kù) |
lxml XML 解析器 | BeautifulSoup(markup, ["lxml", "xml"]) BeautifulSoup(markup, "xml") |
速度快 唯一支持XML的解析器 |
需要安裝C語(yǔ)言庫(kù) |
html5lib | BeautifulSoup(markup, "html5lib") | 最好的容錯(cuò)性 以瀏覽器的方式解析文檔 生成HTML5格式的文檔 |
速度慢 不依賴外部擴(kuò)展 |
首先氢妈,如果是解析從網(wǎng)頁(yè)上爬下來(lái)的HTML文檔,請(qǐng)不要使用lxml XML 解析器段多,因?yàn)镠TML解析器和XML解析器對(duì)于一文檔的解析方式是不同的首量。比如對(duì)于空標(biāo)簽<b />,因?yàn)榭諛?biāo)簽<b />不符合HTML標(biāo)準(zhǔn),所以解析器把它解析成<b></b>,而使用XML解析時(shí),空標(biāo)簽<b/>依然被保留。
其次进苍,在Python2.7.3之前的版本和Python3中3.2.2之前的版本中,標(biāo)準(zhǔn)庫(kù)中內(nèi)置的HTML解析方法不夠穩(wěn)定加缘,所以我推薦使用lxml或者h(yuǎn)tml5lib作為html文檔的解析器,因?yàn)槿蒎e(cuò)性比較好。
在HTML或XML文檔格式正確的情況下觉啊,不同解析器的差別只是解析速度的差別拣宏。而在很多情況下,我們從網(wǎng)頁(yè)上爬取的HTML文檔會(huì)有格式不嚴(yán)謹(jǐn)?shù)牡胤礁苋耍敲丛诓煌慕馕銎髦蟹祷氐慕Y(jié)果可能是不一樣的勋乾,此時(shí)用戶需要選擇合適的解析器來(lái)滿足自己的需求。
# lxml解析
BeautifulSoup("<a></p>", "lxml")
# <html><body><a></a></body></html> 未補(bǔ)全
BeautifulSoup("<a><p>", "lxml")
# <html><body><a><p></p></a></body></html> 補(bǔ)全
# html5lib庫(kù)解析
BeautifulSoup("<a></p>", "html5lib")
# <html><head></head><body><a><p></p></a></body></html> 補(bǔ)全
BeautifulSoup("<a><p>", "html5lib")
# <html><head></head><body><a><p></p></a></body></html> 補(bǔ)全
#Python內(nèi)置庫(kù)解析
BeautifulSoup("<a></p>", "html.parser")
# <a></a> 未補(bǔ)全
BeautifulSoup("<a><p>", "html.parser")
# <a><p></p></a> 補(bǔ)全
從上面的例子可以看出嗡善,html5lib對(duì)于文檔的容錯(cuò)性是最好的辑莫,它能補(bǔ)全大多數(shù)的標(biāo)簽。而lxml和python內(nèi)置解析器會(huì)忽略結(jié)束標(biāo)簽罩引,補(bǔ)全開(kāi)始標(biāo)簽各吨。
而對(duì)于部分沒(méi)有結(jié)束標(biāo)簽的標(biāo)簽比如<input/>
、<img/>
等袁铐,在正常情況下揭蜒,解析器都會(huì)正確解析,但如果是漏掉'/'的情況下昭躺,例如<input><a></a>
:
# lxml解析
BeautifulSoup("<input><a></a>", "lxml")
# <html><body><input/><a></a></body></html> 補(bǔ)全
# html5lib庫(kù)解析
BeautifulSoup("<input><a></a>", "html5lib")
# <html><head></head><body><input/><a></a></body></html> 補(bǔ)全
#Python內(nèi)置庫(kù)解析
BeautifulSoup("<input><a></a>", "html.parser")
# <input><a></a></input> 未補(bǔ)錯(cuò)誤
可見(jiàn)忌锯,Python內(nèi)置庫(kù)解析無(wú)法正確補(bǔ)全不需要結(jié)束標(biāo)簽的標(biāo)簽,比如<input>
领炫。
find_all()的attrs參數(shù)
在find_all()方法中,如果一個(gè)指定名字的參數(shù)不是搜索內(nèi)置的參數(shù)名,搜索時(shí)會(huì)把該參數(shù)當(dāng)作指定名字tag的屬性來(lái)搜索张咳。如傳入 href 參數(shù),Beautiful Soup會(huì)搜索每個(gè)tag的”href”屬性:
soup.find_all(href=re.compile("elsie"))
# [<a class="sister" id="link1">Elsie</a>]
假如我們想用 class 過(guò)濾帝洪,不過(guò) class 是 python 的關(guān)鍵詞似舵,這怎么辦?加個(gè)下劃線就可以
soup.find_all("a", class_="sister")
# [<a class="sister" id="link1">Elsie</a>,
# <a class="sister" id="link2">Lacie</a>,
# <a class="sister" id="link3">Tillie</a>]
但有些tag屬性在搜索不能使用,比如HTML5中的 data-*
屬性葱峡,同時(shí)name由于已經(jīng)是find_all()
方法中的一個(gè)參數(shù)名(代表tag的名字)砚哗,所以也不可通過(guò)tag中的name屬性來(lái)搜索tag,但是可以通過(guò) find_all()
方法的 attrs 參數(shù)定義一個(gè)字典參數(shù)來(lái)搜索包含特殊屬性的tag砰奕,例如:
data_soup.find_all(attrs={"data-foo": "value"})
# [<div data-foo="value">foo!</div>]
.string 和 get_text()的區(qū)別
在Beautiful Soup蛛芥,有兩種獲取標(biāo)簽內(nèi)容的方法:.string屬性 和 get_text()方法。
-
.string 用來(lái)獲取標(biāo)簽的內(nèi)容 ,返回一個(gè) NavigableString 對(duì)象军援。
如果tag只有一個(gè) NavigableString 類型子節(jié)點(diǎn),那么這個(gè)tag可以使用 .string 得到子節(jié)點(diǎn)仅淑。
如果一個(gè)tag僅有一個(gè)子節(jié)點(diǎn),那么這個(gè)tag也可以使用 .string 方法,輸出結(jié)果與當(dāng)前唯一子節(jié)點(diǎn)的 .string 結(jié)果相同。
如果tag包含了多個(gè)子節(jié)點(diǎn),tag就無(wú)法確定 .string 方法應(yīng)該調(diào)用哪個(gè)子節(jié)點(diǎn)的內(nèi)容, .string 的輸出結(jié)果是 None胸哥。
get_text() 用來(lái)獲取標(biāo)簽中所有字符串包括子標(biāo)簽的內(nèi)容涯竟,返回的是 unicode 類型的字符串
實(shí)際場(chǎng)景中我們一般使用 get_text 方法獲取標(biāo)簽中的內(nèi)容。
.next_sibling 和 find_next_sibling()
在文檔樹(shù)中,使用 .next_sibling 和 .previous_sibling 屬性來(lái)查詢兄弟節(jié)點(diǎn)空厌。實(shí)際文檔中的tag的 .next_sibling 和 .previous_sibling 屬性通常是字符串或空白庐船。例如:
<a class="sister" id="link1">Elsie</a>
<a class="sister" id="link2">Lacie</a>
<a class="sister" id="link3">Tillie</a>
如果以為第一個(gè)<a>標(biāo)簽的 .next_sibling 結(jié)果是第二個(gè)<a>標(biāo)簽,那就錯(cuò)了,真實(shí)結(jié)果是第一個(gè)<a>標(biāo)簽和第二個(gè)<a>標(biāo)簽之間的頓號(hào)和換行符:
link = soup.a
link
# <a class="sister" id="link1">Elsie</a>
link.next_sibling
# u',\n'
link.next_sibling.next_sibling
# <a class="sister" id="link2">Lacie</a>
所以我建議使用 find_next_sibling() 方法來(lái)查詢兄弟節(jié)點(diǎn):
link.find_next_sibling("a")
# <a class="sister" id="link2">Lacie</a>
link.find_next_siblings("a")
# [<a class="sister" id="link2">Lacie</a>,
# <a class="sister" id="link3">Tillie</a>]
換行符的問(wèn)題
在HTML文檔中經(jīng)常會(huì)出現(xiàn)一些用來(lái)?yè)Q行<br>
標(biāo)簽,比如:
<div>
some text <br>
<span> some more text </span> <br>
<span> and more text </span>
</div>
Beautiful Soup會(huì)將其自動(dòng)補(bǔ)全為以下錯(cuò)誤的形式:
<div>
some text
<br>
<span> some more text </span>
<br>
<span> and more text </span>
</br>
</br>
</div>
因?yàn)?code><br>標(biāo)簽是為了展示的美觀而出現(xiàn)的嘲更,而我們?cè)诮馕鑫臋n時(shí)筐钟,這種標(biāo)簽的出現(xiàn)會(huì)影響我們解析的正確性(就如上面那個(gè)例子所示)。為了解決這個(gè)問(wèn)題赋朦,我們需要使用extract()方法將文檔中的<br>
標(biāo)簽刪掉
soup = BeautifulSoup(text)
for linebreak in soup.find_all('br'):
linebreak.extract()
這樣最終的文檔格式就變?yōu)椋?/p>
<div>
some text
<span> some more text </span>
<span> and more text </span>
</div>
大小寫(xiě)問(wèn)題
因?yàn)镠TML標(biāo)簽是大小寫(xiě)敏感的,所以3種解析器再出來(lái)文檔時(shí)都將tag和屬性轉(zhuǎn)換成小寫(xiě)盗棵。例如文檔中的 <TAG></TAG> 會(huì)被轉(zhuǎn)換為 <tag></tag> 。如果想要保留tag的大寫(xiě)的話,那么應(yīng)該將文檔 解析成XML北发。
參考資料
- Beautiful Soup 4.2.0 中文文檔
- Python爬蟲(chóng)利器二之Beautiful Soup的用法
- python爬蟲(chóng)入門(mén)教程--HTML文本的解析庫(kù)BeautifulSoup(四)
- Beautifulsoup sibling structure with br tags
- (學(xué)習(xí)筆記)Python BeautifulSoup4 取值部分
文章標(biāo)題:Beautiful Soup 采坑之旅
文章作者:Ciel Ni
文章鏈接:http://www.cielni.com/2018/06/14/beautifulsoup/
有問(wèn)題或建議歡迎與我聯(lián)系討論纹因,轉(zhuǎn)載或引用希望標(biāo)明出處,感激不盡琳拨!