內(nèi)容提取神器 beautiful Soup 的用法

圖片來自 unsplash

上篇文章只是簡單講述正則表達式如何讀懂以及 re 常見的函數(shù)的用法贸诚。我們可能讀懂別人的正則表達式窄瘟,但是要自己寫起正則表達式的話跷坝,可能會陷入如何寫的困境侣诵。正則表達式寫起來費勁又出錯率高,那么有沒有替代方案呢边苹?俗話說得好陵且,條條道路通羅馬。目前還兩種代替其的辦法个束,一種是使用 Xpath 神器慕购,另一種就是本文要講的 BeautifulSoup。

1 BeautifulSoup 簡介

引用 BeautifulSoup 官網(wǎng)的說明:

Beautiful Soup is a Python library for pulling data out of HTML and XML files. It works with your favorite parser to provide idiomatic ways of navigating, searching, and modifying the parse tree. It commonly saves programmers hours or days of work.

大致意思如下: BeautifulSoup 是一個能從 HTML 或 XML 文件中提取數(shù)據(jù)的 Python 庫茬底。它能通過自己定義的解析器來提供導(dǎo)航沪悲、搜索,甚至改變解析樹阱表。它的出現(xiàn)殿如,會大大節(jié)省開發(fā)者的時間。

2 安裝 BeautifulSoup

目前 BeautifulSoup 最新版本是 4.6.0最爬,它是支持 Python3的涉馁。所以可以大膽去升級安裝使用。

安裝方法有兩種:

  • 使用pip
    比較推薦使用這種方式爱致,既簡單又方便管理烤送。
pip install beautifulsoup4
# 如果出現(xiàn)因下載失敗導(dǎo)致安裝不上的情況,可以先啟動 ss 再執(zhí)行安裝命令
# 或者在終端中使用代理
pip --proxy http://代理ip:端口 install beautifulsoup4
  • 使用easy_install
easy_install beautifulsoup4
  • 使用系統(tǒng)包管理
sudo apt-get install Python-bs4
# 適用于 ubuntu 系統(tǒng)以及 Debian 系統(tǒng)

3 初始 BeautifulSoup

首先導(dǎo)入 BeautifulSoup 庫蒜鸡,然后創(chuàng)建一個 BeautifulSoup 對象胯努,再利用對象做文章。
具體參考示例代碼:

from bs4 import BeautifulSoup

soup = BeautifulSoup(response)
print(soup.prettify())

上面代碼中逢防,response 可以urlllib或者request請求返回的內(nèi)容叶沛,也可以是本地 HTML 文本。如果要打開本地忘朝,代碼需要改為

soup = BeautifulSoup(open("index.html"))
# 打開當前目錄下 index.html 文件

soup.prettify()函數(shù)的作用是打印整個 html 文件的 dom 樹灰署,例如上面執(zhí)行結(jié)果如下:

<html>
 <head>
   <title>
       The Dormouse's story
   </title>  
 </head>  
 <body>
   <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
   <p class="story">Once upon a time there were three little sisters; and their names were</p>
   <a  class="sister" id="link1"><!-- Elsie --></a>
 </body>
</html>

4 解析 BeautifulSoup 對象

想從 html 中獲取到自己所想要的內(nèi)容,我歸納出三種辦法:

1)利用 Tag 對象

從上文得知局嘁,BeautifulSoup 將復(fù)雜 HTML 文檔轉(zhuǎn)換成一個復(fù)雜的樹形結(jié)構(gòu),每個節(jié)點都是Python對象溉箕。跟安卓中的Gson庫有異曲同工之妙。節(jié)點對象可以分為 4 種:Tag, NavigableString, BeautifulSoup, Comment悦昵。

Tag 對象可以看成 HTML 中的標簽肴茄。這樣說,你大概明白具體是怎么回事但指。我們再通過例子來更加深入了解 Tag 對象寡痰。以下代碼是以 prettify() 打印的結(jié)果為前提抗楔。

  • 例子1

獲取head標簽內(nèi)容

print(soup.head)
# 輸出結(jié)果如下:
<head><title>The Dormouse's story</title></head>
  • 例子2

獲取title標簽內(nèi)容

print(soup.title)
# 輸出結(jié)果如下:
<title>The Dormouse's story</title>
  • 例子3

獲取p標簽內(nèi)容

print(soup.p)
# 輸出結(jié)果如下:
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>

如果 Tag 對象要獲取的標簽有多個的話,它只會返回所以內(nèi)容中第一個符合要求的標簽拦坠。

對象一般含有屬性连躏,Tag 對象也不例外。它具有兩個非常重要的屬性贞滨, <font color='red'>name</font><font color='red'>attrs</font>入热。

name
name 屬性是 Tag 對象的標簽名。不過也有特殊的晓铆,soup 對象的 name 是 [document]

print(soup.name)
print(soup.head.name)
# 輸出結(jié)果如下:
[document]
head

attrs
attrs 屬性是 Tag 對象所包含的屬性值勺良,它是一個字典類型。

print(soup.p.attrs)
# 輸出結(jié)果如下:
{'class': ['title'], 'name': 'dromouse'}

其他三個屬性也順帶介紹下:

  • NavigableString

說白了就是:Tag 對象里面的內(nèi)容

print(soup.title.string)
 # 輸出結(jié)果如下:
The Dormouse's story
  • BeautifulSoup

BeautifulSoup 對象表示的是一個文檔的全部內(nèi)容.大部分時候,可以把它當作 Tag 對象尤蒿。它是一個特殊的 Tag郑气。

print(type(soup.name))
print(soup.name)
print(soup.attrs)
# 輸出結(jié)果如下:
<type 'unicode'>
[document]
{} 空字典
  • Comment

Comment 對象是一個特殊類型的 NavigableString 對象。如果 HTML 頁面中含有注釋及特殊字符串的內(nèi)容腰池。而那些內(nèi)容不是我們想要的,所以我們在使用前最好做下類型判斷忙芒。例如:

if type(soup.a.string) == bs4.element.Comment:
    ...    # 執(zhí)行其他操作示弓,例如打印內(nèi)容

2)利用過濾器

過濾器其實是一個find_all()函數(shù), 它會將所有符合條件的內(nèi)容以列表形式返回呵萨。它的構(gòu)造方法如下:

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

name 參數(shù)可以有多種寫法:

  • (1)節(jié)點名
print(soup.find_all('p'))
# 輸出結(jié)果如下:
[<p class="title" name="dromouse"><b>The Dormouse's story</b></p>, <p class="story">Once upon a time there were three little sisters; and their names were</p>]
  • (2)正則表達式
print(soup.find_all(re.compile('^p')))
# 輸出結(jié)果如下:
[<p class="title" name="dromouse"><b>The Dormouse's story</b></p>, <p class="story">Once upon a time there were three little sisters; and their names were</p>]
  • (3)列表
    如果參數(shù)為列表奏属,過濾標準為列表中的所有元素〕甭停看下具體代碼囱皿,你就會一目了然了。
print(soup.find_all(['p', 'a']))
# 輸出結(jié)果如下:
[<p class="title" name="dromouse"><b>The Dormouse's story</b></p>,  <p class="story">Once upon a time there were three little sisters; and their names were</p>,  <a  class="sister" id="link1"><!-- Elsie --></a>]

另外 attrs 參數(shù)可以也作為過濾條件來獲取內(nèi)容忱嘹,而 limit 參數(shù)是限制返回的條數(shù)嘱腥。

3)利用 CSS 選擇器

以 CSS 語法為匹配標準找到 Tag。同樣也是使用到一個函數(shù)拘悦,該函數(shù)為select()齿兔,返回類型也是 list。它的具體用法如下, 同樣以 prettify() 打印的結(jié)果為前提:

  • (1)通過 tag 標簽查找
print(soup.select(head))
# 輸出結(jié)果如下:
[<head><title>The Dormouse's story</title></head>]
  • (2)通過 id 查找
print(soup.select('#link1'))
# 輸出結(jié)果如下:
[<a  class="sister" id="link1"><!-- Elsie --></a>]
  • (3)通過 class 查找
print(soup.select('.sister'))
# 輸出結(jié)果如下:
[<a  class="sister" id="link1"><!-- Elsie --></a>]
  • (4)通過屬性查找
print(soup.select('p[name=dromouse]'))
# 輸出結(jié)果如下:
[<p class="title" name="dromouse"><b>The Dormouse's story</b></p>]
print(soup.select('p[class=title]'))
# 輸出結(jié)果如下:
[<p class="title" name="dromouse"><b>The Dormouse's story</b></p>]
  • (5)組合查找
print(soup.select("body p"))
# 輸出結(jié)果如下:
[<p class="title" name="dromouse"><b>The Dormouse's story</b></p>,
<p class="story">Once upon a time there were three little sisters; and their names were</p>]
print(soup.select("p > a"))
# 輸出結(jié)果如下:
[<a  class="sister" id="link1"><!-- Elsie --></a>]
print(soup.select("p > .sister"))
# 輸出結(jié)果如下:
[<a  class="sister" id="link1"><!-- Elsie --></a>]

5 處理上下關(guān)系

從上文可知础米,我們已經(jīng)能獲取到節(jié)點對象分苇,但有時候需要獲取其父節(jié)點或者子節(jié)點的內(nèi)容,我們要怎么做了屁桑?這就需要對parse tree進行遍歷

(1)獲取子節(jié)點
利用.children屬性医寿,該屬性會返回當前節(jié)點所以的子節(jié)點。但是它返回的類型不是列表蘑斧,而是迭代器

(2)獲取所有子孫節(jié)點
使用.descendants屬性靖秩,它會返回所有子孫節(jié)點的迭代器

(3)獲取父節(jié)點
通過.parent屬性可以獲得所有子孫節(jié)點的迭代器

(4)獲取所有父節(jié)點
.parents屬性艾帐,也是返回所有子孫節(jié)點的迭代器

(5)獲取兄弟節(jié)點
兄弟節(jié)點可以理解為和本節(jié)點處在統(tǒng)一級的節(jié)點,.next_sibling屬性獲取了該節(jié)點的下一個兄弟節(jié)點盆偿,.previous_sibling則與之相反柒爸,如果節(jié)點不存在,則返回 None

注意:實際 HTML 中的 tag 的.next_sibling.previous_sibling屬性通常是字符串或空白事扭,因為空白或者換行也可以被視作一個節(jié)點捎稚,所以得到的結(jié)果可能是空白或者換行

(5)獲取所有兄弟節(jié)點
通過.next_siblings.previous_siblings屬性可以對當前節(jié)點的兄弟節(jié)點迭代輸出


上篇文章:Python 正則表達式
推薦閱讀:詳解 python3 urllib


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市求橄,隨后出現(xiàn)的幾起案子今野,更是在濱河造成了極大的恐慌,老刑警劉巖罐农,帶你破解...
    沈念sama閱讀 212,294評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件条霜,死亡現(xiàn)場離奇詭異,居然都是意外死亡涵亏,警方通過查閱死者的電腦和手機宰睡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來气筋,“玉大人拆内,你說我怎么就攤上這事〕枘” “怎么了麸恍?”我有些...
    開封第一講書人閱讀 157,790評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長搀矫。 經(jīng)常有香客問我抹沪,道長,這世上最難降的妖魔是什么瓤球? 我笑而不...
    開封第一講書人閱讀 56,595評論 1 284
  • 正文 為了忘掉前任融欧,我火速辦了婚禮,結(jié)果婚禮上冰垄,老公的妹妹穿的比我還像新娘蹬癌。我一直安慰自己,他們只是感情好虹茶,可當我...
    茶點故事閱讀 65,718評論 6 386
  • 文/花漫 我一把揭開白布逝薪。 她就那樣靜靜地躺著,像睡著了一般蝴罪。 火紅的嫁衣襯著肌膚如雪董济。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,906評論 1 290
  • 那天要门,我揣著相機與錄音虏肾,去河邊找鬼廓啊。 笑死,一個胖子當著我的面吹牛封豪,可吹牛的內(nèi)容都是我干的谴轮。 我是一名探鬼主播,決...
    沈念sama閱讀 39,053評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼吹埠,長吁一口氣:“原來是場噩夢啊……” “哼第步!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缘琅,我...
    開封第一講書人閱讀 37,797評論 0 268
  • 序言:老撾萬榮一對情侶失蹤粘都,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后刷袍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翩隧,經(jīng)...
    沈念sama閱讀 44,250評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,570評論 2 327
  • 正文 我和宋清朗相戀三年呻纹,在試婚紗的時候發(fā)現(xiàn)自己被綠了堆生。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,711評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡居暖,死狀恐怖顽频,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情太闺,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評論 4 332
  • 正文 年R本政府宣布嘁圈,位于F島的核電站省骂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏最住。R本人自食惡果不足惜钞澳,卻給世界環(huán)境...
    茶點故事閱讀 40,018評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涨缚。 院中可真熱鬧轧粟,春花似錦、人聲如沸脓魏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茂翔。三九已至混蔼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間珊燎,已是汗流浹背惭嚣。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評論 1 266
  • 我被黑心中介騙來泰國打工遵湖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晚吞。 一個月前我還...
    沈念sama閱讀 46,461評論 2 360
  • 正文 我出身青樓延旧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親槽地。 傳聞我的和親對象是個殘疾皇子迁沫,可洞房花燭夜當晚...
    茶點故事閱讀 43,595評論 2 350

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