Beautiful Soup 簡(jiǎn)介
Beautiful Soup 是一個(gè)可以從 HTML 或 XML 文件中提取數(shù)據(jù)的 Python 庫(kù),它提供了一些簡(jiǎn)單的操作方式來(lái)幫助你處理文檔導(dǎo)航,查找,修改文檔等繁瑣的工作。因?yàn)槭褂煤?jiǎn)單,所以 Beautiful Soup 會(huì)幫你節(jié)省不少的工作時(shí)間。
Beautiful Soup 安裝
你可以使用如下命令安裝 Beautiful Soup。二選一即可。
$ easy_install beautifulsoup4$ pip install beautifulsoup4
Beautiful Soup 不僅支持 Python 標(biāo)準(zhǔn)庫(kù)中的 HTML 解析器,還支持很多第三方的解析器,比如 lxml,html5lib 等。初始化 Beautiful Soup 對(duì)象時(shí)如果不指定解析器,那么 Beautiful Soup 將會(huì)選擇最合適的解析器(前提是你的機(jī)器安裝了該解析器)來(lái)解析文檔,當(dāng)然你也可以手動(dòng)指定解析器。這里推薦大家使用 lxml 解析器,功能強(qiáng)大,方便快捷,而且該解析器是唯一支持 XML 的解析器。
你可以使用如下命令來(lái)安裝 lxml 解析器。二選一即可。
$ easy_install lxml$ pip install lxml
Beautiful Soup 小試牛刀
Beautiful Soup 使用來(lái)起來(lái)非常簡(jiǎn)單,你只需要傳入一個(gè)文件操作符或者一段文本即可得到一個(gè)構(gòu)建完成的文檔對(duì)象,有了該對(duì)象之后,就可以對(duì)該文檔做一些我們想做的操作了。而傳入的文本大都是通過(guò)爬蟲爬取過(guò)來(lái)的,所以 Beautiful Soup 和 requests 庫(kù)結(jié)合使用體驗(yàn)更佳。
# demo 1from bs4 import BeautifulSoup# soup = BeautifulSoup(open(“index.html”))soup = BeautifulSoup(“indexcontent”, “lxml”) # 指定解析器print(soup.head)# 輸出結(jié)果index
Beautiful Soup 將復(fù)雜的 HTML 文檔轉(zhuǎn)換成一個(gè)復(fù)雜的樹形結(jié)構(gòu),每個(gè)節(jié)點(diǎn)都是 Python 對(duì)象,所有對(duì)象可以歸納為 4 種: Tag,NavigableString,BeautifulSoup,Comment。
Tag 就是 HTML 的一個(gè)標(biāo)簽,比如 p,p 標(biāo)簽等,也是我們用的最多的一個(gè)對(duì)象。
NavigableString 指標(biāo)簽內(nèi)部的文字,直譯就是可遍歷的字符串。
BeautifulSoup 指一個(gè)文檔的全部?jī)?nèi)容,可以當(dāng)成一個(gè) Tag 來(lái)處理。
Comment 是一個(gè)特殊的 NavigableString,其輸出內(nèi)容不包括注視內(nèi)容。
為了故事的順利發(fā)展,我們先定義一串 HTML 文本,下文的所有例子都是基于這段文本的。
html_doc = “””index
我常用的網(wǎng)站GoogleBaiduBing
…
…
“””
子節(jié)點(diǎn)
Tag 有兩個(gè)很重要的屬性,name 和 attributes。期中 name 就是標(biāo)簽的名字,attributes 是標(biāo)簽屬性。標(biāo)簽的名字和屬性是可以被修改的,注意,這種修改會(huì)直接改變 BeautifulSoup 對(duì)象。
# demo 2soup = BeautifulSoup(html_doc, “lxml”);p_tag = soup.pprint(p_tag.name)print(p_tag[“class”])print(p_tag.attrs)p_tag.name=”myTag” # attrs 同樣可被修改,操作同字典print(p_tag)#輸出結(jié)果p[‘title’]{‘class’: [‘title’]}首頁(yè)
由以上例子我么可以看出,可以直接通過(guò)點(diǎn)屬性的方法來(lái)獲取 Tag,但是這種方法只能獲取第一個(gè)標(biāo)簽。同時(shí)我們可以多次調(diào)用點(diǎn)屬性這個(gè)方法,來(lái)獲取更深層次的標(biāo)簽。
# demo 3soup = BeautifulSoup(html_doc, “lxml”);print(soup.p.b)#輸出結(jié)果首頁(yè)
如果想獲得所有的某個(gè)名字的標(biāo)簽,則可以使用 find_all(tag_name) 函數(shù)。
# demo 4soup = BeautifulSoup(html_doc, “lxml”);a_tags=soup.find_all(“a”)print(a_tags)#輸出結(jié)果[Google, Baidu, Bing]
我們可以使用 .contents 將 tag 以列表方式輸出,即將 tag 的子節(jié)點(diǎn)格式化為列表,這很有用,意味著可以通過(guò)下標(biāo)進(jìn)行訪問(wèn)指定節(jié)點(diǎn)。同時(shí)我們還可以通過(guò) .children 生成器對(duì)節(jié)點(diǎn)的子節(jié)點(diǎn)進(jìn)行遍歷。
# demo 5soup = BeautifulSoup(html_doc, “lxml”);head_tag=soup.headprint(head_tag)print(head_tag.contents)for child in head_tag.children:print(“child is : “, child)#輸出結(jié)果index[index]child is : index
.children 只可以獲取 tag 的直接節(jié)點(diǎn),而獲取不到子孫節(jié)點(diǎn),.descendants 可以滿足你。
# demo 6soup = BeautifulSoup(html_doc, “lxml”);head_tag=soup.headfor child in head_tag.descendants:print(“child is : “, child)# 輸出結(jié)果child is : indexchild is : index
父節(jié)點(diǎn)
通過(guò) .parent 屬性獲取標(biāo)簽的父親節(jié)點(diǎn)。 title 的父標(biāo)簽是 head,html 的父標(biāo)簽是 BeautifulSoup 對(duì)象,而 BeautifulSoup 對(duì)象的父標(biāo)簽是 None。
# demo 7soup = BeautifulSoup(html_doc, “lxml”);title_tag=soup.titleprint(title_tag.parent)print(type(soup.html.parent))print(soup.parent)# 輸出結(jié)果indexNone
同時(shí),我們可以通過(guò) parents 得到指定標(biāo)簽的所有父親標(biāo)簽。
# demo 8soup = BeautifulSoup(html_doc, “lxml”);a_tag=soup.afor parent in a_tag.parents: print(parent.name)# 輸出結(jié)果pbodyhtml[document]
兄弟節(jié)點(diǎn)
通過(guò) .next_sibling 和 .previous_sibling 來(lái)獲取下一個(gè)標(biāo)簽和上一個(gè)標(biāo)簽。
# demo 9soup = BeautifulSoup(html_doc, “lxml”);p_tag=soup.pprint(p_tag.next_sibling)print(p_tag.next_sibling.next_sibling)# 輸出結(jié)果
…
你可能會(huì)納悶,調(diào)用了兩次 next_sibling 怎么只有一個(gè)輸出呢,這方法是不是有 bug 啊。事實(shí)上是 p 的第一個(gè) next_sibling 是p 和 p 之間的換行符。這個(gè)規(guī)則對(duì)于 previous_sibling 同樣適用。
另外,我們可以通過(guò) .next_siblings 和 .previous_siblings 屬性可以對(duì)當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)迭代輸出。在該例子中,我們?cè)诿看屋敵銮凹恿饲熬Y,這樣就可以更直觀的看到 dib 的第一個(gè) previous_sibling 是換行符了。
# demo 10soup = BeautifulSoup(html_doc, “lxml”);p_tag=soup.pfor pre_tag in p_tag.previous_siblings:print(“pre_tag is : “, pre_tag)# 輸出結(jié)果pre_tag is : pre_tag is :
我常用的網(wǎng)站GoogleBaiduBing
pre_tag is : pre_tag is :
首頁(yè)
pre_tag is :
前進(jìn)和后退
通過(guò) .next_element 和 .previous_element 獲取指定標(biāo)簽的前一個(gè)或者后一個(gè)被解析的對(duì)象,注意這個(gè)和兄弟節(jié)點(diǎn)是有所不同的,兄弟節(jié)點(diǎn)是指有相同父親節(jié)點(diǎn)的子節(jié)點(diǎn),而這個(gè)前一個(gè)或者后一個(gè)是按照文檔的解析順序來(lái)計(jì)算的。
比如在我們的文本 html_doc 中,head 的兄弟節(jié)點(diǎn)是 body(不考慮換行符),因?yàn)樗麄兙哂泄餐母腹?jié)點(diǎn) html,但是 head 的下一個(gè)節(jié)點(diǎn)是 title。即soup.head.next_sibling=title soup.head.next_element=title
# demo 11soup = BeautifulSoup(html_doc, “lxml”);head_tag=soup.headprint(head_tag.next_element)title_tag=soup.titleprint(title_tag.next_element)# 輸出結(jié)果indexindex
同時(shí)這里還需要注意的是 title 下一個(gè)解析的標(biāo)簽不是 body,而是 title 標(biāo)簽內(nèi)的內(nèi)容,因?yàn)?html 的解析順序是打開 title 標(biāo)簽,然后解析內(nèi)容,最后關(guān)閉 title 標(biāo)簽。
另外,我們同樣可以通過(guò) .next_elements 和 .previous_elements 來(lái)迭代文檔樹。由遺下例子我們可以看出,換行符同樣會(huì)占用解析順序,與迭代兄弟節(jié)點(diǎn)效果一致。
# demo 12soup = BeautifulSoup(html_doc, “lxml”);p_tag=soup.pfor next_element in p_tag.next_elements:print(“next_element is : “, next_element)# 輸出結(jié)果next_element is : 這是注釋內(nèi)容next_element is : next_element is :
…
next_element is : …next_element is : next_element is :
…
next_element is : …next_element is : next_element is :
Beautiful Soup 總結(jié)
本章節(jié)介紹了 Beautiful Soup 的使用場(chǎng)景以及操作文檔樹節(jié)點(diǎn)的基本操作,看似很多東西其實(shí)是有規(guī)律可循的,比如函數(shù)的命名,兄弟節(jié)點(diǎn)或者下一個(gè)節(jié)點(diǎn)的迭代函數(shù)都是獲取單個(gè)節(jié)點(diǎn)函數(shù)的復(fù)數(shù)形式。
同時(shí)由于 HTML 或者 XML 這種循環(huán)嵌套的復(fù)雜文檔結(jié)構(gòu),致使操作起來(lái)甚是麻煩,掌握了本文對(duì)節(jié)點(diǎn)的基本操作,將有助于提高你寫爬蟲程序的效率。