上篇文章只是簡單講述正則表達式如何讀懂以及 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