Beautiful soup是一個(gè)可以從HTML或XML文件中提取數(shù)據(jù)的python庫。在python爬蟲開發(fā)中郎仆,我們主要用到的是Beautiful soup的查找提取功能只祠,修改文檔的方式很少用到。
python用戶可以通過anaconda安裝beautifulsoup4(推薦)扰肌,安裝簡單抛寝,這里不在介紹
安裝完成后铸鹰,接下來講解BeautifulSoup的使用。
1.快速開始
首先導(dǎo)入bs4庫:from bs4 import BeautifulSoup涯贞。
本節(jié)基于上節(jié)獲得網(wǎng)頁請求后扎谎,通過r.text獲得網(wǎng)頁源代碼,即html_str=r.text钻趋。接下來都是基于html_str進(jìn)行數(shù)據(jù)解析和提取川陆。
然后創(chuàng)建BeautifulSoup對象,方法有兩種蛮位,一是直接通過字符串創(chuàng)建:
soup=BeautifulSoup(html_str,'lxml',from_encoding='utf-8')
另一種通過文件來創(chuàng)建较沪,假如將html_str字符串保存為index.html文件,創(chuàng)建方式如下:
soup=BeautifulSoup(open(index.html'))
格式化輸出網(wǎng)頁內(nèi)容
soup.prettify()
2.對象種類
BeautifulSoup將復(fù)雜的HTML文檔轉(zhuǎn)換成一個(gè)復(fù)雜的樹形結(jié)構(gòu)失仁,每個(gè)節(jié)點(diǎn)都是python對象尸曼,所有對象可分為4種:Tag, NavigableString, BeautifulSoup, Comment,接下來依次進(jìn)行介紹。
2.1)Tag
Tag對象就是HTML中的標(biāo)記萄焦,如:<title> The story</title>或<a herf="http://baidu.com",class="sister" id="link1">else</a>控轿,title和a標(biāo)記及其里面的內(nèi)容稱為Tag對象。提取方法為:
抽取title:soup.title
抽取a:soup.a
以上兩個(gè)簡單的方法是查找所有內(nèi)容中第一個(gè)符合要求的標(biāo)記拂封,如果要查詢所有標(biāo)記茬射,且看后續(xù)分解。
Tag中有兩個(gè)重要的屬性:name和attributes冒签。每個(gè)Tag都有自己的名字在抛,通過.name來獲取
soup的name為[document],其他標(biāo)記的名字是標(biāo)記的本身的名稱镣衡。
Tag中具有屬性霜定,如a標(biāo)記中的herf屬性,class屬性和id屬性廊鸥。其使用方式和字典相同:
soup.a['herf']
soup.a.get('herf')
也可以直接用"點(diǎn)"取屬性望浩,比如.attrs,用于獲取Tag中的所有屬性,以字典形式返回:
soup.a.attrs
2.2)NavigableString
標(biāo)記中的內(nèi)容已經(jīng)可以通過Tag獲取惰说,那么其中的文本信息如何獲取呢磨德?需要用到.string:
soup.a.string
2.3)BeautifulSoup
BeautifulSoup對象表示的是一個(gè)文檔的全部內(nèi)容。大部分時(shí)候可以把它當(dāng)作Tag對象吆视,是一個(gè)特殊的Tag典挑。
2.4)Comment
Tag,NavigableString啦吧,BeautifulSoup幾乎覆蓋了HTML和XML中的所有內(nèi)容您觉,但還有一些文檔的注釋部分。
在提取字符串時(shí)授滓,可以判斷一下類型:
if type(soup.a.string)==bs4.element.Comment:
print soup.a.string
3遍歷文檔樹
BeautifulSoup會(huì)將HTML轉(zhuǎn)換成文檔樹進(jìn)行搜索琳水,既然是樹形結(jié)構(gòu)肆糕,必然有節(jié)點(diǎn)。
3.1)子節(jié)點(diǎn)
直接子節(jié)點(diǎn)在孝,Tag中的.contents和.children是非常重要的诚啃。Tag的.contents屬性可以將Tag子節(jié)點(diǎn)以列表的形式輸出.soup.head.contents
字符串沒有.contents屬性,因?yàn)樽址疀]有子節(jié)點(diǎn)私沮。
.children屬性返回一個(gè)生成器始赎,可以對Tag的子節(jié)點(diǎn)進(jìn)行循環(huán):
for children in soup.children:
? ? print(child)
.contents和.children屬性只包含Tag的直接節(jié)點(diǎn)(兒子節(jié)點(diǎn)),不含其他子孫節(jié)點(diǎn);.descendants屬性可以對所有tag的子孫節(jié)點(diǎn)進(jìn)行遞歸循環(huán)仔燕。
for children in soup.descendants:
? ? print(child)
以上都是如何獲取子節(jié)點(diǎn)造垛,也介紹過獲取文本內(nèi)容,.string涨享,.strings筋搏,.stripped_strings三個(gè)屬性。
.string屬性:如果一個(gè)標(biāo)記Tag里面有0個(gè)或1個(gè)標(biāo)記了厕隧,那么返回標(biāo)記里面的內(nèi)容;如果多于一個(gè)標(biāo)記則返回None俄周。
.strings屬性:應(yīng)用于tag中包含多個(gè)字符串的情況吁讨,可以進(jìn)行循環(huán)遍歷,
for string in soup.strings:
? ? print(rear(string))
.stripped_strings屬性可以去掉輸入字符串中包含的空格或空行峦朗,
for string in soup.stripped_strings:
? ? print(rear(string))
3.2)父節(jié)點(diǎn)
每個(gè)Tag都有父節(jié)點(diǎn):被包含在某個(gè)Tag中建丧。
通過.parent屬性來獲取某個(gè)元素的父節(jié)點(diǎn)。通過.parents屬性可以遞歸得到元素的所有父輩節(jié)點(diǎn)波势。
for parent in soup.a.parents:
? ? if parent is None:
? ? ? ? print(parent)
? ? else:
? ? ? ? print(parent.name)
3.3)兄弟節(jié)點(diǎn)
兄弟節(jié)點(diǎn)是和該節(jié)點(diǎn)同一級(jí)的節(jié)點(diǎn)翎朱,.next_sibling屬性可以獲取該節(jié)點(diǎn)的下一個(gè)兄弟節(jié)點(diǎn),.previous_sibling用于獲取上一個(gè)兄弟節(jié)點(diǎn)尺铣,如果不存在則返回None拴曲。
soup.a.next_sibling
soup.a.previous_sibling
通過.next_sibling或.previous_sibling可以對當(dāng)前節(jié)點(diǎn)的兄弟節(jié)點(diǎn)迭代輸出:
for sibling in soup.a.next_siblings:
? ? print(sibling)
3.4)前后節(jié)點(diǎn)
前后節(jié)點(diǎn)需要使用.next_element,.previous_element兩個(gè)屬性凛忿,它們是針對所有節(jié)點(diǎn)澈灼,不分層次。
如果想遍歷所有的前后節(jié)點(diǎn)店溢,通過.next_elements叁熔,.previous_elements的迭代器可以向前或向后訪問文檔解析內(nèi)容
for element in soup.a.next_elements:
? ? print(rear(element))
以上是遍歷文檔樹,接下來講解核心的搜索文檔樹床牧。
4搜索文檔樹
Beautiful Soup定義了多種搜索方法荣回,這里著重介紹find_all()方法。
find_all()方法用于搜索當(dāng)前Tag的所有Tag子節(jié)點(diǎn)戈咳,并判斷是否符合過濾器的條件心软,函數(shù)原型為:
find_all(name, attrs, recursive, text, **kwargs)
1)name參數(shù)
name參數(shù)可以查找所有名字為name的標(biāo)記壕吹,字符串對象會(huì)被自動(dòng)忽略掉。name參數(shù)的取值可以是字符串糯累,正則表達(dá)式算利,列表,True和方法泳姐。
最簡單的是字符串效拭。在搜索方法中傳入一個(gè)字符串參數(shù),查找完整匹配的內(nèi)容胖秒。
soup.find_all('b')#查找所有的<b>標(biāo)記缎患,返回值為列表
傳入正則表達(dá)式作為參數(shù),會(huì)通過正則表達(dá)式的match()來匹配內(nèi)容阎肝,查找以b開頭的標(biāo)記
import re?
for tag in soup.find_all(re.compile('^b')):
? ? print(tag.name)
傳入列表參數(shù)挤渔,返回與列表中任意匹配的內(nèi)容,查找所有的<a>,<b>標(biāo)記:
soup.find_all(['a','b'])
傳入?yún)?shù)為True风题,可以匹配任何值判导,下面代碼會(huì)查找所有的Tag,但不會(huì)返回字符串節(jié)點(diǎn)。
for tag in soup.find_all(True):
? ? print(tag.name)
如果沒有合適的過濾器沛硅,還可以定義一個(gè)方法眼刃,方法只接受一個(gè)元素參數(shù)Tag節(jié)點(diǎn),如果方法返回True表示當(dāng)前元素匹配并且被找到摇肌,如果返回false擂红。
2)kwargs參數(shù)
kwargs參數(shù)在python中表示為keyword參數(shù)。如果一個(gè)指定名字的參數(shù)不是搜索內(nèi)置的參數(shù)名围小,搜索時(shí)會(huì)把該參數(shù)當(dāng)作指定名字Tag屬性來搜索昵骤。搜索指定名字的屬性時(shí)可以使用的參數(shù)值包括字符串,正則表達(dá)式肯适,列表变秦,True.(常用來通過屬性搜索)
soup.find_all(id='link2')#搜索每個(gè)標(biāo)記,id屬性為'link2'疹娶,返回列表
傳入正則表達(dá)式
import re
soup.find_all(herf=re.compile('elsie'))#查找herf屬性中包含'elsie'的標(biāo)記
查找含有id屬性的標(biāo)記
soup.find_all(id=True)
若想用python的關(guān)鍵字重復(fù)的屬性過濾伴栓,可以在后面加上下劃線,如class
soup.find_all('a',class_='sister')
使用多個(gè)指定名字的參數(shù)可以同時(shí)過濾tag的多個(gè)屬性:
soup.find_all(herf=re.compile("elsie"),id='link1')
3)text參數(shù)
通過text參數(shù)可以搜索文檔中的字符串內(nèi)容雨饺。與name參數(shù)的可選值一樣钳垮,text ? ?參數(shù)接受字符串,正則表達(dá)式额港,列表饺窿,True。
soup.find_all(text='Elsie')
soup.find_all(text=['Tille','Elsie'])