1. 程式人生 > >Python模組之BeautifulSoup4

Python模組之BeautifulSoup4

最近有個私人需求用到了爬蟲,雖然自己更擅長的是Java,.NET這樣的靜態語言;但在網上搜索到的爬蟲資料指向Python的居多,而且自從2013年用Python2.7寫了一年指令碼之後就甚少接觸Python,而現在Python的火熱有愈演愈烈之勢,遂決定使用Python3來完成此需求。

1. 概述

正如標題,本文不會涉及到如何抓取感興趣網頁的相關知識,所關注的重點是如何對所抓取到的網頁內容進行解析並抽取到感興趣的內容。本文將主要介紹BS4在簡化文件導航,查詢方面提供的便利方法。

2. 實戰

2.1 樣例文字

站在前人的肩膀上,這裡我們就直接複用官網上的例子;這裡為了便於對比,我所貼上的是經過格式化的,可讀性更強的版本(即經過soup.prettify()

處理過的)

<html>
 <head>
  <title>
   The Dormouse's story
  </title>
 </head>
 <body>
  <p class="title">
   <b>
    The Dormouse's story
   </b>
  </p>
  <p class="story">
   Once upon a time there were three little sisters; and their names were
   <
a
class="sister" href="http://example.com/elsie" id="link1">
Elsie </a> , <a class="sister" href="http://example.com/lacie" id="link2"> Lacie </a> and <a class="sister" href="http://example.com/tillie" id="link2"> Tillie </a> ; and they lived at the bottom of a well. </
p
>
<p class="story"> ... </p> </body> </html>
2.2 簡明程式碼

然後針對上述樣例文件,我們來看看常用的一些方法,記住我們的目標是快速入門——先會走,再考慮跑的問題(本來寫了不少註釋的,但後來又都給刪除了;相比較之下,與其讓程式碼被大量的註釋淹沒,還不如通過對比的方式去理解每個方法的用途)。

# ---------- BeautifulSoup
# 構建一個BeautifulSoup例項,這是我們進行文件導航,查詢指定節點的起點;
# 這裡的引數html_doc: 
#	1. 可以是一段字串,  例如 "<html>data</html>"
#	2. 也可以是檔案控制代碼, 即 open("xxxx.html")的返回值
soup = BeautifulSoup(html_doc, "html.parser")
# <class 'bs4.BeautifulSoup'>
print(type(soup)) 
# 生成可讀性更好的文件
print(soup.prettify()) 

print(soup.name)
# 本質是 soup.find("title")的簡寫形式, 還可以連讀, 例如soup.p.b
print(soup.title)

# ---------- Tag
pTag = soup.p
# 查詢所有的直接子節點, 區別是 contents 返回值是列表, 而children返回值則是生成器
print(pTag.contents)
print(pTag.children)
# 查詢所有的子孫節點
print(pTag.descendants)
# 獲取<p>節點中的 class屬性的值
print(pTag.get("class"))
# 獲取到tag中包含的所有文字內容, 包括子孫tag中的內容
print(pTag.get_text())

# 獲取某個元素的父節點
print(pTag.parent)
# 在同級節點之間導航
print(pTag.previous_sibling)
print(pTag.next_sibling)

# ---------- NavigableString
pTagStr = pTag.string
# 替換成其它的字串
pTagStr.replace_with("LQ")

# ---------- Comment
# Comment 物件是一個特殊型別的 NavigableString 物件

3. find系列方法

在BS4的使用過程中,我們需要對find系列有一個比較全面的瞭解,這樣將可以節省大量的時間。

find系列方法雖然看似有好幾個,但它們的方法引數都是一樣;因此只需要掌握其中一個,其它的也就順理成章了。

這裡我們就以最常用的 find_all( name , attrs , recursive , text , **kwargs ) 為例來進行講解:

  1. name引數,使用該引數可以查詢所有名字為 name 的Tag,Html文件中的字串物件會被自動忽略掉,並不會進入篩選候選名單。該引數值可以使用字串,正則表示式,列表,方法或是 True。
  2. **kwargs引數,使用該引數將把該引數當作指定名字tag的屬性來搜尋,例如soup.find_all(id='link2') 搜尋的是 包含id屬性, 且值為 link2 的Tag。引數值也是可以使用字串,正則表示式,列表,方法或是 True。
  3. attrs 引數,有些tag屬性在搜尋不能使用,比如HTML5中的 data-* 屬性,此時attrs 引數就能派上用場——attrs 引數定義一個字典引數來搜尋包含特殊屬性的tag。例如 soup.find_all(attrs={"data-foo": "value"})
  4. text 引數,通過該引數我們可以搜尋文件中的字串內容。這個特性在處理某些需求時會有奇效。和前面的一樣,text 引數接受字串,正則表示式,列表,方法或是 True。
  5. recursive引數,有時我們可能只是想要搜尋tag的直接子節點,此時可以使用引數 recursive=False,這樣既可以節省處理時間,又能節約處理記憶體。
  6. limit引數,該引數用於限制返回結果的數量,效果與SQL中的limit關鍵字類似,BS4在搜尋到的結果數量達到 limit 的限制時,就會停止搜尋返回結果。樣例如下:soup.find_all("a", limit=2)

討論完了這些方法引數之後,我們再來看下上面討論過程中一再出現的 " 字串,正則表示式,列表,方法或是 True",它們的術語其實是 過濾器(Filter),作用是充當進行篩選操作時的篩選條件;因為需求的千差萬別,所以BS4提供了由簡單到複雜的五種方式,下面就用示例分別來分別講解下:

# --------- name
soup.find_all("a") # 字串
# 找出所有以b開頭的標籤,這表示<body>和<b>標籤都應該被找到
soup.find_all(re.compile("^b") # 正則表示式

# ------- **kwargs引數
soup.find_all(href=re.compile("elsie"))
soup.find_all(id=True) #True,只要包含id屬性的都算上

# ------- attrs引數
data_soup.find_all(attrs={"data-foo": "value"})

# ------- text引數
soup.find_all(text=["Tillie", "Elsie", "Lacie"]) # 列表
soup.find_all(text=re.compile("Dormouse")) # 正則表示式  
soup.find_all("a", text="Elsie")  # 和其他引數混合使用
soup.find_all(text = lambda x: x == x.parent.string) # 方法

4. 總結

Beautiful Soup是一個可以從HTML或XML檔案中提取資料的Python庫,它能夠通過你喜歡的轉換器實現慣用的文件導航,查詢,修改文件的功能;對於新手而言,BS4是爬蟲入門的優先選擇項。