BeautifulSoup 是一個(gè)可以從HTML或XML文件中提取數(shù)據(jù)的Python庫群嗤。它能夠通過你喜歡的轉(zhuǎn)換器實(shí)現(xiàn)慣用的文檔導(dǎo)航,查找,修改文檔的方式畅铭。換句話說氏淑,它是我們解析網(wǎng)頁的利器
BeautifulSoup3 目前已經(jīng)停止開發(fā),今天學(xué)習(xí)的是BeautifulSoup4
1.簡單入手
我們以豆瓣網(wǎng)為例,編輯下面這段代碼
from bs4 import BeautifulSoup
import requests
if __name__ == "__main__":
req = requests.get("https://www.douban.com/") #獲取豆瓣網(wǎng)址
html = req.text #獲得網(wǎng)頁源代碼
soup = BeautifulSoup(html)
print(soup.prettify())
首先通過一個(gè)requests.get()
來獲得目標(biāo)網(wǎng)址的信息硕噩,再通過req.text
獲得網(wǎng)頁源代碼假残,之后就可以利用BeautifulSoup來對源碼進(jìn)行解析。這里進(jìn)行操作后榴徐,你可能會(huì)有這樣的疑問,print(html)
和print(soup.prettify())
的輸出結(jié)果不是一樣嗎匀归?確實(shí)看輸出結(jié)果是這樣的坑资,好像沒什么區(qū)別,其實(shí)兩者是不一樣的穆端,前者只是單純的輸出網(wǎng)頁源碼袱贮,后者是將網(wǎng)頁源碼解析后模塊化輸出,仔細(xì)看一下輸出結(jié)果你會(huì)發(fā)現(xiàn)些許差別体啰。沒發(fā)現(xiàn)也沒有關(guān)系攒巍,下面我們來對解析后的網(wǎng)頁進(jìn)行操作嗽仪。
接著上面的代碼塊操作
print(soup.title) #輸出<title>標(biāo)簽
print(soup.title.name) #輸出title的name
print(soup.title.string) #輸出title的內(nèi)容
print(soup.title.parent.name) #輸出title父節(jié)點(diǎn)的name
print(soup.p) #輸出<p>標(biāo)簽
print(soup.p['class']) #輸出<p>標(biāo)簽的類
print(soup.a) #輸出<a>標(biāo)簽
print(soup.find_all('a')) #找到所有的<a>標(biāo)簽
print(soup.find(id="anony-time")) #找到所有的id為anony-time標(biāo)簽
print(soup.find_all('p',class_="app-title")) #找到所有class為app-title的<a>標(biāo)簽
自己操作實(shí)現(xiàn)一下,你就能對BeautifulSoup的功能有著更深入的了解柒莉。其實(shí)不限于此闻坚,BeautifulSoup能做的還有更多,比如它可以提取網(wǎng)頁中的鏈接
#提取網(wǎng)頁所有<a>標(biāo)簽里的鏈接
for link in soup.find_all('a'):
print(link.get('href'))
對這段代碼稍加限定條件就可以提取到指定<a>
標(biāo)簽的鏈接兢孝,比如soup.find_all('a',class_='lnk-book')
就可以查找所有類別為lnk-book
的<a>
標(biāo)簽
我們也可以通過下面的操作來獲得網(wǎng)頁所有的文字內(nèi)容
print(soup.get_text())
2.BeautifulSoup——解析器
在前面的代碼塊中有一行代碼是這樣的soup = BeautifulSoup(html)
這行代碼其實(shí)不是很規(guī)范窿凤,一般會(huì)在后面補(bǔ)充一個(gè)解析器,變成這樣soup = BeautifulSoup(html,'lxml')
跨蟹。BeautifulSoup支持Python標(biāo)準(zhǔn)庫中的HTML解析器雳殊,還支持一些第三方的解析器,它一共有這么幾種
解析器 | 使用方法 | 優(yōu)勢 | 劣勢 |
---|---|---|---|
Python標(biāo)準(zhǔn)庫 | BeautifulSoup(markup, "html.parser") |
Python的內(nèi)置標(biāo)準(zhǔn)庫 執(zhí)行速度適 文檔容錯(cuò)能力強(qiáng) |
Python 2.7.3 or 3.2.2)前 的版本中文檔容錯(cuò)能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, "lxml") |
速度快 唯一支持XML的解析器 |
需要安裝C語言庫 |
lxml XML 解析器 |
BeautifulSoup(markup, ["lxml", "xml"]) BeautifulSoup(markup, "xml")
|
速度快 唯一支持XML的解析器 |
需要安裝C語言庫 |
html5lib | BeautifulSoup(markup, "html5lib") |
最好的容錯(cuò)性 以瀏覽器的方式解析文檔 生成HTML5格式的文檔 |
速度慢 不依賴外部擴(kuò)展 |
推薦使用lxml
作為解析器,因?yàn)樾矢? 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必須安裝lxml
或html5lib
, 因?yàn)槟切㏄ython版本的標(biāo)準(zhǔn)庫中內(nèi)置的HTML解析方法不夠穩(wěn)定窗轩。
3.對象
BeautifulSoup將復(fù)雜HTML文檔轉(zhuǎn)換成一個(gè)復(fù)雜的樹形結(jié)構(gòu)夯秃,每個(gè)節(jié)點(diǎn)都是Python對象,所有對象可以歸納為4種: Tag
, NavigableString
, BeautifulSoup
,Comment
.
3.1Tag
在前面的操作中痢艺,實(shí)際上我們已經(jīng)見識(shí)了解析后的tag仓洼,解析后的網(wǎng)頁都是由一個(gè)個(gè)的tag組成的。
soup = BeautifulSoup(html,'lxml')
print(soup.title) #輸出了<title>這個(gè)tag
每一個(gè)tag都有自己的name腹备,我們可以查看
print(soup.title.name) #輸出title的name
每一個(gè)tag都有自己的屬性衬潦,比如<li> <a target="_blank" class="lnk-book" href="[https://book.douban.com](https://book.douban.com/)">豆瓣讀書</a></li>
,我們可以查看
soup = BeautifulSoup('<li> <a target="_blank" class="lnk-book" href="
[https://book.douban.com](https://book.douban.com/)">豆瓣讀書</a></li>','lxml')
print(soup.a['target']) #查看target屬性
print(soup.a['class']) #查看class屬性
print(soup.a['href']) #查看href屬性
這里也就是我們上面獲取<a>
標(biāo)簽里面鏈接的原理植酥,也可以直接查看它的所有屬性
print(soup.a.attrs)
另外镀岛,tag里面的屬性也是可以操作的,可以刪除和修改友驮,和字典操作是一樣的
soup.a['target'] = 'white' #修改
del soup.a['class'] #刪除
print(soup.a['target'])
print(bool(soup.a['class'])) #這一步會(huì)報(bào)錯(cuò)漂羊,因?yàn)橐呀?jīng)被刪掉了
熟悉html的童鞋還會(huì)知道另外一個(gè)問題,就是有些html會(huì)有多值屬性卸留,比如<a class="lnk-book ink-book">豆瓣讀書</a>
soup = BeautifulSoup('<a class="lnk-book ink-book">豆瓣讀書</a>','lxml')
print(soup.a['class'])
#['lnk-book', 'ink-book']
會(huì)發(fā)現(xiàn)走越,BeautifulSoup會(huì)自動(dòng)的區(qū)分開多值屬性,并以list的形式返回耻瑟,但是當(dāng)你將tag轉(zhuǎn)換為string字符串時(shí)旨指,它就會(huì)自動(dòng)將他們的多值屬性合并到一起。
3.2NavigableString
NavigableString是一個(gè)類喳整,用來包裝tag中的字符串谆构,一個(gè) NavigableString 字符串與Python中的Unicode字符串相同。tag中包含的字符串不能編輯,但是可以被替換成其它的字符串框都。
soup = BeautifulSoup('<a class="lnk-book ink-book">豆瓣讀書</a>','lxml')
soup.a.string.replace_with('walt white')
print(soup.a)
#<a class="lnk-book ink-book">walt white</a>
3.3BeautifulSoup
BeautifulSoup 對象表示的是一個(gè)文檔的全部內(nèi)容搬素。大部分時(shí)候,可以把它當(dāng)作 Tag 對象。上一段代碼中的soup
就是一個(gè)BeautifulSoup對象熬尺。
3.4Comment
comment其實(shí)顧名思義就是注釋的意思摸屠,它是NavigableString一個(gè)特殊子類,用來表示html中的注釋
soup = BeautifulSoup('<b><!--Hey, how you\'re doing ?--></b>','lxml')
comment = soup.b.string
print(comment)
注意這段代碼粱哼,輸出結(jié)果是Hey, how you're doing ?
季二,而不是html的代碼中的``,已經(jīng)自動(dòng)過濾了注釋標(biāo)簽了
4.遍歷文檔樹
這里用一段官網(wǎng)給出的文檔來做例子
from bs4 import BeautifulSoup
if __name__ == "__main__":
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" id="link1">Elsie</a>,
<a class="sister" id="link2">Lacie</a> and
<a class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = BeautifulSoup(html_doc,'lxml')
4.1子節(jié)點(diǎn)
我們可以通過這樣的方法來獲取節(jié)點(diǎn)中的子節(jié)點(diǎn)
print(soup.body.b)
這樣通過點(diǎn)取的方法只能獲取第一個(gè)元素皂吮,如果需要獲取全部的標(biāo)簽戒傻,則需要通過另外一種方法
print(soup.find_all('a'))
它可以通過列表的形式輸出所有的標(biāo)簽
4.2.contents
利用.contents
可以來將子節(jié)點(diǎn)以列表的形式輸出
soup = BeautifulSoup(html_doc,'lxml')
head_tag = soup.head
title_tag = head_tag.contents[0]
print(title_tag)
print(title_tag.contents)
通過結(jié)果可以看到.contents
輸出了了當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn),并用列表的形式輸出蜂筹,直到到最后一個(gè)字符串節(jié)點(diǎn)需纳,無法再往下遍歷
4.3.children
利用.children
操作也可以遍歷子節(jié)點(diǎn)
soup = BeautifulSoup(html_doc,'lxml')
title_tag = soup.head.contents[0]
for child in title_tag.children:
print(child)
4.4.descendants
利用.descendants
操作也可以遍歷子孫節(jié)點(diǎn)
for child in title_tag.descendants:
print(child)
4.5.string
如果tag有且僅有一個(gè)子節(jié)點(diǎn),利用.string
可以得到子節(jié)點(diǎn)的內(nèi)容艺挪,但是tag如果有多個(gè)子節(jié)點(diǎn)不翩,那么會(huì)輸出none
print(title_tag.string)
print(soup.string)
#The Dormouse's story
#None
4.6.strings 和 stripped_strings
如果tag中包含多個(gè)字符串 ,可以使用 .strings
來循環(huán)獲取
for string in soup.strings:
print(repr(string))
#"The Dormouse's story"
#'\n'
#"The Dormouse's story"
#'\n'
#'Once upon a time there were three little sisters; and their names were\n '
#'Elsie'
#',\n '
#'Lacie'
#' and\n '
#'Tillie'
#';\n and they lived at the bottom of a well.'
#'\n'
#'...'
#'\n'
輸出的字符串中可能包含了很多空格或空行麻裳,使用 .stripped_strings
可以去除多余空白內(nèi)容:
for string in soup..stripped_strings:
print(repr(string))
#"The Dormouse's story"
#"The Dormouse's story"
#'Once upon a time there were three little sisters; and their names were'
#'Elsie'
#','
#'Lacie'
#'and'
#'Tillie'
#';\n and they lived at the bottom of a well.'
#'...'
今天的學(xué)習(xí)就到這里了口蝠,BeautifulSoup篇學(xué)習(xí)還沒有結(jié)束,后面還會(huì)繼續(xù)
歡迎關(guān)注公眾號(hào):老白和他的爬蟲