Python爬蟲之Beautiful Soup用法

關(guān)于bs4孕讳,官方文檔的介紹已經(jīng)非常詳細(xì)了,傳送:Beautifulsoup 4官方文檔,這里我把它組織成自己已經(jīng)消化的筆記厂财,你們最好看官方的璃饱,官方的全些可視化更強(qiáng)。本文從解釋器磷支,DOM樹創(chuàng)建雾狈,遍歷DOM樹善榛,修改DOM樹锭弊,Beautiful Soup3和4的區(qū)別味滞,Soup結(jié)合Requests 這幾個(gè)主題整理剑鞍。ps蚁署,其實(shí)如果掌握了javascript的DOM樹的話光戈,會(huì)更好理解soup遂赠。



Beautiful Soup

將復(fù)雜HTML文檔轉(zhuǎn)換成一個(gè)復(fù)雜的樹形結(jié)構(gòu)DOM,每個(gè)節(jié)點(diǎn)都是Python對(duì)象,所有對(duì)象可以歸納為4種: Tag , NavigableString , BeautifulSoup , Comment .


A筷弦、DOM樹的解釋器


推薦使用lxml作為解析器,因?yàn)樾矢呃们伲馕銎鞯暮锰幨强梢匀蒎e(cuò)比如沒(méi)有結(jié)束標(biāo)簽奸绷。


B号醉、創(chuàng)建DOM樹的三種類型:

1)打開本地文件with open("foo.html","r") as foo_file:soup_foo = BeautifulSoup(foo_file)

2)手動(dòng)創(chuàng)建soup = BeautifulSoup(“hello world”,編碼類型選填)

3)打開外部文件url = "http://www.packtpub.com/books"??? page = urllib.urlopen(url)soup_packtpage = BeautifulSoup(page,'lxml')


C.DOM樹介紹:

節(jié)點(diǎn)【元素,屬性父虑,內(nèi)容(string授药,NavigableString悔叽,contents娇澎,Comment】,還有節(jié)點(diǎn)的家庭【父節(jié)點(diǎn)括细,同輩鄰接兄弟節(jié)點(diǎn)奋单,同輩往右鄰接所有兄弟節(jié)點(diǎn)览濒,同輩往左鄰接所有兄弟節(jié)點(diǎn)贷笛,直接子節(jié)點(diǎn)江兢,所有子節(jié)點(diǎn)】

1)節(jié)點(diǎn)tag:類似javascript的元素-

訪問(wèn)元素:soup = BeautifulSoup(html_tag1,'lxml')tag1 = soup.a

訪問(wèn)元素的名字:tagname = tag1.name

訪問(wèn)元素的屬性:tag1['class']或tag1.attrs

修改元素的屬性訪問(wèn):? tag['class'] = 'verybold'賦予多個(gè)屬性rel_soup.a['rel'] = ['index', 'contents']
是否有元素的屬性:tag1.has_attr('class')
獲取所有屬性:比如獲取a所有標(biāo)簽的鏈接杉允,for link in soup.find_all('a'): print(link.get('href'))

獲取元素的內(nèi)容:soup.p.string .如果tag包含了多個(gè)子節(jié)點(diǎn),tag就無(wú)法確定 .string 方法應(yīng)該調(diào)用哪個(gè)子節(jié)點(diǎn)的內(nèi)容, .string 的輸出結(jié)果是 None.? 如果tag中包含多個(gè)字符串 [2] ,可以使用 .strings 來(lái)循環(huán)獲取.for string in soup.strings: print(repr(string))

獲取元素的內(nèi)容含編碼過(guò)的:head_tag.contents拢驾,#[u'Hello', u' there']

獲取元素帶標(biāo)簽的內(nèi)容:head_tag.NavigableString改基,#<b>Hello there.</b>

獲取元素帶注釋的內(nèi)容Comment創(chuàng)建對(duì)象時(shí)用Comment,輸出對(duì)象時(shí)用soup.b.prettify()

獲取所有文字內(nèi)容:soup.get_text()

2)節(jié)點(diǎn)的家庭

訪問(wèn)直接子節(jié)點(diǎn):.children()? 和.contents()

.children() 不返回文本節(jié)點(diǎn)躁染,如果需要獲得包含文本和注釋節(jié)點(diǎn)在內(nèi)的所有子節(jié)點(diǎn)吞彤,請(qǐng)使用 .contents()饰恕。

遍歷所有子節(jié)點(diǎn):.descendants

遍歷子節(jié)點(diǎn)的內(nèi)容:.strings.stripped_strings(去除空格或空行)

訪問(wèn)父節(jié)點(diǎn):.parent

遍歷所有父節(jié)點(diǎn):.parents

獲得匹配元素集合中所有元素的同輩元素:.next_siblings .previous_siblings

獲得匹配元素集合中的下或上一個(gè)同輩元素.next_element? .previous_element 屬性:

獲得匹配元素集合中的往后所有同輩或者往前所有元素:.next_elements? .previous_elements


D埋嵌、遍歷DOM樹

可以通過(guò)方法或者css選擇器

方法:find_all( name , attrs , recursive , string, **kwargs )

參數(shù):支持可以使用的參數(shù)值包括 字符串 , 正則表達(dá)式 , 列表, True雹嗦,自定義包含一個(gè)參數(shù)的方法Lambda表達(dá)式(這個(gè)我放在正則表達(dá)式那篇文章了)俐银。

如支持字符串soup.find_all('b')捶惜,正則表達(dá)式soup.find_all(re.compile("^b"))吱七,列表soup.find_all(["a","b"])踊餐,True值如soup.find_all(True)吝岭,自定義包含一個(gè)參數(shù)的方法如soup.find_all(has_class_but_no_id)吧寺,而方法def has_class_but_no_id(tag): return tag.has_attr('class') and not tag.has_attr('id')稚机。


name:是元素名赖条,可以是一個(gè)元素find('head')常熙,也可以是多個(gè)元素裸卫,多個(gè)元素可以用列表find(['a','b'])纽竣,字典find({'head':True, 'body':True}),或者lambda表達(dá)式find(lambda name: if len(name) == 1) 搜索長(zhǎng)度為1的元素调炬,或者正則表達(dá)式匹配結(jié)果find(re.compile('^p'))來(lái)表示缰泡,find(True) 搜索所有元素

attrs:是元素屬性缠借。

搜索指定名字的屬性時(shí)可以使用的參數(shù)值包括 字符串 , 正則表達(dá)式 , 列表, True?

按照屬性值搜索tag:一個(gè)屬性如find(id='xxx') 泼返,soup.find_all("a", class_="sister"),soup.find_all("a", attrs={"class": "sister"})绅喉;也可以是多個(gè)屬性柴罐,比如正則表達(dá)式:find(attrs={id=re.compile('xxx'), p='xxx'})或者soup.find_all(class_=re.compile("itl"))革屠,或者true方法:find(attrs={id=True, algin=None}),或者列表方法find_all(attrs={"data-foo": "value"})似芝。



recursive和limit參數(shù)

recursive=False表示只搜索直接兒子,否則搜索整個(gè)子樹麻诀,默認(rèn)為True。當(dāng)使用findAll或者類似返回list的方法時(shí)呻率,limit屬性用于限制返回的數(shù)量呻引,如findAll('p', limit=2): 返回首先找到的兩個(gè)tag.

soup.html.find_all("title", recursive=False)

string 參數(shù)

通過(guò) string參數(shù)可以搜搜文檔中的字符串內(nèi)容.與 name 參數(shù)的可選值一樣, text 參數(shù)接受 字符串 , 正則表達(dá)式 , 列表, 自定義方法元践,True.

soup.find_all(string="Elsie")#字符串

soup.find_all(string=["Tillie", "Elsie", "Lacie"]) )#列表

soup.find_all(string=re.compile("Dormouse"))#正則表達(dá)式

def is_the_only_string_within_a_tag(s): #方法

""Return True if this string is the only child of its parent tag.""

return (s == s.parent.string)

soup.find_all(string=is_the_only_string_within_a_tag)

關(guān)于Beautiful Soup樹的操作具體可以看官方文檔?


另外還有和find_all類似的方法:

find( name , attrs , recursive , text , **kwargs )

和find_all區(qū)別:返回第一個(gè)節(jié)點(diǎn)

find_parents()? find_parent()

find_next_siblings()? find_next_sibling()

find_previous_siblings()? find_previous_sibling()

find_all_next()? find_next()

find_all_previous() 和 find_previous()


除了find方法遍歷外单旁,還有CSS選擇器

CSS選擇器:soup.select()

元素查找象浑,比如元素名soup.select('a')愉豺,類名soup.select('.sister')蚪拦,ID名.soupselect('#link1')或者soup.select("#link1,#link2")外盯,組合查饱苟,比如soup.select('p #link1')箱熬,或者直接子元素選擇器soup.select("head > title")城须,所有子元素soup.select("body a"),篩選soup.select("p nth-of-type(3)")糕伐,soup.select("p > a:nth-of-type(2)")良瞧。

屬性查找soup.select('a[class="sister"]'),soup.select('p a[#link1 ~ .sister")褥蚯,直接后面兄弟soup.select("#link1 + .sister")挚冤,soup.select('a[href*=".com/el"]')

查找到的元素的第一個(gè)soup.select_one(".sister")


E、修改DOM樹

修改tag的名稱和屬性:tag.name = "blockquote" tag['class'] = 'verybold'

修改tag的內(nèi)容:.string tag = soup.a?? tag.string = "New link text."

添加節(jié)點(diǎn)內(nèi)容:append()或者NavigableString構(gòu)造對(duì)象赞庶,new_string構(gòu)造comment注釋

append():soup.a.append("Bar")训挡,輸出內(nèi)容時(shí)soup.a.contents,結(jié)果從"<a>Foo</a>變成<a>FooBar</a>"

NavigableString方法:new_string = NavigableString(" there") tag.append(new_string)

new_string構(gòu)造comment注釋:from bs4 import Comment new_comment = soup.new_string("Nice to see you.", Comment) tag.append(new_comment)


添加子節(jié)點(diǎn):new_tag(),第一個(gè)參數(shù)作為tag的name,是必填,其它參數(shù)選填

soup = BeautifulSoup("<b></b>") original_tag = soup.b

new_tag = soup.new_tag("a", ) original_tag.append(new_tag)

#<b><a ></b>


插入子節(jié)點(diǎn)或內(nèi)容:Tag.insert(指定位置索引歧强,節(jié)點(diǎn)或內(nèi)容)澜薄,Tag.insert_before(節(jié)點(diǎn)或內(nèi)容) 和 Tag.insert_after(節(jié)點(diǎn)或內(nèi)容)

tag = soup.p?

insert():tag.insert(1, "but did not")#從"<p>haha<a href='www.baidu.com'>i like <i>fish</i></a></p>"變成"<p>haha but did not <a href='www.baidu.com'></p>"

insert_before():如tag = soup.new_tag("i") tag.string = "Don't" soup.b.string.insert_before(tag)

insert_after():如soup.b.i.insert_after(soup.new_string(" ever "))


刪除當(dāng)前節(jié)點(diǎn)內(nèi)容:tag = soup.a ? ?? tag.clear()

刪除當(dāng)前節(jié)點(diǎn)并返回刪除后的內(nèi)容:tag=soup.i.extract(),注意這個(gè)tag是新創(chuàng)建的摊册,和dom樹soup有區(qū)別肤京。

刪除當(dāng)前tag并完全銷毀,不會(huì)創(chuàng)建新的:soup.i.decompose()
刪除當(dāng)前節(jié)點(diǎn)并替換新的節(jié)點(diǎn)或內(nèi)容:a_tag = soup.a new_tag = soup.new_tag("b") new_tag.string = "example.net" a_tag.i.replace_with(new_tag)

對(duì)指定節(jié)點(diǎn)進(jìn)行包裝,添加標(biāo)簽如div:soup.p.wrap(soup.new_tag("div"))

刪除指定節(jié)點(diǎn)的標(biāo)簽如a標(biāo)簽:tag.a.unwrap()


打印DOM樹或DOM樹的節(jié)點(diǎn):

含標(biāo)簽:soup.prettify() ,soup.p.prettify()? #<p>i will<p>

不含標(biāo)簽:unicode() 或 str() 方法:#i will

特殊字符如ldquo轉(zhuǎn)換成‘\’:soup = BeautifulSoup("“Dammit!” he said.") unicode(soup)

獲取本文內(nèi)容并以字符隔開:get_text(隔開字符可選填,去除空白符開關(guān)可選填),soup.get_text()突琳,soup.get_text("|")如‘i like | fish’啊终,soup.get_text("|", strip=True)


判斷節(jié)點(diǎn)內(nèi)容是否相同:first_b == second_b

判斷節(jié)點(diǎn)是否相同,即指向同一地址:first_b is second_b
判斷節(jié)點(diǎn)的類型是否是已知類型:isinstance(1, int)

復(fù)制節(jié)點(diǎn):p_copy = copy.copy(soup.p)




F已卸、Python3和Python2版本的Beautiful Soup區(qū)別



G具伍、Soup與Requests






附上可靠的網(wǎng)絡(luò)連接代碼

把這段代碼

from urllib.request import urlopen

from bs4 import BeautifulSoup

html = urlopen("http://www.pythonscraping.com/pages/page1.html")

bsObj = BeautifulSoup(html.read())

print(bsObj.h1)

改成以下代碼

from urllib.request import urlopen

from urllib.error import HTTPError

from bs4 import BeautifulSoup

??????? def getTitle(url):

?????? ? ? ? ? try:

???????????????????? html = urlopen(url)

??????? ? ? ? ? except HTTPError as e:

???????????????????? return None

?????????????? try:

???????????????????? bsObj = BeautifulSoup(html.read())

????????????????????? title = bsObj.body.h1

???????????? ?? except AttributeError as e:

????????????? ? ? ? ? return None

???????????? ?? return title

??????? title = getTitle("http://www.pythonscraping.com/pages/page1.html")

??????? if title == None:

??????????? print("Title could not be found")

??????? else:

???????????? print(title)




不過(guò)橄抹,最終選擇了xpath

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市榄融,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異章鲤,居然都是意外死亡皱蹦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門籍滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)插掂,“玉大人璃弄,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵沦泌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么袁梗? 我笑而不...
    開封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上验游,老公的妹妹穿的比我還像新娘蒜魄。我一直安慰自己谦铃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乙埃,像睡著了一般趋箩。 火紅的嫁衣襯著肌膚如雪竹勉。 梳的紋絲不亂的頭發(fā)上女气,一...
    開封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天霎肯,我揣著相機(jī)與錄音,去河邊找鬼荤崇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播阔逼,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼及志,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乾蛤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤尤蛮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婿滓,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年续担,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜀铲。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖托嚣,靈堂內(nèi)的尸體忽然破棺而出冲秽,到底是詐尸還是另有隱情球订,我是刑警寧澤较店,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布官卡,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏阻课。R本人自食惡果不足惜适秩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一洒试、第九天 我趴在偏房一處隱蔽的房頂上張望乍构。 院中可真熱鬧哥遮,春花似錦、人聲如沸陵究。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)畔乙。三九已至君仆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牲距,已是汗流浹背返咱。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牍鞠,地道東北人咖摹。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像难述,于是被迫代替她去往敵國(guó)和親萤晴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容