Beautiful Soup 是一個可以從HTML或XML文件中提取數據的Python庫.
本文爲Beautiful Soup屬性方法總結,更多例子請查閱官方文檔
$ pip install beautifulsoup4
#debian或Ubuntu下可以
$ apt-get install Python-bs4
加載BeautifulSoup庫
>>> from bs4 import BeautifulSoup
>>> a = Beautiful('<html><head></head><body></body></html>') #創建BeautifulSoup對象
文檔解析器
Beautiful Soup會自動選擇一個解析器來解析文檔,也可以通過參數來制定使用哪種解析器
第一次使用BeautifulSoup構造函數如果沒有指定解析器,會出現提示,無需擔心,後續調用就不會出現該提示
BeartifulSoup第一個參數爲被解析的文檔字符串或文件句柄,第二個參數來表示選擇什麼解析器,
如果第二個參數爲空,則根據當前系統安裝的庫自動選擇解析器,lxml,html5lib,python標準庫
>>> BeautifulSoup(markup, "html.parser")
>>> BeautifulSoup(markup, "lxml")
>>> BeautifulSoup(markup, "lxml-xml") #使用lxml庫解析xml文檔
>>> BeautifulSoup(markup, "xml")
>>> BeautifulSoup(markup, "html5lib")
推薦使用lxml作爲解析器,速度快,容錯能力強,效率高
安裝lxml
$ pip install lxml
安裝html5lib
$ pip install html5lib
Beautiful Soup將複雜HTML文檔轉換成一個複雜的樹形結構,每個節點都是Python對象,所有對象可以歸納爲4種: Tag , NavigableString , BeautifulSoup , Comment .
BeautifulSoup類
BeautifulSoup對象表示一個文檔的全部內容,大部分時候,可以把它當作 Tag 對象,它支持遍歷文檔樹和搜索文檔樹中描述的大部分的方法.
它的name屬性返回字符串'[document]'
BeautifulSoup沒有標籤屬性,所以無法使用字典索引或attrs屬性
NavigableSoup類
字符串通常被包含在tag對象內,而這些字符串使用NavigableSoup類用來包裝
標籤與標籤之間的字符串同樣會被作爲文檔的節點,包裝爲NavigableSoup對象
NabigableSoup對象支持遍歷和搜索的大部分屬性方法 但是不能包含其他對象,所以不支持.contents,.strings或find()等針對子節點的方法
在tag中包含的NavigableSoup對象可以通過replace_with()方法來替換字符串
可以通過unicode()函數將NaviableSoup對象轉會成Unicode字符串,通常在BeautifulSoup之外使用時進行轉換
Comment類
Comment對象是一個特殊類型的NavigableString對象,包含文檔中的註釋
Beautiful Soup還定義了一些其他類型CData , ProcessingInstruction , Declaration , Doctype用於處理XML文檔,這些類也都是NavigableString類的子類
Tag對象
tag.name 獲取標籤名字,可以更改該屬性
tag[attr_name] 獲取該標籤的attr_name屬性,可以更改和添加attr_name屬性的值
tag.attrs 獲取該標籤的所有屬性,返回一個字典
如果屬性爲多值屬性,則返回一個列表,但是如果該屬性在html中沒有被定義爲多值屬性,則返回字符串,
對於xml,tag不包含多值屬性,返回的都是字符串
Tag對象屬性遍歷文檔樹
子節點
tag.tag_name 使用tag的名稱tag_name屬性來獲取相應第一個匹配的後代節點tag對象
>>> a = bs('<html id="df"><body id="df"> <div> <p>body元素後代節點第一個p</p> </div> <p>body子節點第一個</p></body></html>')
>>> a.body.p
<p>body元素後代節點第一個p</p>
tag.contents 將tag的子節點以列表的方式輸出
>>> a = bs('<html id="df"><body id="df"> <p>sdfd</p><div><p></p></div></body></html>')
>>> a.body.contents
['',<p>sdfd</p>, <div><p></p></div>]
注意空白符同樣會作爲子節點輸出tag.children 生成器屬性,可以對tag的子節點進行循環
tag.descendants 生成器屬性,可以對所有tag的後代節點進行遞歸循環
tag.string 如果tag只有一個NavigableString類型子節點,則string屬性可以得到子節點
如果tag僅有一個子節點,那麼輸出和該子節點的輸出一樣
如果tag包含多個子節點,tag就不確定.string方法由誰來調用
tag.strings 如果tag中包含多個字符串,可以使用.strings來循環獲取
tag.stripped_strings 輸出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多餘空白內容
段首和段末的空白會被刪除
父節點
tag.parent 獲取tag元素的父節點
tag.parents 生成器屬性,遞歸獲取所有父輩節點,最後返回None
兄弟節點
tag.next_sibling 查詢下一個同級節點,同級最後一個節點沒有該屬性
tag.previous_sibling 查詢上一個同級節點,同級第一個節點沒有該屬性
tag.netx_siblings 生成器屬性可以對當前節點的兄弟節點迭代輸出
tag.previous_siblings
實際兄弟節點也許並不是下一個標籤元素,而是文本節點NavigableString節點或者註釋Comment對象
HTML解析器會根據標籤的打開關閉和字符串來解析文檔
<html><head><title>The Dormouse's story</title></head>
<p class="title"><b>The Dormouse's story</b></p>
針對上面的字段HTML解析器會打開html標籤,打開head標籤,打開title標籤,添加字符串,關閉title標籤,關閉head標籤,打開p標籤...
tag.next_element 根據解析過程返回下一個被解析的對象(字符串或tag),結果可能於.next_sibling相同,但通常不一樣
next_element屬性會先返回後代節點,然後返回兄弟節點
>>> a = BeautifulSoup('<html id="df"><body id="df"><div><p>xixi</p></div><p>sdfd</p></body></html>')
>>> a.div.next_sibling
<p>sdfd</p>
>>> a.div.next_element
<p>xixi</p>
tag.previous_element 根據解析過程返回上一個被解析對象
tag.next_elements 生成器屬性
tag.previous_elements
Tag對象方法搜索文檔樹
搜索過濾器,用於搜索方法的參數來過濾搜索結果
字符串
正則表達式 如果傳入正則表達式作爲參數,Beautiful Soup會通過正則表達式的match()來匹配內容.
列表 將與列表中任一元素匹配的內容返回
True 可以匹配任何值
方法 定義一個方法,方法只接受一個元素參數,如果這個方法返回 True 表示當前元素匹配並且被找到,如果不是則反回 False
>>> def has_class(tag):
... return tag.has_attr('class')
...
>>> a = bs('<html id="df"><body id="df"><div><p class="1">xixi</p></div><p>sdfd</p></body></html>')
>>> a.find(has_class)
<p class="1">xixi</p>
tag.find_all(name, attrs, recursive, text ,limit, **kwargs)
搜索當前tag的所有tag子節點,並判斷是否符合過濾器的條件
name 參數可以查找所有名字爲name的tag,字符串對象會被自動忽略掉
text 參數用於搜索文檔中字符串內容,可以和其他參數混合使用來過濾
soup.find_all("a", text="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
recursive 參數設爲False,表示只搜索tag的直接子節點
**kwargs 關鍵字參數
如果指定的名字參數不是搜索內置的參數名,搜索時會把該參數作爲name參數的屬性,參數的值可以是任何類型的過濾器
對於標籤的class屬性,可以使用class_參數,因爲class爲Python的保留字
對於多值屬性可以使用字符串過濾器來全值完全匹配
>>> css_soup.find_all("p", class_="body strikeout")
attrs 參數定義一個字典參數作爲name參數的屬性搜索,可以搜索包含特殊屬性的tag
limit 參數會限制返回的數量,否則如果文檔很大的話搜索會很慢
tag.find (name, attrs, recursive, text , **kwargs)
等同於.find_all(...,limit=1),不過帶有limit=1參數的find_all()方法返回的值是一個包含一個元素的列表,而find()方法返回單個tag對象
當find_all()方法找不到目標時返回空列表,find()方法找不到目標,返回None
tag.find_parents(...)
tag.find_parent(...)
用來搜索當前節點的父輩節點,這兩個方法實際是對.parents屬性的迭代
tag.find_next_siblings(...)
tag.find_next_sibling(...)
通過.next_siblings屬性對當前tag之後的所有兄弟節點進行迭代
tag.find_previous_siblings(...)
tag.find_previous_sibling(...)
通過.previous_siblings屬性對當前tag之前的所有兄弟節點進行迭代
tag.find_all_next(...)
tag.find_next(...)
通過.next_elements屬性對當前tag之後的tag和字符串進行迭代
tag.find_all_previous(...)
tag.find_previous(...)
通過.previous_elements屬性對當前節點前面的tag和字符串進行迭代
CSS選擇器
.select()
傳入字符串參數可以使用CSS選擇器的語法找到對應tag
>>> soup.select("body a")
>>> soup.select("head > title")
>>> soup.select(".sister")
>>> soup.select("#link1")
>>> soup.select('a[href$="tillie"]')
解析部分文檔
如果僅僅因爲想要查找文檔中的<a>標籤而將整片文檔進行解析,實在是浪費內存和時間
可以使用SoupStrainer類定義某段內容,這樣搜索文檔時,就不必先解析全部文檔了
創建一個SoupStrainer對象並作爲parse_only參數傳遞給BeautifulSoup()構造方法
SoupStrainer類接受與搜索方法相同的參數
SoupStrainer(name, attrs,recursive,text,**kwargs)
>>> from bs4 import SoupStrainer
>>> only_a_tags = SoupStrainer("a")
>>> only_tags_with_id_link2 = SoupStrainer(id="link2")
>>> def is_short_string(string):
... return len(string) < 10
...
>>> only_short_strings = SoupStrainer(text=is_short_string)
>>> BeautifulSoup(html_doc, "html.parser", parse_only=only_a_tags)
修改文檔樹
tag.name 更改name屬性可以重命名tag
tag[attr_name] 添加刪除更改tag的屬性值
tag.string 給string屬性賦值相當於替代使用當前內容替代原來內容
beautifulsoup.new_string(string, class_name)
BeautifulSoup對象方法,創建一個NavigableString對象
可傳入NavigableString的任何子類作爲第二個參數,構建相應對象,比如Comment
beautifulsoup.new_tag(name, **kwargs)
BeautifulSoup對象方法,創建一個Tag對象,第一個參數爲標籤的名字,其他關鍵字參數爲標籤屬性
tag.append() 向tag中添加內容
tag.insert() 與列表的insert方法類似,在指定位置插入內容
>>> markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
>>> soup = BeautifulSoup(markup)
>>>soup.a.tag.insert(1, "but did not endorse ")
# <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a>
tag.insert_before() 在當前tag或文本節點前插入元素
tag.insert_after() 在當前tag或文本節點後插入元素
>>> soup = BeautifulSoup("<b>stop</b>")
>>> tag = soup.new_tag("i")
>>> tag.string = "Don't"
>>> soup.b.string.insert_before(tag)
>>> soup.b
<b><i>Don't</i>stop</b>
tag.clear() 移除當前tag中的內容
tag.extract() 將當前tag移除文檔樹,並作爲方法結果返回,返回的依然是Tag對象
tag.decompose() 將當前節點移除文檔樹並完全銷燬
tag.replace_with() 移除文檔樹中的某段內容,並用新tag或文本節點替代它
tag.wrap() 對指定的tag元素進行包裝,並返回包裝後的結果
>>> soup = BeautifulSoup("<p>I wish I was bold.</p>")
>>> soup.p.string.wrap(soup.new_tag("b"))
<b>I wish I was bold.</b>
tag.unwrap() 移除tag的當前tag.name標籤,該方法常被用來進行標記的解包
>>> bs = BeautifulSoup('<a href="http://example.com/">I linked to <i>example.com<i></i></i></a>')
>>> bs.i.unwrap()
<i></i>
>>> bs
<html><body><a href="http://example.com/">I linked to example.com<i></i></a></body></html>
輸出
tag.prettify() 將文檔樹格式化後以Unicode編碼輸出,每個XML/HTML標籤都獨佔一行
>>> a = Beautiful('<html id="df">\n<body id="df"><div><p class="1">xixi</p></div><p>sdfd</p></body>\n</html>')
>>> a.prettify()
'<html id="df">\n <body id="df">\n <div>\n <p class="1">\n xixi\n </p>\n </div>\n <p>\n sdfd\n </p>\n </body>\n</html>'
如果只想得到結果字符串,不重視格式,那麼可以對一個 BeautifulSoup 對象或 Tag 對象使用Python的 unicode() 或 str() 函數
>>> a
<html id="df">
<body id="df"><div><p class="1">xixi</p></div><p>sdfd</p></body>
</html>
tag .get_text() 獲取到tag中包含的所有文本內容包括子孫tag中的內容,並將結果作爲Unicode字符串返回
>>> a = bs('<html id="df"><body id="df"><div><p class="1">\nxixi</p></div><p>\nsdfd</p></body></html>')
>>> a.get_text()
'\nxixi\nsdfd'
>>> a.get_text('|') #使用參數來指定文本之間的分隔符
'\nxixi|\nsdfd'
>>> a.get_text('|',strip=True) #使用strip參數去除文本內容中的前後空白符
'xixi|sdfd'
編碼
任何HTML/XML文檔都有自己的編碼方式,但是使用Beautiful Soup解析後文檔轉換爲Unicode編碼
.orginal_encoding屬性記錄了自動識別的編碼
BeautifulSoup構造方法時可傳入from_encoding參數指定編碼方式
soup = BeautifulSoup(markup, from_encoding="iso-8859-8")
#####未完待續####