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 開發的網路爬蟲範例程式,請繼續閱讀下一頁。

程式設計

10 留言

  1. Aileen

    Thank you for the post. it helps me a lot!
    many thanks!!

  2. Martina

    很多地方都只是写prettify()可以把代码格式化。。。解释得一点都不通俗,还是不明白。你解释得清楚多了,一目了然!谢谢!

  3. ZHONG

    先感謝作者對這部分的用心
    內容相當充實,對新手來說也很好上手
    在試過google搜尋範例之後我有個問題
    如果我想要一次搜尋多一些資料該怎麼做
    目前所知是第1頁的’start’會是’0’,第2頁是’10’,以此類推

  4. Wilson

    感謝作者那麼用心的整理出這些步驟。
    想知道到如何設定Google 的設計抓取資料的量

  5. W.

    謝謝站長寫得真好 容易看懂重要的功能 還有很實用的範例

  6. XXL

    寫得太棒了!!!

  7. H

    範例列出得十分清晰易懂,十分感謝!

  8. 怎麼去提取數據從一個jQuery.get() 的網頁應用.
    查看網頁源代碼 找不到該網頁所展示的數據資料

  9. Jay

    寫的真的很好,棒!

  10. neoyang

    高手

Leave a Reply