Beautiful Soup 是一個可以從HTML和XML文件中提取數(shù)據(jù)的Python。它可以實現(xiàn)文檔的增刪改查操作结窘,我們側(cè)重點是它的查詢操作。
安裝 Beautiful Soup
你可以根據(jù)自己的系統(tǒng)選擇下面的安裝代碼進行安裝操作:
$ apt-get install Python-bs4
$ easy_install beautifulsoup4
$ pip install beautifulsoup4
安裝解析器
Beautiful Soup支持Python標(biāo)準(zhǔn)庫中的HTML解析器,還支持一些第三方的解析器喳逛,其中一個是 lxml邮偎。根據(jù)操作系統(tǒng)不同管跺,你可以選擇下面方法來安裝 lxml:
$ apt-get install Python-lxml
$ easy_install lxml
$ pip install lxml
另外一個可供選擇的解析器是純Python實現(xiàn)的 html5lib,解析方式和瀏覽器相同禾进,你可以選擇下面的方法來安裝 html5lib:
$ apt-get install Python-html5lib
$ easy_install html5lib
$ pip install html5lib
幾種解析器的優(yōu)缺點
解析器 | 使用方法 | 優(yōu)勢 | 劣勢 |
---|---|---|---|
Python標(biāo)準(zhǔn)庫 | BeautifulSoup(markup, "html.parser") | 執(zhí)行速度適中豁跑,文檔容錯能力強 | Python2.7.3 or 3.2.3前的版本文檔容錯能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, "lxml") | 速度快,文檔容錯能力強 | 需要安裝C語言庫 |
lxml XML 解析器 | BeautifulSoup(markup, "xml") | 速度快泻云,唯一支持XML的解析器 | 需要安裝C語言庫 |
html5lib | BeautifulSoup(markup, "html5lib") | ||
最高的容錯性艇拍,以瀏覽器的方式解析文檔,生成HTML5格式的文檔 | 數(shù)獨慢宠纯,不依賴外部擴展 |
推薦使用 lxml 作為解析器卸夕,因為效率更高。在Python2.7.3之前的版本和Python3.2.3之前的版本婆瓜,必須安裝 lxml 或 html5lib快集,因為Python版本的標(biāo)準(zhǔn)庫中的內(nèi)置的 HTML 解析方法不夠穩(wěn)定。
如何使用
將一段文檔傳入 Beautiful Soup 的構(gòu)造方法,就能得到一個文檔的對象碍讨,可以傳入一段文字或一個文件句柄治力。
from bs4 import BeautifulSoup
soup = BeautifulSoup(open("index.html")) // 文件句柄
soup = BeautifulSoup("<html>data</html>") // 文檔
文檔被轉(zhuǎn)換成 Unicode,并且HTML的實例都被轉(zhuǎn)換成 Unicode 編碼勃黍,然后宵统,Beautiful Soup選擇最合適的解析器來解析這段文檔,如果手動指定解析器覆获,那么 Beautiful Soup 會選擇指定的解析器來解析文檔马澈。
對象的種類
Beautiful Soup 將復(fù)雜 HTML 文檔轉(zhuǎn)換成一個復(fù)雜的樹形結(jié)構(gòu),每隔節(jié)點都是Python對象弄息,多有對象可以歸納為4種:Tag
痊班,NavigableString
,BeautifulSoup
摹量,Comment
涤伐。
Tag對象
Tag 對象與 XML 或 HTML 原生文檔中的 tag 相同。Tag 有很多方法和屬性缨称,其中最重要的屬性:name 和 attributes凝果。
from bs4 import BeautifulSoup
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','lxml')
tag = soup.b // 一個 Tag 對象
print(type(tag)) // <class 'bs4.element.Tag'>
print(tag.name) // tag都有自己的名字:b
print(tag.attrs) // tag的屬性字典:{'class': ['boldest']}
print(tag['class']) // 屬性字典中的'class'值:['boldest']
// tag 的屬性可以被添加,刪除或修改睦尽,屬性操作和字典一樣
tag['class'] = 'myClsss'
print(tag) // <b class="myClsss">Extremely bold</b>
tag['id'] = 'custemId'
print(tag) // <b class="myClsss" id="custemId">Extremely bold</b>
NavigableString對象
字符串常被包含在 tag 內(nèi)器净。Beautiful Soup 用 NavigableString 類來包裝 tag 中的字符串。
print(type(tag.string)) // <class 'bs4.element.NavigableString'>
print(tag.string) // Extremely bold
字符串不支持 .contents 或 .string 屬性或 find() 方法当凡。
BeautifulSoup對象
該對象表示的是一個文檔的全部內(nèi)容山害,大部分的適合可以把它當(dāng)作 Tag 對象。因為 BeautifulSoup 對象并不是真正的 HTML 或 XML 的tag沿量,所以它沒有name和attributes屬性浪慌。但有時查看它時,.name 屬性還是可以的朴则。
print(type(soup)) // <class 'bs4.BeautifulSoup'>
print(soup.name) // [document]
Comment對象
Tag , NavigableString , BeautifulSoup 幾乎覆蓋了html和xml中的所有內(nèi)容,但是還有一些特殊對象.容易讓人擔(dān)心的內(nèi)容是文檔的注釋部分眷射。
markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>"
soup = BeautifulSoup(markup,'lxml')
comment = soup.b.string
print(type(comment)) // <class 'bs4.element.Comment'>
Comment 對象是一個特殊類型的 NavigableString 對象:
print(comment) // Hey, buddy. Want to buy a used parser?
但是當(dāng)它出現(xiàn)在HTML文檔中時, Comment 對象會使用特殊的格式輸出:
print(soup.b.prettify())
<b>
<!--Hey, buddy. Want to buy a used parser?-->
</b>
搜索文檔樹
說完 Beautiful Soup 中的四種對象,接下來介紹一下如何搜索內(nèi)容佛掖。 Beautiful Soup 提供了很多搜索方法妖碉,這里著重介紹其中的兩個:find()
和 find_all()
,其他的方法和參數(shù)以及用法都類似芥被。
過濾器
過濾器作為搜索文檔的參數(shù)欧宜,貫穿整個搜索的 API。過濾器可以被用在 tag 中的 name 中拴魄,節(jié)點的屬性中冗茸,字符串中或者他們的混合中席镀。過濾器可以是字符串、正則表達(dá)式夏漱、列表豪诲、True值甚至是方法。
- 字符串
soup.find_all('b') // 查找文檔中所有的<b>標(biāo)簽
- 正則表達(dá)式
import re
// 查找 b 開頭的標(biāo)簽
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
// body
// b
- 列表
markup = '''
<b>Hey, buddy. Want to buy a used parser?</b>
<p class="title">The Dormouse's story</p>
<a>Once upon a time there were three little sisters</a>
'''
soup = BeautifulSoup(markup,'lxml')
for tag in soup.find_all(['a','p']):
print(tag)
// <p class="title">The Dormouse's story</p>
// <a>Once upon a time there were three little sisters</a>
- True
True 可以匹配任何值
for tag in soup.find_all(True):
print(tag.name)
// html
// body
// b
// p
// a
- 方法
如果沒有合適的過濾器挂绰,那么還可以定一個方法屎篱,方法只接收一個參數(shù),如果這個方法返回 True 表示當(dāng)前元素匹配并且被找到葵蒂,如果不是則返回 False
def has_class(tag):
return tag.has_attr('class')
for tag in soup.find_all(has_class):
print(tag)
# <p class="title">The Dormouse's story</p>
find_all()
find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
- name 參數(shù)
name 參數(shù)可以查找所有名字為 name 的tag交播,字符串對象會被自動忽略掉。其中践付,name 參數(shù)的值可以是任一類型的過濾器:字符串秦士、正則表達(dá)式、列表永高、Ture或是方法隧土。
markup = '''
<b>Hey, buddy. Want to buy a used parser?</b>
<p class="title">The Dormouse's story</p>
<a>Once upon a time there were three little sisters</a>
'''
soup = BeautifulSoup(markup,'lxml')
print(soup.find_all('p'))
# [<p class="title">The Dormouse's story</p>]
- keyword 參數(shù)
如果一個指定名字的參數(shù)不是搜索內(nèi)置的參數(shù)名稱,搜索時會把該參數(shù)當(dāng)作指定名字tag的屬性來搜索命爬。
markup = '''
<b id='link'>Hey, buddy. Want to buy a used parser?</b>
<p class="title">The Dormouse's story</p>
<a>Once upon a time there were three little sisters</a>
<a class="sister" id="link1">three</a>
<div data-foo="value">foo!</div>
'''
soup = BeautifulSoup(markup,'lxml')
print(soup.find_all(id='link'))
# [<b id="link">Hey, buddy. Want to buy a used parser?</b>]
# class是Python的保留關(guān)鍵字曹傀,這里是 class_
print(soup.find_all(class_='title'))
# [<p class="title">The Dormouse's story</p>]
# 可以使用多個指定名字的參數(shù)來過濾多個tag的屬性
import re
print(soup.find_all(id='link1', href=re.compile('example')))
# [<a class="sister" id="link1">three</a>]
- attrs 參數(shù)
有些tag屬性在搜索中不能使用,如HTML5中的 data-* 屬性遇骑,我們可以使用 attrs 參數(shù)定一個字典參數(shù)來搜索包含特殊屬性的tag卖毁。
print(soup.find_all(attrs={'data-foo':'value'}))
# [<div data-foo="value">foo!</div>]
- text參數(shù)
通過 text 參數(shù)可以搜索文檔中的字符串內(nèi)容揖曾,和 name 參數(shù)一樣落萎,接受參數(shù)有:字符串、正則表達(dá)式炭剪、列表练链、True。
print(soup.find_all(text='three'))
# ['three']
import re
print(soup.find_all(text=re.compile('^H.*?$')))
# ['Hey, buddy. Want to buy a used parser?']
- recursive 參數(shù)
Beautiful Soup 會檢索當(dāng)前tag的所有子孫節(jié)點奴拦,如果你只想搜索tag的直接子節(jié)點媒鼓,可以使用參數(shù) recursive = False。
markup = '''
<html>
<head>
<title>
The Dormouse's story
</title>
</head>
</html>
'''
soup = BeautifulSoup(markup, 'lxml')
print(soup.html.find_all('title'))
# [<title>
# The Dormouse's story
# </title>]
print(soup.html.find_all('title', recursive=False))
# []
- limit 參數(shù)
markup = '''
<b id='link'>Hey, buddy. Want to buy a used parser?</b>
<p class="title">The Dormouse's story</p>
<a>Once upon a time there were three little sisters</a>
<a class="sister" id="link1">three</a>
<div data-foo="value">foo!</div>
'''
soup = BeautifulSoup(markup,'lxml')
import re
for str in soup.find_all(text=re.compile('o')):
print(str)
# Hey, buddy. Want to buy a used parser?
# The Dormouse's story
# Once upon a time there were three little sisters
# foo!
for str in soup.find_all(text=re.compile('o'), limit=1):
print(str)
# Hey, buddy. Want to buy a used parser?
- 說明
find_all()
是 Beautiful Soup中最常用的搜索方法错妖,該庫還提供了它的簡寫方法绿鸣。BeautifulSoup 對象和 tag 對象都可以被當(dāng)作一個方法來使用,這個方法的執(zhí)行結(jié)果與調(diào)用這個對象的 find_all() 方法相同暂氯,下面代碼是等價的:
soup.find_all("a")
soup("a")
soup.title.find_all(text=True)
soup.title(text=True)
find()
find_all()
方法將返回文檔中符合條件的所有tag潮模,盡管有時候我們只想得到一個結(jié)果。比如文檔中只有一個<body>
標(biāo)簽痴施,那么使用 find_all()
方法來查找顯然不太合適擎厢,如果使用 limit=1 參數(shù)不如使用 find()
方法究流。
soup.find_all('title', limit=1)
# [<title>The Dormouse's story</title>]
soup.find('title')
# <title>The Dormouse's story</title>
唯一的區(qū)別是 find_all() 方法的返回結(jié)果是值包含一個元素的列表,而 find() 方法直接返回結(jié)果动遭。
find_all() 方法沒有找到目標(biāo)是返回空列表芬探,find() 方法找不到目標(biāo)時,返回 None 厘惦。
soup.head.title 是 tag的名字 方法的簡寫偷仿。這個簡寫的原理就是多次調(diào)用當(dāng)前tag的 find() 方法:
soup.head.title
# <title>The Dormouse's story</title>
soup.find("head").find("title")
# <title>The Dormouse's story</title>
輸出
prettify()
方法將Beautiful Soup的文檔樹格式化后以Unicode編碼輸出,每個XML/HTML標(biāo)簽都獨占一行绵估。
markup = '<a >I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup, 'lxml')
print(soup.prettify())
# <html>
# <body>
# <a >
# I linked to
# <i>
# example.com
# </i>
# </a>
# </body>
# </html>
Beautiful Soup 功能強大炎疆,除了上面提到的 搜索文檔樹 功能,更有遍歷文檔樹国裳,修改文檔樹等等功能形入。更多詳細(xì)介紹請查閱官方文檔 4.2.0。
作者:繁夢三千
鏈接:http://www.reibang.com/p/35b3616c74c1
來源:簡書