BeautifulSoup 美味的湯
學習一時爽,一直學習一直爽!
Hello,大家好,我是Connor,一個從無到有的技術小白。上一次我們說到了 Xpath
的使用方法。Xpath
我覺得還是比較繞該怎麼辦呢???有沒有更加簡單易懂的方法呢?答案是肯定的,當然有更加簡單易懂的方法了,那就是 BeautifulSoup
美味的湯。這個方法對於正則和 Xpath
來說更加的簡單方便,更加易懂,能夠節省我們大量的分析時間。
1.BeautifulSoup 的簡介
BeautifulSoup是一個HTML資料提取庫。幾乎沒有什麼資料可以難住BeautifulSoup。只要是你可以獲取的到的資料,那麼你都可以通過BeautifulSoup簡單快捷的進行資料提取。是一款非常適合新手入門使用的資料提取庫。當然,作為一個HTML資料提取庫,requests
都有了官方中文文件,那麼 BeautifulSoup
當然也不能少啊,你可以訪問 BeautifulSoup
的官方中文文件:點我走起 >>>
2.BeautifulSoup 的安裝
既然我又來說到安裝了,那就證明這個庫和我們平常想的庫不太一樣,它具體的安裝方法為:
pip install beautifulsoup4
複製程式碼
注意,是beautifulsoup4,並不是beautifulsoup,雖然我們beautifulsoup的叫,但人家實際叫beautifulsoup4,一定要記清楚哈。
3.BeautifulSoup 的基礎使用
安裝完了,下面我們就正式開始使用,老規矩,我們先來一段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>"""
複製程式碼
3.1 簡單的使用
在進行內容提取之前,我們需要將獲取的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
物件。
3.2 解析器的使用
在上面的語句中,大家可以看到我們使用了一個 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
來進行解析的。具體的視情況而定吧。
3.3 節點物件
BeautifulSoup將複雜的HTML文件轉換成了一個樹狀的結構,每個節點都是一個Python物件,所有的物件都可以歸納為四類:Tag
,NavigableString
,BeautifulSoup
,Commnet
。
3.3.1 Tag 物件
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
物件有哪些屬性:
- name屬性:
每一個tag標籤都有name屬性
In [4]: tag.name
Out[4]: 'p'
複製程式碼
- Attributes
在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()
通過 get_text()
方法我們可以獲取某個 Tag
下所有的文字內容:
In [8]: soup.a.get_text()
Out[8]: '百度一下,你就知道'
複製程式碼
3.3.2 NavigableString 物件
NavigableString
的意思是可以遍歷的字串,一般被標籤包裹在自種的文字就是NavigableString
格式:
In [9]: soup.p.string
Out[9]: '這是一個示例'
In [10]: type(soup.p.string)
Out[10]: bs4.element.NavigableString
複製程式碼
3.3.3 BeautifulSoup 物件
BeautifulSoup
物件就是通過解析網頁所得到的物件,我們的 soup
即是 BeautifulSoup
物件:
In [1]: from bs4 import BeautifulSoup
In [2]: soup = BeautifulSoup(html, "html5lib")
In [3]: type(soup)
Out[3]: bs4.BeautifulSoup
複製程式碼
3.3.4 Comment 物件
Comment
物件是網頁中的註釋及特殊字串,當你提取網頁中的註釋的時候,它會自動幫你生成Comment
物件:
In [4]: comment = soup.body.span.string
In [5]: type(comment)
Out[5]: bs4.element.Comment
複製程式碼
瞭解了 BeautifulSoup
的基礎使用之後,我們來看一下 BeautifulSoup
的進階用法:
4 BeautifulSoup 的高階用法
4.1 Tag與遍歷文件樹
Tag
物件可以說 BeautifulSoup
中最為重要的物件,通過 BeautifulSoup
來提取資料基本都圍繞著這個物件來進行操作。
首先,一個節點中是可以包含多個子節點和多個字串的。例如html
節點中包含著head
和body
節點。所以BeautifulSoup
就可以將一個HTML的網頁用這樣一層層巢狀的節點來進行表示。
使用我們的例子,你可以這樣做:
4.1.1 contents 和 children
通過 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>
複製程式碼
4.1.2 descendants
上面的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
標籤的第一個子節點,然後將子節點中的字串提取出來。提取出子節點的字串之後再提取子節點的子節點,再將其內容提取出來。直到該節點不再擁有子節點。這麼說可能有些抽象,我們來直接看圖:
4.1.3 string 和 strings
我們常常會遇到需要獲取某個節點中的文字值的情況,如果這個節點中只有一個字串,那麼使用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)
...:
百度一下,你就知道
這是一個示例
複製程式碼
4.1.4 父節點 parent 和 parents
有的時候我們也需要去獲取某個節點的父節點,就是當前節點的上一層節點:
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>
複製程式碼
同樣這種方式獲取的父輩元素也是一個可迭代物件,需要處理後才能使用
4.1.5 兄弟節點
兄弟節點就是指當前節點同級節點。
-
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] 複製程式碼
4.2. find_all()
在前面我們講了通過標籤的屬性來進行標籤的訪問的方法,大多都只適用於簡單的一些場景,所以 BeautifulSoup
還提供了搜尋整個文件樹的方法,即 find_all()
。該方法基本適用於任何節點:
4.2.1 通過 name 搜尋
最簡單的使用方式就是使用 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 標籤。
4.2.2 通過屬性搜尋
除了通過 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傳遞多個引數。
4.2.3 通過文字搜尋
在find_all()
方法中,還可以根據文字內容來進行搜尋。
In [36]: soup.find_all("a", text="百度一下,你就知道")
Out[36]: [<a href="https://www.baidu.com">百度一下,你就知道</a>]
複製程式碼
可見找到的都是字串物件,如果想要找到包含某個文字的tag
,加上tag
名即可。
4.2.4 限制查詢範圍為子節點
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>]
複製程式碼
4.2.5 通過正規表示式來篩選結果
在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>]
複製程式碼
4.3 CSS選擇器
在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 爬蟲十六式 - 第八式:例項解析-全書網 >>>