【Python 網路爬蟲筆記】BeautifulSoup Library - part 3 感謝你點進本篇文章!!我是 LukeTseng,一個熱愛資訊的無名創作者,由於近期大學開設大數據分析程式設計這門課程,裡面談到了爬蟲概念,讓我激起一些興趣,因而製作本系列筆記。
聲明:本篇筆記僅供個人學習用途,斟酌參考。
另外本篇筆記使用 VSCode 環境進行編寫,部分模組(函式庫)需自行下載。
安裝 BeautifulSoup 模組 若使用 google colab 或 anaconda 環境者無須安裝。
指令:
1 pip install beautifulsoup4
引入 BeautifulSoup 模組 1 from bs4 import BeautifulSoup
為什麼我們要用 BeautifulSoup? BeautifulSoup 的主要用途是解析 HTML 和 XML,將網頁內容轉換成結構化的樹狀格式供程式操作 。
網頁資料解析與擷取是 BeautifulSoup 最主要的用途。
在網路爬蟲的世界,無可或缺的除了 request 模組外,就是 BeautifulSoup,有了這個模組就可以進一步擷取、分析我們想要的資訊。
例如可以擷取個人部落格所有文章的總瀏覽量,可以做到的方式就是透過 sitemap 一個一個進文章,去抓取每個文章的瀏覽量資訊,最後加總起來。
第一支 BeautifulSoup 程式 以我的部落格網站為例:https://luketsengtw.github.io/
1 2 3 4 5 6 7 import requestsfrom bs4 import BeautifulSoupurl = 'https://luketsengtw.github.io/' html = requests.get(url) soup = BeautifulSoup(html.text, 'html.parser' ) print (soup.title)
Output:
1 <title>Yaoの程式小窩 - 只想好好學程式</title>
如果想要去掉 <title></title> 標籤的話,可以加上 .string 方法。
1 2 3 4 5 6 7 import requestsfrom bs4 import BeautifulSoupurl = 'https://luketsengtw.github.io/' html = requests.get(url) soup = BeautifulSoup(html.text, 'html.parser' ) print (soup.title.string)
Output:
解析器(Parser) 解析器是 BeautifulSoup 第二個參數,用於將 html 原始碼轉換成標籤樹好讓程式去做一些操作。
Python 內建的網頁解析器是 html.parser,如果要使用其它解析器需要額外安裝。
常見的解析器就有 lxml 跟 html5lib。
要安裝它們的話可以輸入指令:pip install lxml html5lib
以下表格可以幫各位快速閱覽這些解析器的能力:
解析器 速度 準確性 容錯能力 html.parser 中 最差 最差 lxml 最快 高 高 html5lib 最慢 最高 最高
通常會使用 lxml 作為解析器,若在學習階段,不想裝這些有的沒的話,用後續用 html.parser 就可以了。
BeautifulSoup 常用方法 搜尋方法 find()、find_all() 是在 BeautifulSoup 中使用頻率最高的方法,因此先特別介紹這個。
基本上他的功能就是尋找這樣,find() 若有多個標籤存在的話,只會找第一個。
以下是一個範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from bs4 import BeautifulSouphtml = """ <html> <body> <div class="container"> <h2>標題</h2> <p class="content">第一段內容</p> <p class="content">第二段內容</p> <a href="https://example.com">連結一</a> <a href="https://google.com">連結二</a> </div> </body> </html> """ soup = BeautifulSoup(html, 'lxml' ) first_p = soup.find('p' , class_='content' ) print (first_p.text)all_p = soup.find_all('p' , class_='content' ) for p in all_p: print (p.text) links = soup.find_all('a' , href=True ) for link in links: print (f"連結文字: {link.text} , 網址: {link['href' ]} " )
Output:
1 2 3 4 5 第一段內容 第一段內容 第二段內容 連結文字: 連結一, 網址: https://example.com 連結文字: 連結二, 網址: https://google.com
CSS Selector 使用 select()。
以下是測試資料:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 from bs4 import BeautifulSouphtml_content = """ <!DOCTYPE html> <html> <head> <title>CSS 選擇器練習範例</title> </head> <body> <header id="main-header" class="site-header"> <h1 class="title">網站標題</h1> <nav class="navigation"> <a href="/home" class="nav-link active">首頁</a> <a href="/about" class="nav-link">關於我們</a> <a href="/contact" class="nav-link">聯絡我們</a> </nav> </header> <main class="container"> <article id="article-1" class="post featured"> <h2 class="post-title">第一篇文章</h2> <p class="post-content">這是第一篇文章的內容。</p> <div class="meta"> <span class="author" data-name="A">作者:A</span> <span class="date" data-published="2025-01-01">日期:2025-01-01</span> </div> </article> <article id="article-2" class="post"> <h2 class="post-title">第二篇文章</h2> <p class="post-content highlight">這是第二篇文章的重點內容。</p> <div class="meta"> <span class="author" data-name="B">作者:B</span> <span class="date" data-published="2025-01-02">日期:2025-01-02</span> </div> </article> <aside class="sidebar"> <div class="widget recent-posts"> <h3>最新文章</h3> <ul> <li><a href="/post1">文章一</a></li> <li><a href="/post2">文章二</a></li> <li><a href="/post3">文章三</a></li> </ul> </div> </aside> </main> <footer id="main-footer" class="site-footer"> <p>© 2025 練習網站</p> </footer> </body> </html> """ soup = BeautifulSoup(html_content, 'lxml' )
標籤選擇器 :以下範例可選取 HTML 中所有的 h2 標籤及 p 段落標籤。
註:使用選擇器回傳的物件會是一個 list,所以以下的 for h2 in h2_tags: 會迭代 list 物件內的元素。
1 2 3 4 5 6 7 8 9 h2_tags = soup.select('h2' ) print ("所有 h2 標籤:" )for h2 in h2_tags: print (f" {h2.text} " ) paragraphs = soup.select('p' ) print (f"\n找到 {len (paragraphs)} 個段落" )
Output:
1 2 3 4 5 所有 h2 標籤: 第一篇文章 第二篇文章 找到 3 個段落
ID 選擇器 :<header id="main-header" class="site-header"> 做舉例,可以選擇 <header> 標籤裡面的 id。
要選取特定 ID,則使用一個 # 井字號作為前綴。
1 2 3 4 5 6 7 header = soup.select('#main-header' ) print (f"\n標頭內容: {header[0 ].find('h1' ).text} " )article1 = soup.select('#article-1' ) print (f"第一篇文章標題: {article1[0 ].find('h2' ).text} " )
Output:
1 2 標頭內容: 網站標題 第一篇文章標題: 第一篇文章
Class 選擇器 :顧名思義,選擇 class 的值。
那 class 選擇器與 ID 選擇器不一樣,class 選擇器使用 . 一個半形點作為前綴。
註:以下程式碼的 .get('href') 是 BeautifulSoup 的方法,用於取得某個標籤的屬性值。
要取得屬性值也可直接寫 link['href'],與使用方法的差別在於這種方式比較不安全(會直接報錯),而 .get() 找不到的話會直接回傳 None。
1 2 3 4 5 6 7 8 9 10 11 12 posts = soup.select('.post' ) print (f"\n找到 {len (posts)} 篇文章:" )for post in posts: title = post.find('h2' ).text print (f" {title} " ) nav_links = soup.select('.nav-link' ) print (f"\n找到 {len (nav_links)} 個導航連結:" )for link in nav_links: print (f" {link.text} -> {link.get('href' )} " )
Output:
1 2 3 4 5 6 7 8 找到 2 篇文章: 第一篇文章 第二篇文章 找到 3 個導航連結: 首頁 -> /home 關於我們 -> /about 聯絡我們 -> /contact
多重選擇器 :可以同時選擇多個選擇器。像是可以同時有兩個類別,請見範例:
1 2 3 4 5 6 7 8 9 post_titles = soup.select('h2.post-title' ) print ("\n文章標題:" )for title in post_titles: print (f" {title.text} " ) featured_posts = soup.select('.post.featured' ) print (f"\n精選文章數量: {len (featured_posts)} " )
Output:
1 2 3 4 5 文章標題: 第一篇文章 第二篇文章 精選文章數量: 1
後代選擇器 :可以選擇一個標籤底下的其中一個標籤,假設要找 <article> 裡面的 <p>,那透過這個選擇器,就會找出 <article> 裡面的所有 <p> 標籤。
若要做到這個選擇器的功能,則在兩個標籤之間空一格。
以下是個範例:
1 2 3 4 5 6 7 8 9 meta_spans = soup.select('main span' ) print ("\n文章元資訊:" )for span in meta_spans: print (f" {span.text} " ) article_paragraphs = soup.select('article p' ) print (f"\n文章段落數: {len (article_paragraphs)} " )
Output:
1 2 3 4 5 6 7 文章元資訊: 作者:A 日期:2025-01-01 作者:B 日期:2025-01-02 文章段落數: 2
透過 BeautifulSoup 提取純文字 透過 .get_text() 可獲取標籤內的內容,而非標籤本身(如<p>123</p>)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from bs4 import BeautifulSouphtml = """ <div class="article"> <h1>文章標題</h1> <p>這是第一段落。</p> <p>這是<strong>重要</strong>的第二段落。</p> </div> """ soup = BeautifulSoup(html, 'html.parser' ) article = soup.find('div' , class_='article' ) print (article.get_text())
Output:
BeautifulSoup 結合 Requests 小應用 爬取網站:https://www.nptu.edu.tw/p/412-1000-2972.php?Lang=zh-tw
爬取國立屏東大學中的所有學術單位,含學院、以及旗下學系。
註:此爬蟲程式僅供學習用途,絕無任何其餘用途。
建議使用 colab 或 jupyter notebook 進行實作,若使用真實環境有可能會遇到 SSL Certificate 的問題。
另外可於該網站中任意處右鍵、按下檢查,開啟開發者工具介面,在左上方箭頭處可選取任意元素,選取完後會跳至該行的 HTML 原始碼。
範例程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import requestsfrom bs4 import BeautifulSoupurl = "https://www.nptu.edu.tw/p/412-1000-2972.php?Lang=zh-tw" html = requests.get(url) html.encoding = "utf-8" soup = BeautifulSoup(html.text, "lxml" ) main_content = soup.find("div" , class_="main" ) if main_content: full_text = main_content.get_text() lines = [line.strip() for line in full_text.split('\n' ) if line.strip()] start_idx = -1 for i, line in enumerate (lines): if "學術單位列表" in line: start_idx = i break if start_idx != -1 : relevant_lines = lines[start_idx+1 :] current_college = None departments = [] for line in relevant_lines: if "學院" in line and not line.endswith("系" ): if current_college and departments: print (f"{current_college} :" ) for dept in departments: print (f" - {dept} " ) print () current_college = line departments = [] elif any (keyword in line for keyword in ["學系" , "研究所" , "中心" , "學程" ]): if current_college: departments.append(line) elif "校區" in line: break if current_college and departments: print (f"{current_college} :" ) for dept in departments: print (f" - {dept} " )
Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 管理學院: - 商業大數據學系(含碩士班) - 行銷與流通管理學系(含碩士班) - 休閒事業經營學系(含碩士班) - 不動產經營學系(含碩士班) - 企業管理學系(含碩士班) - 國際經營與貿易學系(含碩士班) - 財務金融學系(含碩士班) - 會計學系 - 大數據商務應用學士學位學程(113學年停招) 資訊學院: - 電腦與通訊學系 - 資訊工程學系(含碩士班) - 電腦科學與人工智慧學系(含碩士班) - 資訊管理學系(含碩士班) - 智慧機器人學系 - 國際資訊科技與應用碩士學位學程 教育學院: - 教育行政研究所(含博碩士班) - 教育心理與輔導學系(含碩士班) - 教育學系(含碩士班) - 特殊教育學系(含碩士班) - 幼兒教育學系(含碩士班) - STEM教育國際碩士學位學程 - 特殊教育中心 - 社區諮商中心 - 文教事業經營碩士在職學位學程(110學年停招) 人文社會學院: - 視覺藝術學系(含碩士班) - 音樂學系(含碩士班) - 文化創意產業學系(含碩士班) - 社會發展學系(含碩士班) - 中國語文學系(含碩士班) - 應用日語學系 - 應用英語學系 - 英語學系(含碩士班) - 文化發展學士學位學程原住民專班 - 文化事業發展碩士學位學程原住民專班 - 客家文化產業碩士學位學程 - 客家研究中心 - 原住民族教育研究中心 - 藝文中心 理學院: - 科學傳播學系(含科學傳播暨教育碩士班) - 應用化學系(含碩士班) - 應用數學系(含碩士班) - 體育學系(含碩士班) 國際學院: - 東南亞發展中心 - 華語教學中心 大武山學院: - 共同教育中心 - 博雅教育中心 - 跨領域學程中心 - EMI發展中心 - 大武山社會實踐暨永續發展中心 - 新媒體創意應用碩士學位學程 - 大武山跨領域學士學位學程 - 師資培育中心 - 師資培育中心 - 教育學程組
總結 BeautifulSoup 是 Python 中用於解析 HTML 和 XML 的函式庫,將網頁內容轉換成樹狀結構供程式操作。主要應用於網路爬蟲和網頁資料擷取。
第一支 BeautifulSoup 範例 1 2 3 4 5 6 7 import requestsfrom bs4 import BeautifulSoupurl = 'https://luketsengtw.github.io/' html = requests.get(url) soup = BeautifulSoup(html.text, 'html.parser' ) print (soup.title)
from bs4 import BeautifulSoup 以此來引入 BeautifulSoup 做網頁分析與資料擷取。
soup = BeautifulSoup(html.text, 'html.parser') 是建立 BeautifulSoup 的方法。
解析器介紹 解析器 速度 準確性 容錯能力 html.parser 中 最差 最差 lxml 最快 高 高 html5lib 最慢 最高 最高
BeautifulSoup 的常用方法 find() - 找第一個符合的元素 find_all() - 找所有符合的元素 以下程式碼分別找出第一個 <p> 和找出所有的 <p> 標籤。
1 2 first_p = soup.find('p' , class_='content' ) all_p = soup.find_all('p' , class_='content' )
CSS 選擇器 使用 select() 方法:
一般標籤:soup.select('h2') ID 選擇器(用 #):soup.select('#main-header') Class 選擇器(用 .):soup.select('.nav-link') 多重選擇器:soup.select('h2.post-title') 後代選擇器(空格):soup.select('main span') 文字提取 soup.title.string - 取得標籤內文字element.get_text() - 取得純文字內容(去除標籤)link['href'] 或 link.get('href') - 取得屬性值基本爬蟲模板 1 2 3 4 5 6 7 8 9 10 11 import requestsfrom bs4 import BeautifulSoupurl = "目標網址" html = requests.get(url) html.encoding = "utf-8" soup = BeautifulSoup(html.text, "lxml" ) main_content = soup.find("div" , class_="main" ) text = main_content.get_text()
參考資料 Beautiful Soup 函式庫 - Python 網路爬蟲教學 | STEAM 教育學習網
Beautiful Soup Documentation — Beautiful Soup 4.13.0 documentation
Implementing Web Scraping in Python with BeautifulSoup - GeeksforGeeks