這裡介紹如何使用 Python 的 Beautiful Soup 模組自動下載並解析網頁資料,開發典型的網路爬蟲程式。

Beautiful Soup 是一個 Python 的函式庫模組,可以讓開發者僅須撰寫非常少量的程式碼,就可以快速解析網頁 HTML 碼,從中翠取出使用者有興趣的資料、去蕪存菁,降低網路爬蟲程式的開發門檻、加快程式撰寫速度。


Beautiful Soup 這套模組的網頁結構搜尋與萃取功能相當完整,這裡我們只介紹比較常用的幾種功能,更詳細的用法請參考 Beautiful Soup 官方的說明文件

安裝 Beautiful Soup

Beautiful Soup 可以使用 pip 安裝:

# 安裝 Python 2 的 Beautiful Soup 4 模組
pip install beautifulsoup4

# 安裝 Python 3 的 Beautiful Soup 4 模組
pip3 install beautifulsoup4

在 Ubuntu Linux 中亦可使用 apt 安裝:

# 安裝 Python 2 的 Beautiful Soup 4 模組
sudo apt-get install python-bs4

# 安裝 Python 3 的 Beautiful Soup 4 模組
sudo apt-get install python3-bs4

Beautiful Soup 基本用法

Beautiful Soup 的運作方式就是讀取 HTML 原始碼,自動進行解析並產生一個 BeautifulSoup 物件,此物件中包含了整個 HTML 文件的結構樹,有了這個結構樹之後,就可以輕鬆找出任何有興趣的資料了。

以下是一個簡單的小程式,示範如何使用 Beautiful Soup 模組解析原始的 HTML 程式碼:

# 引入 Beautiful Soup 模組
from bs4 import BeautifulSoup

# 原始 HTML 程式碼
html_doc = """
<html><head><title>Hello World</title></head>
<body><h2>Test Header</h2>
<p>This is a test.</p>
<a id="link1" href="/my_link1">Link 1</a>
<a id="link2" href="/my_link2">Link 2</a>
<p>Hello, <b class="boldtext">Bold Text</b></p>
</body></html>
"""

# 以 Beautiful Soup 解析 HTML 程式碼
soup = BeautifulSoup(html_doc, 'html.parser')

這裡的 soup 就是解析完成後,所產生的結構樹物件,接下來所有資料的搜尋、萃取等操作都會透過這個物件來進行。

首先我們可以將完整個 HTML 結構經過排版後輸出,觀察整份文件的輪廓:

# 輸出排版後的 HTML 程式碼
print(soup.prettify())
<html>
 <head>
  <title>
   Hello World
  </title>
 </head>
 <body>
  <h2>
   Test Header
  </h2>
  <p>
   This is a test.
  </p>
  <a href="/my_link1" id="link1">
   Link 1
  </a>
  <a href="/my_link2" id="link2">
   Link 2
  </a>
  <p>
   Hello,
   <b class="boldtext">
    Bold Text
   </b>
  </p>
 </body>
</html>

取得節點文字內容

若要輸出網頁標題的 HTML 標籤,可以直接指定網頁標題標籤的名稱(title),即可將該標籤的節點抓出來:

# 網頁標題 HTML 標籤
title_tag = soup.title
print(title_tag)
<title>Hello World</title>

HTML 標籤節點的文字內容,可以透過 string 屬性存取:

# 網頁的標題文字
print(title_tag.string)
Hello World

搜尋節點

我們可以使用 find_all 找出所有特定的 HTML 標籤節點,再以 Python 的迴圈來依序輸出每個超連結的文字:

# 所有的超連結
a_tags = soup.find_all('a')
for tag in a_tags:
  # 輸出超連結的文字
  print(tag.string)
Link 1
Link 2

取出節點屬性

若要取出 HTML 節點的各種屬性,可以使用 get,例如輸出每個超連結的網址(href 屬性):

for tag in a_tags:
  # 輸出超連結網址
  print(tag.get('href'))
/my_link1
/my_link2

同時搜尋多種標籤

若要同時搜尋多種 HTML 標籤,可以使用 list 來指定所有的要列出的 HTML 標籤名稱:

# 搜尋所有超連結與粗體字
tags = soup.find_all(["a", "b"])
print(tags)
[<a href="/my_link1" id="link1">Link 1</a>, <a href="/my_link2" id="link2">Link 2</a>, <b class="boldtext">Bold Text</b>]

限制搜尋節點數量

find_all 預設會輸出所有符合條件的節點,但若是遇到節點數量很多的時候,就會需要比較久的計算時間,如果我們不需要所有符合條件的節點,可以用 limit 參數指定搜尋節點數量的上限值,這樣它就只會找出前幾個符合條件的節點:

# 限制搜尋結果數量
tags = soup.find_all(["a", "b"], limit=2)
print(tags)
[<a href="/my_link1" id="link1">Link 1</a>, <a href="/my_link2" id="link2">Link 2</a>]

如果只需要抓出第一個符合條件的節點,可以直接使用 find

# 只抓出第一個符合條件的節點
a_tag = soup.find("a")
print(a_tag)
<a href="/my_link1" id="link1">Link 1</a>

遞迴搜尋

預設的狀況下,find_all 會以遞迴的方式尋找所有的子節點:

# 預設會以遞迴搜尋
soup.html.find_all("title")
[<title>Hello World</title>]

如果想要限制 find_all 只找尋次一層的子節點,可以加上 recursive=False 關閉遞迴搜尋功能:

# 不使用遞迴搜尋,僅尋找次一層的子節點
soup.html.find_all("title", recursive=False)
[]

接下來我們要介紹一些更詳細的使用方式,請繼續閱讀下一頁。