程式設計

Python 使用 Beautiful Soup 抓取與解析網頁資料,開發網路爬蟲教學

以 HTML 屬性搜尋

我們也可以根據網頁 HTML 元素的屬性來萃取指定的 HTML 節點,例如搜尋 id 屬性為 link2 的節點:

# 根據 id 搜尋
link2_tag = soup.find(id='link2')
print(link2_tag)
<a href="/my_link2" id="link2">Link 2</a>

我們可以結合 HTML 節點的名稱與屬性進行更精確的搜尋,例如搜尋 href 屬性為 /my_link1a 節點:

# 搜尋 href 屬性為 /my_link1 的 a 節點
a_tag = soup.find_all("a", href="/my_link1")
print(a_tag)
[<a href="/my_link1" id="link1">Link 1</a>]

搜尋屬性時,也可以使用正規表示法,例如以正規表示法比對超連結網址:

import re

# 以正規表示法比對超連結網址
links = soup.find_all(href=re.compile("^/my_linkd"))
print(links)
[<a href="/my_link1" id="link1">Link 1</a>, <a href="/my_link2" id="link2">Link 2</a>]

我們也可以同時使用多個屬性的條件進行篩選:

# 以多個屬性條件來篩選
link = soup.find_all(href=re.compile("^/my_linkd"), id="link1")
print(link)
[<a href="/my_link1" id="link1">Link 1</a>]

在 HTML5 中有一些屬性名稱若直接寫在 Python 的參數中會有一些問題,例如 data-* 這類的屬性直接寫的話,就會產生錯誤訊息:

data_soup = BeautifulSoup('<div data-foo="value">foo!</div>', 'html.parser')

# 錯誤的用法
data_soup.find_all(data-foo="value")
SyntaxError: keyword can't be an expression

遇到這種狀況,可以把屬性的名稱與值放進一個 dictionary 中,再將此 dictionary 指定給 attrs 參數即可:

# 正確的用法
data_soup.find_all(attrs={"data-foo": "value"})
[<div data-foo="value">foo!</div>]

以 CSS 搜尋

由於 class 是 Python 程式語言的保留字,所以 Beautiful Soup 改以 class_ 這個名稱代表 HTML 節點的 class 屬性,例如搜尋 class 為 boldtextb 節點:

# 搜尋 class 為 boldtext 的 b 節點
b_tag = soup.find_all("b", class_="boldtext")
print(b_tag)
[<b class="boldtext">Bold Text</b>]

CSS 的 class 屬性也可以使用正規表示法搜尋:

# 以正規表示法搜尋 class 屬性
b_tag = soup.find_all(class_=re.compile("^bold"))
print(b_tag)
[<b class="boldtext">Bold Text</b>]

一個 HTML 標籤元素可以同時有多個 CSS 的 class 屬性值,而我們在以 class_ 比對時,只要其中一個 class 符合就算比對成功,例如:

css_soup = BeautifulSoup('<p class="body strikeout"></p>', 'html.parser')

# 只要其中一個 class 符合就算比對成功
p_tag = css_soup.find_all("p", class_="strikeout")
print(p_tag)
[<p class="body strikeout"></p>]

我們也可以拿完整的 class 字串來進行比對:

# 比對完整的 class 字串
p_tag = css_soup.find_all("p", class_="body strikeout")
print(p_tag)
[<p class="body strikeout"></p>]

不過如果多個 class 名稱排列順序不同時,就會失敗:

# 若順序不同,則會失敗
p_tag = css_soup.find_all("p", class_="strikeout body")
print(p_tag)
[]

遇到多個 CSS class 的狀況,建議改用 CSS 選擇器來篩選:

# 使用 CSS 選擇器
p_tag = css_soup.select("p.strikeout.body")
print(p_tag)
[<p class="body strikeout"></p>]

以文字內容搜尋

若要依據文字內容來搜尋特定的節點,可以使用 find_all 配合 string 參數:

links_html = """
<a id="link1" href="/my_link1">Link One</a>
<a id="link2" href="/my_link2">Link Two</a>
<a id="link3" href="/my_link3">Link Three</a>
"""
soup = BeautifulSoup(links_html, 'html.parser')

# 搜尋文字為「Link One」的超連結
soup.find_all("a", string="Link One")
[<a href="/my_link1" id="link1">Link One</a>]

亦可使用正規表示法批配文字內容:

# 以正規表示法搜尋文字為「Link」開頭的超連結
soup.find_all("a", string=re.compile("^Link"))
[<a href="/my_link1" id="link1">Link One</a>, <a href="/my_link2" id="link2">Link Two</a>, <a href="/my_link3" id="link3">Link Three</a>]

向上、向前與向後搜尋

前面介紹的 find_all 都是向下搜尋子節點,如果需要向上搜尋父節點的話,可以改用 find_parents 函數(或是 find_parent),它可讓我們以某個特定節點為起始點,向上搜尋父節點:

html_doc = """
<body><p class="my_par">
<a id="link1" href="/my_link1">Link 1</a>
<a id="link2" href="/my_link2">Link 2</a>
<a id="link3" href="/my_link3">Link 3</a>
<a id="link3" href="/my_link4">Link 4</a>
</p></body>
"""
soup = BeautifulSoup(html_doc, 'html.parser')
link2_tag = soup.find(id="link2")

# 往上層尋找 p 節點
p_tag = link2_tag.find_parents("p")
print(p_tag)
[<p class="my_par">
<a href="/my_link1" id="link1">Link 1</a>
<a href="/my_link2" id="link2">Link 2</a>
<a href="/my_link3" id="link3">Link 3</a>
<a href="/my_link4" id="link3">Link 4</a>
</p>]

如果想要在在同一層往前尋找特定節點,則可用 find_previous_siblings 函數(或是 find_previous_sibling):

# 在同一層往前尋找 a 節點
link_tag = link2_tag.find_previous_siblings("a")
print(link_tag)
[<a href="/my_link1" id="link1">Link 1</a>]

如果想要在在同一層往後尋找特定節點,則可用 find_next_siblings 函數(或是 find_next_sibling):

# 在同一層往後尋找 a 節點
link_tag = link2_tag.find_next_siblings("a")
print(link_tag)
[<a href="/my_link3" id="link3">Link 3</a>, <a href="/my_link4" id="link3">Link 4</a>]

網頁檔案

如果我們想要用 Beautiful Soup 解析已經下載的 HTML 檔案,可以直接將開啟的檔案交給 BeautifulSoup 處理:

from bs4 import BeautifulSoup
# 從檔案讀取 HTML 程式碼進行解析
with open("index.html") as f:
    soup = BeautifulSoup(f)

以下我們提供了幾個實際以 Beautiful Soup 開發的網路爬蟲範例程式,請繼續閱讀下一頁。

Page: 1 2 3

G. T. Wang

個人使用 Linux 經驗長達十餘年,樂於分享各種自由軟體技術與實作文章。

Share
Published by
G. T. Wang
標籤: Python網路

Recent Posts

[DIY] 自製凡士林火種

這裡介紹如何利用簡單的凡士林與...

2 年 ago

[DIY] 自製火影木葉、砂忍者村標誌雕刻木牌

本篇記錄我用路邊撿來的樟木與龍...

2 年 ago

收集龍眼木修剪枝幹用於木頭工藝

最近打算帶著阿玄做一些木工作品...

2 年 ago

[DIY] 樟木手工自製迷你手裏劍(忍者武器)

本篇記錄阿玄第一次使用木工工具...

2 年 ago

[DIY] 龍眼木手工自製木槌

本篇記錄我用自己砍的龍眼木還有...

2 年 ago

[DIY] 樟木手工自製苦無(忍者武器)

本篇記錄我自己用樟木的枝幹,以...

2 年 ago