學習一時爽,一直學習一直爽!html
Hello,你們好,我是Connor,一個從無到有的技術小白。上一次咱們說到了 Xpath
的使用方法。Xpath
我以爲仍是比較繞該怎麼辦呢???有沒有更加簡單易懂的方法呢?答案是確定的,固然有更加簡單易懂的方法了,那就是 BeautifulSoup
美味的湯。這個方法對於正則和 Xpath
來講更加的簡單方便,更加易懂,可以節省咱們大量的分析時間。前端
BeautifulSoup是一個HTML數據提取庫。幾乎沒有什麼數據能夠難住BeautifulSoup。只要是你能夠獲取的到的數據,那麼你均可以經過BeautifulSoup簡單快捷的進行數據提取。是一款很是適合新手入門使用的數據提取庫。固然,做爲一個HTML數據提取庫,requests
都有了官方中文文檔,那麼 BeautifulSoup
固然也不能少啊,你能夠訪問 BeautifulSoup
的官方中文文檔:點我走起 >>>html5
既然我又來講到安裝了,那就證實這個庫和咱們日常想的庫不太同樣,它具體的安裝方法爲:python
pip install beautifulsoup4
複製代碼
注意,是beautifulsoup4,並非beautifulsoup,雖然咱們beautifulsoup的叫,但人家實際叫beautifulsoup4,必定要記清楚哈。ios
安裝完了,下面咱們就正式開始使用,老規矩,咱們先來一段html文檔,而後逐一舉例,來看BeautifulSoup如何使用:正則表達式
首先咱們來隨意編寫一段html代碼:瀏覽器
html = """
<html>
<head>
<title>Hello,Wrold</title>
</head>
<body>
<div class="book">
<span><!--這裏是註釋的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg" />
<p class="abc">這是一個示例</p>
</div>
</body>
</html>"""
複製代碼
在進行內容提取以前,咱們須要將獲取的html內容轉換成BeautifulSoup對象:工具
In [1]: from bs4 import BeautifulSoup
In [2]: soup = BeautifulSoup(html, "html5lib")
In [3]: type(soup)
Out[3]: bs4.BeautifulSoup
複製代碼
能夠看的到,咱們生成的對象是一個 bs4.BeautfulSoup
對象,咱們全部的內容提取都基於這個對象。切記進行內容提取以前先生成 bs4.BeautifulSoup
對象。post
在上面的語句中,你們能夠看到咱們使用了一個 html5lib
這是一個解析器,在構造BeautifulSoup
對象的時候,須要用到解析器。BeautifulSoup
支持python自帶的解析器和少數第三方解析器。詳細對好比下:性能
解析器 | 使用方法 | 優點 | 劣勢 |
---|---|---|---|
Python標準庫 | BeautifulSoup(html,"html.parser") |
Python的內置標準庫。 執行速度適中。 文檔容錯能力強。 | Python 3.2.2前的版本文檔容錯能力差 |
lxml HTML 解析器 | BeautifulSoup(html, "lxml") |
速度快文檔容錯能力強 | 須要安裝C語言庫 |
lxml XML 解析器 | BeautifulSoup(html, ["lxml","xml"]) BeautifulSoup(html, "xml") |
速度快 惟一支持XML的解析器 | 須要安裝C語言庫 |
html5lib | BeautifulSoup(markup,"html5lib") |
最好的容錯性 以瀏覽器的方式解析文檔生成HTML5格式的文檔 | 速度慢但不依賴外部擴展 |
通常來講,對於速度或性能要求不過高的話,仍是建議你們使用 html5lib
來進行解析的,可是當規模達到必定程度的時候,解析速度就會影響到總體項目的快慢了,因此若是你對性能有要求的話,仍是推薦使用 lxml
來進行解析的。具體的視狀況而定吧。
BeautifulSoup將複雜的HTML文檔轉換成了一個樹狀的結構,每一個節點都是一個Python對象,全部的對象均可以概括爲四類:Tag
,NavigableString
,BeautifulSoup
,Commnet
。
Tag
就是咱們平時所說的標籤,Tag下擁有許多屬性和方法,和前端相似,例如 a 標籤必定會有它的href屬性,某些屬性是某些標籤所獨有的。下面咱們來看一下如何提取一個 Tag
對象:
In [1]: soup = BeautifulSoup(html)
In [2]: tag = soup.p
In [3]: type(tag)
Out[3]: bs4.element.Tag
複製代碼
能夠看的到,咱們生成了一個 Tag
對象,咱們再來看看 Tag
對象有哪些屬性:
每個tag標籤都有name屬性
In [4]: tag.name
Out[4]: 'p'
複製代碼
在html中,某個 Tag
可能有多個屬性, Tag
屬性使用和字典同樣的方法取值:
In [5]: tag["class"]
Out[5]: ['abc']
複製代碼
若是某個 Tag
屬性有多個值,那麼返回的是一個列表:
In [6]: soup = BeautifulSoup('<p class="body strikeout"></p>')
In [7]: soup.p['class']
Out[7]: ['body', 'strikeout']
複製代碼
經過 get_text()
方法咱們能夠獲取某個 Tag
下全部的文本內容:
In [8]: soup.a.get_text()
Out[8]: '百度一下,你就知道'
複製代碼
NavigableString
的意思是能夠遍歷的字符串,通常被標籤包裹在自種的文本就是NavigableString
格式:
In [9]: soup.p.string
Out[9]: '這是一個示例'
In [10]: type(soup.p.string)
Out[10]: bs4.element.NavigableString
複製代碼
BeautifulSoup
對象就是經過解析網頁所獲得的對象,咱們的 soup
便是 BeautifulSoup
對象:
In [1]: from bs4 import BeautifulSoup
In [2]: soup = BeautifulSoup(html, "html5lib")
In [3]: type(soup)
Out[3]: bs4.BeautifulSoup
複製代碼
Comment
對象是網頁中的註釋及特殊字符串,當你提取網頁中的註釋的時候,它會自動幫你生成Comment
對象:
In [4]: comment = soup.body.span.string
In [5]: type(comment)
Out[5]: bs4.element.Comment
複製代碼
瞭解了 BeautifulSoup
的基礎使用以後,咱們來看一下 BeautifulSoup
的進階用法:
Tag
對象能夠說 BeautifulSoup
中最爲重要的對象,經過 BeautifulSoup
來提取數據基本都圍繞着這個對象來進行操做。
首先,一個節點中是能夠包含多個子節點和多個字符串的。例如html
節點中包含着head
和body
節點。因此BeautifulSoup
就能夠將一個HTML的網頁用這樣一層層嵌套的節點來進行表示。
使用咱們的例子,你能夠這樣作:
經過 contents
能夠獲取某個節點的全部子節點,包括裏面的 NavigbleString
對象,獲取的子節點是列表格式:
In [5]: soup.head.contents
Out[5]: [<title>Hello,Wrold</title>]
複製代碼
經過 children
也能夠獲取某個節點的全部子節點,可是返回的是一個迭代器,這種方式使用起來比列表更加的省內存:
In [6]: tags = soup.head.children
In [7]: print(tags)
<list_iterator object at 0x000002E5B44E6860>
In [8]: for tag in tags:
...: print(tag)
...:
<title>Hello,Wrold</title>
複製代碼
上面的contents
和children
獲取的是某個節點的直接子節點,而沒法得到子孫節點。經過descendants
能夠得到全部子孫節點,返回的結果跟children
同樣,須要迭代或者轉類型使用。
In [15]: tags = soup.body.descendants
In [16]: for tag in tags:
...: print(tag)
...:
<div class="book">
<span><!--這裏是註釋的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">這是一個示例</p>
</div>
<span><!--這裏是註釋的部分--></span>
這裏是註釋的部分
<a href="https://www.baidu.com">百度一下,你就知道</a>
百度一下,你就知道
<img src="https://abc.jpg"/>
<p class="abc">這是一個示例</p>
這是一個示例
複製代碼
經過上圖咱們能夠看得出經過 descendants
首先找出了 body
標籤的第一個子節點,而後將子節點中的字符串提取出來。提取出子節點的字符串以後再提取子節點的子節點,再將其內容提取出來。直到該節點再也不擁有子節點。這麼說可能有些抽象,咱們來直接看圖:
咱們經常會遇到須要獲取某個節點中的文本值的狀況,若是這個節點中只有一個字符串,那麼使用string
能夠正常將其取出。
In [17]: soup.body.a.string
Out[18]: '百度一下,你就知道'
複製代碼
可是若是一個節點下有多個節點中包含有字符串的時候,這時使用 string
方法就沒法準確的取出字符串了,它沒法肯定你要取出的是哪一個字符串,這時你須要使用 strings
:
In [19]: strings = soup.body.strings
In [20]: strings
Out[20]: <generator object _all_strings at 0x000002E5B44EA1A8>
In [21]: for string in strings:
...: print(string)
...:
百度一下,你就知道
這是一個示例
複製代碼
使用 strings
也會給你返回一個可迭代對象。固然,你會發現裏面有不少的'\n','\t'啊等這樣的轉義字符,上面的程序中沒有是由於爲了美觀我手動去掉了。若是你想要獲取的內容中沒有轉義字符的話,你可使用 stripped_strings
來去掉內容中的空白:
In [22]: strings = soup.body.stripped_strings
In [23]: strings
Out[23]: <generator object stripped_strings at 0x000002E5B39DF3B8>
In [24]: for string in strings:
...: print(string)
...:
百度一下,你就知道
這是一個示例
複製代碼
有的時候咱們也須要去獲取某個節點的父節點,就是當前節點的上一層節點:
In [25]: soup.a.parent
Out[25]:
<div class="book">
<span><!--這裏是註釋的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">這是一個示例</p>
</div>
複製代碼
若是使用 parents
的話將會遞歸獲取該節點的全部父輩元素:
In [26]: soup.a.parents
Out[26]: <generator object parents at 0x000002E5B38C3150>
複製代碼
一樣這種方式獲取的父輩元素也是一個可迭代對象,須要處理後才能使用
兄弟節點就是指當前節點同級節點。
next_sibling 和 previous_sibling
兄弟節點選取的方法與當前節點的位置有關,next_sibling
獲取的是當前節點的下一個兄弟節點,previous_sibling
獲取的是當前節點的上一個兄弟節點。
因此,兄弟節點中排第一個的節點是沒有previous_sibling
的,最後一個節點是沒有next_sibling
的。
In [27]: soup.head.next_sibling
Out[27]: '\n'
In [28]: soup.head.previos_sibling
In [29]: soup.body.previous_sibling
Out[29]: '\n'
複製代碼
next_siblings 和 previous_siblings
相對應的,next_siblings
獲取的是下方全部的兄弟節點,previous_siblings
獲取的上方全部的兄弟節點。
In [30]: [i.name for i in soup.head.next_siblings]
Out[30]: [None, 'body', None]
In [31]: [i.name for i in soup.body.next_siblings]
Out[31]: [None]
In [32]: [i.name for i in soup.body.previous_siblings]
Out[32]: [None, 'head', None]
複製代碼
在前面咱們講了經過標籤的屬性來進行標籤的訪問的方法,大多都只適用於簡單的一些場景,因此 BeautifulSoup
還提供了搜索整個文檔樹的方法,即 find_all()
。該方法基本適用於任何節點:
最簡單的使用方式就是使用 name 屬性進行搜索,你能夠這樣作:
In [33]: soup.find_all('a')
Out[33]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]
複製代碼
經過 find_all()
方法獲取的內容是一個列表對象。若是你給的條件是一個列表,則會匹配列表裏的所有標籤,例如:
In [34]: soup.find_all(['a','p'])
Out[34]: [<a href="https://www.baidu.com">百度一下,你就知道</a>, <p class="abc">這是一個示例</p>]
複製代碼
經過上面的例子咱們能夠看得出,咱們能夠看獲得, BeautifulSoup
對象匹配出了全部的 a標籤和 p 標籤。
除了經過 name 屬性來進行匹配以外,咱們還能夠經過屬性進行匹配。這個時候咱們須要向 find_all()
方法傳遞一個字典參數:
In [35]: soup.find_all(attrs={'class':'book'})
Out[35]:
[<div class="book">
<span><!--這裏是註釋的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">這是一個示例</p>
</div>]
複製代碼
若是一個標籤有多個參數,爲了查找的準確性,你也能夠向attrs傳遞多個參數。
在find_all()
方法中,還能夠根據文本內容來進行搜索。
In [36]: soup.find_all("a", text="百度一下,你就知道")
Out[36]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]
複製代碼
可見找到的都是字符串對象,若是想要找到包含某個文本的tag
,加上tag
名便可。
find_all()
方法會默認的去全部的子孫節點中搜索,而若是將 recursive
參數設置爲False,則能夠將搜索範圍限制在直接子節點中:
In [37]: soup.find_all("a",recursive=False)
Out[37]: []
In [38]: soup.find_all("a",recursive=True)
Out[38]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]
複製代碼
在BeautifulSoup
中,也是能夠與re
模塊進行相互配合的,將re.compile編譯的對象傳入find_all()
方法,便可經過正則來進行搜索。
In [39]: import re
In [40]: soup.find_all(re.compile("b"))
Out[40]:
[<body>
<div class="book">
<span><!--這裏是註釋的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">這是一個示例</p>
</div>
</body>]
複製代碼
能夠看到,經過正則表達式,咱們找到了全部以 b 開頭的標籤。正則怎麼用咱們會在以後的文章中詳細的說的,你們不用着急。固然,正則除了能用在標籤上,也能夠用在屬性上:
In [57]: soup.find_all(attrs={"class":re.compile("a")})
Out[57]: [<p class="abc">這是一個示例</p>]
複製代碼
在BeautifulSoup
中,一樣也支持使用CSS選擇器來進行搜索。使用select()
,在其中傳入字符串參數,就可使用CSS選擇器的語法來找到tag:
In [58]: soup.select("title")
Out[58]: [<title>Hello,Wrold</title>]
In [60]: soup.select(".book")
Out[60]:
[<div class="book">
<span><!--這裏是註釋的部分--></span>
<a href="https://www.baidu.com">百度一下,你就知道</a>
<img src="https://abc.jpg"/>
<p class="abc">這是一個示例</p>
</div>]
複製代碼
Xpath 和 BueatifulSoup 都很好用,可是有時候遇到複雜的選擇也很麻煩,有沒有能像 JQuery同樣快速經過 CSS 來選擇的工具啊???固然有了!!!那就是咱們的 PyQuery,一個和 JQuery 像兄弟同樣的庫,敬請期待下期——PyQuery,一個相似JQuery的庫。
好了,這就是今天最美味的湯了,不知道你喝了之後有什麼感覺,我是Connor,一個從無到有的技術小白,但願你能和我一同進步,一同成長!咱們下期再見!
學習一時爽,一直學習一直爽!
Python 爬蟲十六式 - 第一式:HTTP協議 >>>
Python 爬蟲十六式 - 第二式:urllib 與 urllib3 >>>
Python 爬蟲十六式 - 第三式:Requests的用法 >>>
Python 爬蟲十六式 - 第四式: 使用Xpath提取網頁內容 >>>
Python 爬蟲十六式 - 第六式:JQuery的假兄弟-pyquery >>>
Python 爬蟲十六式 - 第七式:正則的藝術 >>>
Python 爬蟲十六式 - 第八式:實例解析-全書網 >>>