2024年4月7日 星期日

Python 學習筆記 : 網頁擷取 (三) : 使用 BeautifulSoup 剖析網頁

這篇筆記其實從 2021 年初就開始寫, 斷斷續續到現在已超過 3 年了 (bs4 套件都從 4.9.3 升到 4.12.3 啦), 趁清明節連假回鄉下的這幾天終於把它給寫完了. 觸發原因是前陣子想要用 selenium 來爬我在市圖借的書何時該還, 以及目前預約了哪些書等等, 才想起 BeautifulSoup 筆記都還晾在那邊呢! 

雖然爬蟲程式已寫了不少, 但每次用都會忘記而必須東翻西找, 如果有自己寫的筆記就能快速滿血復活 (不過現在有 ChatGPT 後確實方便不少). 雖然整理筆記還蠻花時間的, 但寫過測過印象真的會比較深刻 (ChatGPT 複製貼上即使 work, 但這算學會了嗎? 停電斷網怎麼辦?).     

本系列之前的測試紀錄參考 :  


在前一篇測試中使用 requests 套件所下載的網頁並不是爬蟲程式的終極目標,  真正有價值的資訊是那些內嵌在網頁結構 (例如清單或表格) 中的純文字內容. 而 Beautiful Soup 就是為了萃取網頁中的資訊而生的好用工具, 只要對 HTML 與 CSS 選擇器有基本的了解, 即可利用 BeautifulSoup 套件迅速從 HTML 網頁中定位出目標的位置, 精確地擷取所需要的資訊, 而且會自動將輸入網頁轉成 Unicode 編碼後以 utf-8 格式輸出, 參考 :                  


早期的爬蟲程式使用字串物件方法與正規式來剖析網頁內容, 但 BeautifulSoup 問世後已經能透過走訪網頁文件樹來取得任何元素, 這使得正規式在網頁剖析中的用途已大為減少, 但正規式在下載具規律性檔名的網頁時仍是不可或缺的好工具. 

用正規式解析簡單且格式符合標準 (well-formatted) 的網頁而言不難, 但對複雜或格式不標準的網頁來說就不容易, 若使用不當還可能使爬蟲程式進入無限循環. 在 "Python 自動化的樂趣 (英文版為 Automate the Boring Stuff with Python)" 這本書的第 11 章裡, 作者建議不要直接用正規式來剖析網頁, 而應該用 BeautifulSoup; 理由是 HTML 是一個非正規式語法, 其寫法多變無法完全正規化, 用正規式難以完全捕捉到目標資訊, 而且容易因為原始網頁更動 (例如添加標籤屬性或空格等) 而使剖析失敗, 參考 :


"Regular expressions are a tool that is insufficiently sophisticated to understand the constructs employed by HTML. HTML is not a regular language and hence cannot be parsed by regular expressions. Regex queries are not equipped to break down HTML into its meaningful parts."

在上面這篇文章中, Vlad Gudim 提到 HTML 在形式語言理論中屬於 Chomsky Type 2 語言 (上下文無關文法), 而正規表示法則屬於 Chomsky Type 3 語言 (正規文法), 由於 Type 2 語言本質上就比 Type 3 語言複雜, 因此要用正規式剖析 HTML 網頁是不容易的的任務. 所幸這些困難已經被 BeautifulSoup 解決了, 此套件提供簡易的方法來定位與搜尋網頁中的標籤, 當然也可以搭配正規式進行搜尋. 

不過要注意的是, BeautifulSoup 只能用來剖析後端伺服器直接回應的原生靜態網頁, 若網頁內容是透過 Javascript 或 Ajax 非同步請求在客戶端動態產生的話, BeautifulSoup 無法剖析這些後來才插入 DOM 樹的資料. 對於這種透過前端技術產生的網頁內容須改用 sellenium 套件來模擬瀏覽器執行 Javascript 才能取得目標網頁內容. 

本篇測試參考了下列書籍 : 
  1. Python 網路文字探勘入門到上手 (陳寬裕, 五南, 2020) 第 10 章
  2. Python 自學聖經 (碁峰, 鄧文淵工作室, 2020) 第 11 章
  3. Python 網路爬蟲與資料視覺化應用實務 (旗標, 陳允傑, 2018) 第 3,4 章
  4. Python 3.x 網頁資料節與與分析特訓教材 (全華, 曹祥雲, 2018)
  5. Python 網路爬蟲與資料分析實戰 (博碩, 林俊瑋, 2018) 第 2 章
  6. 矽谷工程師爬蟲手冊 (深智, 黃永祥, 2020) 第 13 章
  7. Python 自動化的樂趣 (碁峰, AI Sweigart, 2017) 第 11 章
  8. Python Web Scraping 2nd Edition (Packt, Richard Lawson, 2017)
  9. Website Scraping with Python (Apress, 2018)
其中前三本非常適合 Python 初學者學習網頁爬蟲. 


一. 安裝 BeautifulSoup :

Beautiful Soup 是第三方套件, 必須安裝後才能匯入使用, 目前為第四版, 套件名稱為 beautifulsoup4, 可用 pip (樹莓派或 macOS 須用 pip3) 安裝 :

pip install beautifulsoup4    
 
若已安裝過可加 -U 參數提升到最新版本 :

pip install beautifulsoup4 -U   

D:\python\test>pip install beautifulsoup4 -U  
Requirement already satisfied: beautifulsoup4 in c:\users\tony1\appdata\roaming\python\python310\site-packages (4.12.2)
Collecting beautifulsoup4
  Downloading beautifulsoup4-4.12.3-py3-none-any.whl.metadata (3.8 kB)
Requirement already satisfied: soupsieve>1.2 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from beautifulsoup4) (2.4.1)
Downloading beautifulsoup4-4.12.3-py3-none-any.whl (147 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 147.9/147.9 kB 550.7 kB/s eta 0:00:00
Installing collected packages: beautifulsoup4
  Attempting uninstall: beautifulsoup4
    Found existing installation: beautifulsoup4 4.12.2
    Uninstalling beautifulsoup4-4.12.2:
      Successfully uninstalled beautifulsoup4-4.12.2
Successfully installed beautifulsoup4-4.12.3

可見目前最新是 4.12.3 版. 


二. 建立 BeautifulSoup 物件 :

安裝好 BeautifulSoup 即可用 import 匯入 bs4 模組來建立 BeautifulSoup 物件. 注意, 雖然套件名稱為 beautifulsoup4, 但其內部模組名稱卻是 bs4, 使用時是要匯入 bs4 :

import bs4  

先用 dir() 顯示 bs4 模組內容 : 

>>> import bs4  
>>> dir(bs4)  
>>> dir(bs4)
['BeautifulSoup', 'BeautifulStoneSoup', 'CData', 'CSS', 'Comment', 'Counter', 'DEFAULT_OUTPUT_ENCODING', 'Declaration', 'Doctype', 'FeatureNotFound', 'GuessedAtParserWarning', 'HTMLParserTreeBuilder', 'MarkupResemblesLocatorWarning', 'NavigableString', 'PYTHON_SPECIFIC_ENCODINGS', 'PageElement', 'ParserRejectedMarkup', 'ProcessingInstruction', 'ResultSet', 'Script', 'SoupStrainer', 'StopParsing', 'Stylesheet', 'Tag', 'TemplateString', 'UnicodeDammit', 'XMLParsedAsHTMLWarning', '__all__', '__author__', '__builtins__', '__cached__', '__copyright__', '__doc__', '__file__', '__license__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_s', '_soup', 'builder', 'builder_registry', 'css', 'dammit', 'element', 'formatter', 'os', 're', 'sys', 'traceback', 'warnings']

由於 dir() 的只輸出成員名稱, 無法得知那些是模組, 類別, 或函式, 我們可以利用下面的自訂模組 members 的 list_members() 來檢視 bs4 的成員 : 

# members.py
import inspect 
def varname(x): 
    return [k for k,v in inspect.currentframe().f_back.f_locals.items() if v is x][0]
def list_members(parent_obj):
    members=dir(parent_obj)
    parent_obj_name=varname(parent_obj)       
    for mbr in members:
        child_obj=eval(parent_obj_name + '.' + mbr) 
        if not mbr.startswith('_'):
            print(mbr, type(child_obj))  


將此函式存成 members.py 模組放在目前工作目錄下, 然後匯入其 list_members() 函式來檢視 bs4 套件的成員 :

>>> import bs4  
>>> from members import list_members   
>>> list_members(bs4)   
BeautifulSoup <class 'type'>
BeautifulStoneSoup <class 'type'>
CData <class 'type'>
CSS <class 'type'>
Comment <class 'type'>
Counter <class 'type'>
DEFAULT_OUTPUT_ENCODING <class 'str'>
Declaration <class 'type'>
Doctype <class 'type'>
FeatureNotFound <class 'type'>
GuessedAtParserWarning <class 'type'>
HTMLParserTreeBuilder <class 'type'>
MarkupResemblesLocatorWarning <class 'type'>
NavigableString <class 'type'>
PYTHON_SPECIFIC_ENCODINGS <class 'set'>
PageElement <class 'type'>
ParserRejectedMarkup <class 'type'>
ProcessingInstruction <class 'type'>
ResultSet <class 'type'>
Script <class 'type'>
SoupStrainer <class 'type'>
StopParsing <class 'type'>
Stylesheet <class 'type'>
Tag <class 'type'>
TemplateString <class 'type'>
UnicodeDammit <class 'type'>
XMLParsedAsHTMLWarning <class 'type'>
builder <class 'module'>
builder_registry <class 'bs4.builder.TreeBuilderRegistry'>
css <class 'module'>
dammit <class 'module'>
element <class 'module'>
formatter <class 'module'>
os <class 'module'>
re <class 'module'>
sys <class 'module'>
traceback <class 'module'>
warnings <class 'module'>

可見 bs4 套件內部也使用了正規式模組 re. 

bs4 套件中最重要的成員是 BeautifulSoup 類別, 用 bs4 進行網頁剖析必須先呼叫其建構式 BeautifulSoup() 建立一個 BeautifulSoup 物件, 因此使用  bs4 套件時通常只需要從 bs4 裡匯入 BeautifulSoup 類別即可 :

from bs4 import BeautifulSoup    

接著就可以呼叫其建構式 BeautifulSoup(), 它會傳回一個代表 DOM 語法樹 (文件樹) 結構的  BeautifulSoup 物件, 語法如下 : 

BeautifulSoup(html [, parser])  

其中第一參數 html 必要參數, 是待剖析之 HTML 文本內容, 可以是下列三種來源 :
  1. HTML 字串 
  2. 開啟之本地 HTML 檔案參考 (file handle)
  3. HTTP/HTTPS 請求之回應內容
注意, BeautifulSoup 並非 HTTP 客戶端, 因此不可以直接將 URL 作為第一參數, 必須利用 urllib 或第三方之 requests 等模組取得網頁文本 (字串) 後再傳入. 除了字串外, html 參數也可以接受檔案參考, 因此使用 open() 開啟本地檔案後不需要讀取檔案內容文本, Beautiful Soup 會自行讀取檔案內容. 

BeautifulSoup() 的第二參數 parser 為 HTML 剖析器名稱 (字串), 此為備選參數, 當未指定時 Beautiful Soup 會自行選定, 可用的剖析器有三個 :


 bs4 網頁剖析器名稱 說明
 lxml  外掛模組, 剖析速度快, 容錯性佳 (有安裝時預設)
 html5lib 外掛模組, 剖析速度較慢, 以瀏覽器方式剖析, 容錯性最佳
 html.parser Python 內建之剖析器不須安裝, 但剖析速度普通, 容錯性較差


其中 "html.parser" 是 BeautifulSoup 內建的 HTML 剖析器, 其容錯性在 Python 3.2 版之前比較差, 但之後的 Python 版本雖已改善, 但仍有一些缺點, 因此 BeautifulSoup 官網建議使用 lxml,  此剖析器可剖析 HTML 與 XML 文本,  如果有安裝 lxml 套件, 未指定剖析器時 BeautifulSoup 會優先使用 lxml. , 否則預設會使用內建的 html.parser 剖析器. 

如果要使用第三方套件 lxml 或 html5lib, 使用前需先安裝, 指令如下 :

pip3 install lxml 
pip3 install html5lib

以下分別用 HTML 字串, 本地 HTML 檔案參考, 以及 HTTP 請求之回應三種 HTML 來源來建立 BeautifulSoup 物件. 


1. 傳入 HTML 字串 :

首先建立一個 HTML 字串物件, 將其傳入 BeautifulSoup() : 

>>> import bs4 
>>> html="<p>Hello World</p>"    
>>> soup=bs4.BeautifulSoup(html, 'html.parser')     # 建立 BeautifulSoup 物件 (文件樹)
>>> type(soup)   
<class 'bs4.BeautifulSoup'>      
>>> soup   
<p>Hello World</p>

此處 HTML 文本係來自一個不完整的 HTML 字串, 使用內建的 html.parser 剖析後所建立的語法樹與來源字串一模一樣, 可見 html.parser 不會自動將網頁補全. 

如果使用 lxml 剖析器則會補上 html 與 body 標籤 (但沒有補上 head) :  

>>> soup=bs4.BeautifulSoup(html, "lxml")    
>>> soup    
<html><body><p>Hello World</p></body></html>    

使用 html5lib 則最完整 (會補上 head) : 

>>> soup=bs4.BeautifulSoup(html, "html5lib")     
>>> soup      
<html><head></head><body><p>Hello World</p></body></html>

可見使用不同的剖析器所產生的 HTML 語法樹有些微差異. 

除此之外, 對於格式殘缺或不標準的網頁文件, 不同剖析器的補全效果也不同, 例如下面的無序清單, 屬性 class 之值缺了括號; 而 li 元素則缺少結束標籤 </li> : 

>>> html="<ul class=name><li>Amy<li>Peter<li>Kelly</ul>"   

使用內建的 html.parser 剖析器會為 class 屬性值加上括號, 但是卻把三個結束標籤 </li> 錯誤地解析為巢狀 li 結構 (不優), 這會對定位目標元素造成困擾 :

>>> soup=bs4.BeautifulSoup(html, "html.parser")    
>>> soup
<ul class="name"><li>Amy<li>Peter<li>Kelly</li></li></li></ul>   

使用 lxml 剖析器則會正確地解析並補全網頁格式 (讚) : 

>>> html="<ul class=name><li>Amy<li>Peter<li>Kelly</ul>"
>>> bs4obj=bs4.BeautifulSoup(html, "lxml")  
>>> bs4obj     
<html><body><ul class="name"><li>Amy</li><li>Peter</li><li>Kelly</li></ul></body></html>

使用 html5lib 也把 </li> 放在正確位置 (還另外補上 head 標籤), 例如 :

>>> html="<ul class=name><li>Amy<li>Peter<li>Kelly</ul>"
>>> soup=bs4.BeautifulSoup(html, "html5lib")    
>>> soup   
<html><head></head><body><ul class="name"><li>Amy</li><li>Peter</li><li>Kelly</li></ul></body></html> 

以上可知 lxml 在正確性, 容錯性, 與剖析速度上是較佳的選擇, 因此在有安裝 lxml 情況下, 未指定剖析器時 BeatifulSoup 會自動選用 lxml. 


2. 傳入 HTML 檔案參考 :

以上是以 HTML 字串為來源建立 BeautifulSoup 物件, 也可以用 open() 函式開啟本機的 HTML檔, 將檔案參考傳入 BeautifulSoup() 來建立 BeautifulSoup 物件, 先準備一個 HTML 檔案 bs4test.htm 以 UTF-8 格式儲存在目前工作目錄下 : 

<--! bs4test.htm -->
<!doctype html>
<html>
 <head>
  <meta charset="UTF-8">
  <meta name="Keywords" content="requests,bs4">
  <title>BeautifulSoup 測試</title>
 </head>
 <body>
  <h1>標題一</h1>
  <div>
    <p>段落一</p>
    <img src="https://upload.wikimedia.org/wikipedia/zh/thumb/3/34/Lenna.jpg/300px-Lenna.jpg" alt="Lenna 圖">
    <a href="https://en.wikipedia.org/wiki/Lenna">Wiki:Lenna 圖</a>
  </div>
 </body>
</html>

此測試網頁檔可在 GitHub 下載 :


用內建函式 open() 讀取 bs4test.htm 後傳入 BeautifulSoup() 中建立 BeautifulSoup 物件 : 

try:    
    htmlfile=open("bs4test.htm", "r", encoding="utf8")
    soup=bs4.BeautifulSoup(htmlfile, "lxml")
except:    
    htmlfile.close()    

或者使用 with 語法開啟檔案 : 

with open("bs4test.htm", "r", encoding="utf8") as htmlfile:    
    soup=bs4.BeautifulSoup(htmlfile, "lxml")   

使用 with 語句開啟檔案的好處就是它會自行關閉檔案, 不須呼叫 close() 函式關檔. 

>>> with open("bs4test.htm", "r", encoding="utf8") as htmlfile:       
    soup=BeautifulSoup(htmlfile, "lxml")   

>>> type(soup)   
<class 'bs4.BeautifulSoup'>  
>>> soup  
&lt;<html><body><p>--! bs4test.htm --&gt;
<!DOCTYPE html>

</p>
<meta charset="utf-8"/>
<meta content="requests,bs4" name="Keywords"/>
<title>BeautifulSoup 測試</title>
<h1>標題一</h1>
<div>
<p>段落一</p>
<img alt="Lenna 圖" src="https://upload.wikimedia.org/wikipedia/zh/thumb/3/34/Lenna.jpg/300px-Lenna.jpg"/>
<a href="https://en.wikipedia.org/wiki/Lenna">Wiki:Lenna 圖</a>
</div>
</body></html>

可見傳入 HTML 檔案參考也是可以建立 BeautifulSoup 物件. 


3. 傳入 HTTP 請求之回應內容 :

可以利用 urllib 或第三方的 requests 套件發出 HTTP 請求向遠端主機取得 HTML 回應, 將其傳入BeautifulSoup() 來建立物件, 以之前測試 requests 時所使用的 PHP 爬蟲範例網址為例 :  


使用 requests 模組的 get() 方法取得此遠端 HTML 檔內容 :

>>> import requests   
>>> url='http://www.webbotsspidersscreenscrapers.com/hello_world.html'     
>>> response=requests.get(url)      
>>> response.text    
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\r\n\r\n<html>\r\n<head>\r\n\t<title>Hello, world!</title>\r\n</head>\r\n\r\n<body>\r\nCongratulations! If you can read this, <br>\r\nyou successfully downloaded this file.\r\n</body>\r\n</html>\r\n'

可見 HTTP 回應是該網頁的 HTML 字串, 可以將它傳入 BeautifulSoup() 以建立物件 :

>>> soup=BeautifulSoup(response.text)   
>>> type(soup)    
<class 'bs4.BeautifulSoup'>   
>>> soup   
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Hello, world!</title>
</head>
<body>
Congratulations! If you can read this, <br/>
you successfully downloaded this file.
</body>
</html> 

其實這跟上面直接傳入 HTML 字串沒甚麼不同, 差別在於需先透過 requests 的 HTTP 方法取得遠方主機的網頁內容而已, 這是因為 BeautifulSoup 不具有 HTTP 功能, 不可以直接傳入 URL 給 BeautifulSoup(). 

如果使用 urllib 擷取網頁的話, 可以呼叫其回應物件 HTTPResponse 的 read() 方法取得網頁內容 (byte 型別) 後傳入 BeautifulSoup(), 也可以直接將 HTTPResponse 物件直接傳入 BeautifulSoup() :

>>> from urllib.request import urlopen  
>>> from bs4 import BeautifulSoup  
>>> url='http://www.webbotsspidersscreenscrapers.com/hello_world.html'  
>>> response=urlopen(url)   
>>> type(response)      
<class 'http.client.HTTPResponse'>      
>>> html=response..read()   
>>> html  
b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\r\n\r\n<html>\r\n<head>\r\n\t<title>Hello, world!</title>\r\n</head>\r\n\r\n<body>\r\nCongratulations! If you can read this, <br>\r\nyou successfully downloaded this file.\r\n</body>\r\n</html>\r\n'
>>> type(html)     
<class 'bytes'>   
>>> soup=BeautifulSoup(response)     # 傳入 HTTPResponse 物件 (也可以傳入 html)
>>> soup    
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Hello, world!</title>
</head>
<body>
Congratulations! If you can read this, <br/>
you successfully downloaded this file.
</body>
</html>

結果與使用 requests 相同. 


三. HTML 網頁與 DOM 語法樹 : 

以上所建立的 BeautifulSoup 物件其實就是將 HTML 網頁內容依照 DOM 樹狀結構剖析後所建立的一個語法樹物件, 結構如下圖所示 : 




其中每一個節點都是一個 Tag 物件, 從網頁中定位目標資料就是利用 Tag 物件所提供的屬性與方法來搜尋語法樹中符合條件的 HTML 標籤元素. 

網頁中的資料通常放在下面這些標籤元素中 :


 HTML 標籤 說明
 p 段落 (資料放在 text 中)
 a 超連結 (URL 放在 href 屬性中)
 img 圖片 (URL 放在 href 屬性中)
 table, tr, td 表格元素 (資料放在 td 元素的 text 中)
 div 區塊容器 (資料放在 text 中)
 span 行內容器 (資料放在 text 中)
 h1~h6 內文標題 (資料放在 text 中)
 font 字型 (資料放在 text 中)


定位網頁中的目標資料除了鎖定標籤元素名稱外, 還可以搜尋標籤中所帶的屬性, 其中最常被鎖定的屬性如下表 : 


 標籤屬性 說明
 id 標籤的 ID (網頁中必須是唯一)
 class 標籤套用的樣式類別 (可套用於多個標籤)
 style 標籤的行內樣式
 title 滑鼠在標籤上時顯示之資訊
 href 超連結之 URL
 src 圖檔之 URL 
 data-xxxx HTML5 自訂屬性 (例如 Bootstrap)



具體而言, bs4.BeautifulSoup() 是將整份 HTML 網頁剖析後建立一個由 Tag, NavigableString, 以及 Comment 三個子物件構成的 BeautifulSoup 物件 : 


 bs4 的物件 說明
 BeautifulSoup 代表整個 HTML 文件樹狀結構之物件
 Tag 代表網頁元素 (標籤) 之物件
 NavigableString 代表標籤內容字串之物件
 Comment 代表網頁說明之物件 (即 <!-- 與 --> 之間內容)


這四個物件具有如下之階層關係 : 

BeautifulSoup
        |______ Tag
        |______ NavigableString
        |______ Comment


其中 BeautifulSoup 物件代表整份網頁的語法樹; Tag 物件則是語法樹中的各節點, 主要是網頁中的各標籤 (元素) 與文字等元素, Tag 也是定位目標資料的最重要物件, 它繼承了 BeautifulSoup 物件的屬性與方法; NavigableString 物件為標籤的內容字串, 而 Comment 物件是特殊的 NavigableString 物件, 代表 <!-- 與 --> 網頁註解之內容 (但這些註解文字必須放在 p, span, div 等可走訪的標籤內才會被剖析器放入 Comment 物件中). 

從 BeautifulSoup 物件語法樹中定位出目標資料主要是透過下列六種方式 :
  • 標籤名稱 : 例如 p, div, table 等
  • 標籤之 id 屬性 
  • 標籤之 class 屬性
  • CSS 選擇器
  • 正規表達式
  • XPath 表達式
通常使用前四種方法即可定位到目標資料. 


四. BeautifulSoup 與 Tag 物件的屬性與方法 :

使用 BeatifulSoup 剖析網頁主要是使用 BeatifulSoup 物件與其子物件 Tag, 首先用上面的自訂模組 members 的 list_members() 函式來檢視 BeatifulSoup 物件的成員 : 

>>> from members import list_members   
>>> list_members(soup)    
ASCII_SPACES <class 'str'>
DEFAULT_BUILDER_FEATURES <class 'list'>
DEFAULT_INTERESTING_STRING_TYPES <class 'tuple'>
EMPTY_ELEMENT_EVENT <class 'object'>
END_ELEMENT_EVENT <class 'object'>
NO_PARSER_SPECIFIED_WARNING <class 'str'>
ROOT_TAG_NAME <class 'str'>
START_ELEMENT_EVENT <class 'object'>
STRING_ELEMENT_EVENT <class 'object'>
append <class 'method'>
attrs <class 'dict'>
builder <class 'bs4.builder._lxml.LXMLTreeBuilder'>
can_be_empty_element <class 'bool'>
cdata_list_attributes <class 'dict'>
childGenerator <class 'method'>
children <class 'list_iterator'>
clear <class 'method'>
contains_replacement_characters <class 'bool'>
contents <class 'list'>
css <class 'bs4.css.CSS'>
currentTag <class 'bs4.BeautifulSoup'>
current_data <class 'list'>
declared_html_encoding <class 'NoneType'>
decode <class 'method'>
decode_contents <class 'method'>
decompose <class 'method'>
decomposed <class 'bool'>
default <class 'object'>
descendants <class 'generator'>
element_classes <class 'dict'>
encode <class 'method'>
encode_contents <class 'method'>
endData <class 'method'>
extend <class 'method'>
extract <class 'method'>
fetchNextSiblings <class 'method'>
fetchParents <class 'method'>
fetchPrevious <class 'method'>
fetchPreviousSiblings <class 'method'>
find <class 'method'>
findAll <class 'method'>
findAllNext <class 'method'>
findAllPrevious <class 'method'>
findChild <class 'method'>
findChildren <class 'method'>
findNext <class 'method'>
findNextSibling <class 'method'>
findNextSiblings <class 'method'>
findParent <class 'method'>
findParents <class 'method'>
findPrevious <class 'method'>
findPreviousSibling <class 'method'>
findPreviousSiblings <class 'method'>
find_all <class 'method'>
find_all_next <class 'method'>
find_all_previous <class 'method'>
find_next <class 'method'>
find_next_sibling <class 'method'>
find_next_siblings <class 'method'>
find_parent <class 'method'>
find_parents <class 'method'>
find_previous <class 'method'>
find_previous_sibling <class 'method'>
find_previous_siblings <class 'method'>
format_string <class 'method'>
formatter_for_name <class 'method'>
get <class 'method'>
getText <class 'method'>
get_attribute_list <class 'method'>
get_text <class 'method'>
handle_data <class 'method'>
handle_endtag <class 'method'>
handle_starttag <class 'method'>
has_attr <class 'method'>
has_key <class 'method'>
hidden <class 'int'>
index <class 'method'>
insert <class 'method'>
insert_after <class 'method'>
insert_before <class 'method'>
interesting_string_types <class 'tuple'>
isSelfClosing <class 'bool'>
is_empty_element <class 'bool'>
is_xml <class 'bool'>
known_xml <class 'bool'>
markup <class 'NoneType'>
name <class 'str'>
namespace <class 'NoneType'>
new_string <class 'method'>
new_tag <class 'method'>
next <class 'NoneType'>
nextGenerator <class 'method'>
nextSibling <class 'NoneType'>
nextSiblingGenerator <class 'method'>
next_element <class 'NoneType'>
next_elements <class 'generator'>
next_sibling <class 'NoneType'>
next_siblings <class 'generator'>
object_was_parsed <class 'method'>
open_tag_counter <class 'collections.Counter'>
original_encoding <class 'NoneType'>
parent <class 'NoneType'>
parentGenerator <class 'method'>
parents <class 'generator'>
parse_only <class 'NoneType'>
parserClass <class 'type'>
parser_class <class 'type'>
popTag <class 'method'>
prefix <class 'NoneType'>
preserve_whitespace_tag_stack <class 'list'>
preserve_whitespace_tags <class 'set'>
prettify <class 'method'>
previous <class 'NoneType'>
previousGenerator <class 'method'>
previousSibling <class 'NoneType'>
previousSiblingGenerator <class 'method'>
previous_element <class 'NoneType'>
previous_elements <class 'generator'>
previous_sibling <class 'NoneType'>
previous_siblings <class 'generator'>
pushTag <class 'method'>
recursiveChildGenerator <class 'method'>
renderContents <class 'method'>
replaceWith <class 'method'>
replaceWithChildren <class 'method'>
replace_with <class 'method'>
replace_with_children <class 'method'>
reset <class 'method'>
select <class 'method'>
select_one <class 'method'>
self_and_descendants <class 'generator'>
setup <class 'method'>
smooth <class 'method'>
string <class 'bs4.element.NavigableString'>
string_container <class 'method'>
string_container_stack <class 'list'>
strings <class 'generator'>
stripped_strings <class 'generator'>
tagStack <class 'list'>
text <class 'str'>
unwrap <class 'method'>
wrap <class 'method'>

可見 BeautifulSoup 物件提供了非常多的屬性方法可用來剖析網頁內容.


1. BeautifulSoup 物件的常用屬性 :

所有網頁元素的標籤名稱都是 BeautifulSoup 物件的屬性, 可以直接用點運算符取得這些元素的 Tag 物件, 不過因為這種方式只能取得同標籤名稱的第一個元素 (如果有多個相同元素只能取得第一個 Tag 物件), 因此通常用來取得唯一的標籤, 例如 : 


 BeautifulSoup 物件的屬性 說明
 html 網頁整體 html 元素之 Tag 物件
 head 網頁標頭 head 元素之 Tag 物件 
 title 網頁標題 title 元素之 Tag 物件
 body 網頁內容 body 元素之 Tag 物件
 script 網頁內嵌 Javascript 元素之 Tag 物件


注意, 這些屬性專屬於 BeautifulSoup 物件, 其值都是代表網頁元素的 Tag 物件, 例如 :

>>> html="""   
     <html><head><title>BeautifulSoup 測試網頁</title></head>
     <body><!-- 這是註解1 --><!-- <p>這是註解2</p> -->
     <p>Hello World! <b>Tony</b></p>
     <p>哈囉!</p>
     <div>這是 div 區塊</div>
     </body><script>alert('Hello World!')</script></html>"""

將它傳入 BeautifulSoup() 建立 BeautifulSoup 物件 : 

>>> from bs4 import BeautifulSoup 
>>> soup=BeautifulSoup(html, "lxml")     # 建立 BeautifulSoup 物件

檢視 BeautifulSoup 物件之屬性 : 

>>> soup.html   
<html><head><title>BeautifulSoup 測試網頁</title></head>
<body><!-- 這是註解1 --><!-- <p>這是註解2</p> -->
<p>Hello World! <b>Tony</b></p>
<p>哈囉!</p>
<div>這是 div 區塊</div>
</body><script>alert('Hello World!')</script></html>
>>> type(soup.html)   
<class 'bs4.element.Tag'>  
>>> soup.head   
<head><title>BeautifulSoup 測試網頁</title></head>
>>> type(soup.head)   
<class 'bs4.element.Tag'>  
>>> soup.title    
<title>BeautifulSoup 測試網頁</title>
>>> type(soup.title)   
<class 'bs4.element.Tag'>  
>>> soup.body   
<body><!-- 這是註解1 --><!-- <p>這是註解2</p> -->
<p>Hello World! <b>Tony</b></p>
<div>這是 div 區塊</div>
</body>
>>> type(soup.body)   
<class 'bs4.element.Tag'>  
>>> soup.script 
<script>alert('Hello World!')</script>
>>> type(soup.script)   
<class 'bs4.element.Tag'>  
>>> soup.div  
<div>這是 div 區塊</div>  
>>> type(soup.div)  
<class 'bs4.element.Tag'>  

此網頁中有兩個 p 元素 (註解中的不算), 但 soup.p 只能取得第一個 p 元素的 Tag 物件 :

>>> soup.p   
<p>Hello World! <b>Tony</b></p>

我們可以從 BeautifulSoup 這個根物件開始, 循著上面的語法樹結構, 用點運算符由上而下走訪節點 (但同名元素只能走訪第一個) :

soup.tag.sub_tag. .....

例如取得上面網頁中的 <b>Tony</b> 這個 Tag 物件 : 

>>> tag=soup.html.body.p.b   
>>> tag  
<b>Tony</b>  

事實上省略層級路徑也是可以的 :

>>> soup.body.p.b  
<b>Tony</b>  
>>> soup.html.p.b  
<b>Tony</b> 
>>> soup.p.b   
<b>Tony</b>  
>>> soup.b  
<b>Tony</b> 

由於使用 BeautifulSoup 物件之標籤屬性只能取得網頁中的第一個元素之 Tag 物件, 網頁剖析實務較常使用其方法來找到網頁中的目標元素. 


2. BeautifulSoup 物件的常用方法 :  

常用的 BeautifulSoup 物件方法如下表 :


 BeautifulSoup 物件方法 說明
 prettify() 重排網頁碼使其符合規格, 傳回 HTML 字串
 find() 以名稱與屬性搜尋標籤, 傳回找到的第一個 Tag 物件
 find_all() 以名稱與屬性搜尋標籤, 傳回全部符合之 Tag 物件串列
 select() 以標籤名稱, id, class 選取標籤, 傳回符合之 Tag 物件串列
 select_one() 功能與 select() 相同, 但只傳回第一個符合之 Tag 物件


呼叫 BeautifulSoup 物件的 find(), find_all(), select_one(), 與 select() 這四個方法就能精確地定位網頁中的任一元素, find() 與 select_one() 會傳回第一個符合之 Tag 物件, 而 find_all() 與 select() 則會傳回所有符合之 Tag 物件組成的串列. 

注意, 上表中的這些常用方法在 Tag 物件中也都有, 因此當呼叫 BeautifulSoup 物件的這些方法找出 Tag 物件後, 還可以繼續呼叫 Tag 物件的這些方法連鎖地往下層搜尋子物件. 


(1). prettify() 方法 : 

prettify() 方法可以將原本格式凌亂的 HTML 碼排列整齊, 例如 : 

>>> from bs4 import BeautifulSoup   
>>> soup=BeautifulSoup(html, 'lxml')   
>>> html="""   
     <html><head><title>BeautifulSoup 測試網頁</title></head>
     <body><!-- 這是註解1 --><!-- <p>這是註解2</p> -->
     <p>Hello World! <b>Tony</b></p>
     <p>哈囉!</p>
     <div>這是 div 區塊</div>
     </body><script>alert('Hello World!')</script></html>"""
>>> soup=BeautifulSoup(html, "lxml")     # 建立 BeautifulSoup 物件 

如果直接輸出 prettify() 的傳回值會是連在一起的字串 :

>>> soup.prettify() 
"<html>\n <head>\n  <title>\n   BeautifulSoup 測試網頁\n  </title>\n </head>\n <body>\n  <!-- 這是註解1 -->\n  <!-- <p>這是註解2</p> -->\n  <p>\n   Hello World!\n   <b>\n    Tony\n   </b>\n  </p>\n  <div>\n   這是 div 區塊\n  </div>\n </body>\n <script>\n  alert('Hello World!')\n </script>\n</html>\n"

要用 print() 輸出才會看到整齊的效果 : 

>>> print(soup.prettify())    
<html>
 <head>
  <title>
   BeautifulSoup 測試網頁
  </title>
 </head>
 <body>
  <!-- 這是註解1 -->
  <!-- <p>這是註解2</p> -->
  <p>
   Hello World!
   <b>
    Tony
   </b>
  </p>
  <p>
   哈囉!
  </p>
  <div>
   這是 div 區塊
  </div>
 </body>
 <script>
  alert('Hello World!')
 </script>
</html>


(2). find() 與 find_all() 方法 : 

find() 與 find_all() 方法的差別在於 find() 只傳回第一個符合的 Tag 物件, 而 find_all() 則傳回所有符合之 Tag 物件 (以串列傳回), 語法如下 :

tag=soup.find(name, attrs, recursive, text)    

參數說明 :
  • name : 標籤名稱/正規式/標籤名稱串列/函式名稱
    可以直接傳入名稱字串, 例如 "a", "div" 等, 也可以傳入鍵值對, 例如 :
    name="a"
    name="div"
    此外 name 參數也可以傳入正規式, 標籤名稱串列 (各元素是 OR 關係), 或函式名稱. 
  • attrs : 標籤屬性
    attrs 的值為一個字典, 以標籤屬性名稱為鍵, 屬性值為值, 例如 :
    attrs={"id": "account"}
    attrs={"class": "box_color"}
    字典可以同時有多個屬性, 彼此為 AND 關係, 例如 :
    attrs={"class": "box_color", "name": "color_sel"}
    如果屬性是具有唯一性的 id, 可以直接用 id 當參數例如 id="account", 毋須使用 attrs 字典. 
    由於搜尋 class 屬性很常用, BeautifulSoup 特別為此設置了 class_ 參數給樣式搜尋使用, 例如 :
    class_="box_color"
  • recursive : 是否向下搜尋所有孫標籤, 預設為 True, 設為 False 僅搜尋子標籤.
  • text : 標籤的內容字串, 例如 :
    text="性別"
其中最常用的是 name 與 attrs 這兩個參數.

find_all() 方法的語法如下 :

tag=soup.find(name, attrs, recursive, text, limit)    

其參數用法與 find() 相同, 但多了一個 limit 參數 :
  • limit() : 搜尋 Tag 物件數的上限 (到達即停止搜尋)
首先準備一個測試網頁 bs4test-2.htm

<!doctype html>
<html>
 <head>
  <meta charset="UTF-8">
  <meta name="Keywords" content="requests,bs4">
  <title>BeautifulSoup 測試</title>
  <style>
    .large {
      color: blue;
      text-align:left;}
  </style>
 </head>
 <body>
  <h1 class="large"> h1 標題</h1>
  <h2> h2 標題</h2>
  <h3> h3 標題</h3>
  <h4 class="large"> h4 標題</h4>
  <h5 class="large"> h5 標題</h5>
  <h6 class="large"> h6 標題</h6>
  <p id="p1">段落</p>
  <p id="p2" style="">Hello, <strong>World!</strong></p>
  <div>
    <a href="https://yhhuang1966.blogspot.com/">超連結</a>
  </div>
  <p id="p3" class="story">很久以前有三個小姊妹, 她們的名字是 : 
    <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 與 
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
    , 她們住在一個井底.
  </p>
  <p class="story">...</p>
 </body>
</html>




將此網頁以 utf-8 編碼儲存在工作目錄下的 bs4test-2.htm 檔, 然後用 with open 開啟檔案, 再將檔案參考傳給 BeautifulSoup() 以建立 BeautifulSoup 物件 : 

>>> from bs4 import BeautifulSoup   
>>> with open("bs4test-2.htm", "r", encoding="utf8") as htmlfile:       
    soup=BeautifulSoup(htmlfile, "lxml")   

此網頁中有四個 p 元素, 但呼叫 soup 的 find() 只會傳回第一個 p 元素的 Tag 物件 :

>>> tag=soup.find('p')     # 或 name='p'
>>> type(tag)   
<class 'bs4.element.Tag'>
>>> tag                             # 傳回 Tag 物件
<p id="p1">段落</p>
>>> str(tag)                      # 傳回 p 元素 (字串)
'<p id="p1">段落</p>'

呼叫 find_all('p') 則會傳回所有 p 元素的 Tag 物件組成之串列 (但物件名稱叫做 ResultSet, 不是 list), 例如 :

>>> tags=soup.find_all('p')         # 或 name='p'
>>> type(tags)   
<class 'bs4.element.ResultSet'>     # 傳回 ResultSet 物件 (其實就是串列)
>>> tags   
[<p id="p1">段落一</p>, <p id="p2" style="">段落二</p>, <p class="story" id="p3">很久以前有三個小姊妹, 她們的名字是 : 
    <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> 與 
    <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
    她們住在一個井底;
  </p>, <p class="story">...</p>]

檢查 tags 串列的元素數量為 4, 因為網頁內有 4 個 p 元素, 每一個元素都是 Tag 物件 :

>>> len(tags)   
4
>>> type(tags[0])    
<class 'bs4.element.Tag'>
>>> type(tags[1])    
<class 'bs4.element.Tag'> 
>>> type(tags[2])    
<class 'bs4.element.Tag'>
>>> type(tags[3])    
<class 'bs4.element.Tag'>

name 參數也可以傳入標籤名稱的串列, 元素之間是 OR 關係, 例如上面的網頁檔中含有 1 個 strong 元素與 4 個 a 元素, 則傳入 name=['a', 'strong'] 會傳回 5 個 Tag 物件 :

>>> tags=soup.find_all(name=['a', 'strong'])     # 搜尋 a 或 strong 標籤
>>> for tag in tags:   
    print(tag)
    
<strong>World!</strong>
<a href="https://yhhuang1966.blogspot.com/">超連結</a>
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

name 參數也可以傳入正規式來搜尋有規則可循的標籤名稱, 例如上面網頁檔中有 h1~h6 元素, 它們的正規式為 'h[1-6]', 用 re.compile() 產生 Pattern 物件傳給 name 參數便可一次將此 6 個元素搜尋出來, 例如 :

>>> import re    
>>> tags=soup.find_all(name=re.compile('h[1-6]'))       # 搜尋 h1~h6 元素
>>> for tag in tags:   
    print(tag)   
    
<h1 class="large"> h1 標題</h1>
<h2> h2 標題</h2>
<h3> h3 標題</h3>
<h4 class="large"> h4 標題</h4>
<h5 class="large"> h5 標題</h5>
<h6 class="large"> h6 標題</h6>

但是要注意, 不當使用正規式可能使程式陷入無窮迴圈的危險

name 參數也可以傳入一個函式來搜尋元素, 例如上面網頁檔中有 4 個 a 元素, 只有第一個沒有 class 屬性, 我們可以定義一個搜尋有 class 屬性的 a 元素 :

>>> def a_with_class(tag):    
    return tag.name=='a' and tag.has_attr('class')    

此處要用到 Tag 物件的 has_attr() 方法來判斷有無屬性 class, 以及 name 屬性來判斷是否為 a 元素. 呼叫 find() 或 find_all() 時會掃描網頁語法樹並隱性地傳入每一個 Tag 物件, 然後將其傳給 name 參數所指的搜尋函式 :

>>> tags=soup.find_all(a_with_class)   
>>> for tag in tags:   
    print(tag)    
    
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

可見 4 個 a 元素中只搜尋到最後那 a 個符合有 class 屬性之條件. 

接下來測試用 attrs 參數


(3). select_one() 與 select() 方法 : 

BeautifulSoup 支援在 select_one() 與 select() 方法中使用 CSS 選擇器來來搜尋目標元素, 它們的差別在於 select_one() 只傳回第一個符合的 Tag 物件, 而 select() 則傳回所有符合之 Tag 物件串列, 語法如下 :

tag=soup.select_one(選擇器字串)    
tags=soup.select(選擇器字串)      

CSS 選擇器字串主要由標籤名稱, id 屬性 (用 # 表示), class 屬性 (用 . 表示) 與 [] 等字符組成, 常用的選擇器字串範例如下表 :


 CSS 選擇器範例 說明
 'div' 選擇所有 div 元素
 '.myclass' 選擇所有具有 class='myclass' 樣式屬性之元素
 '#myid' 選擇具有 id='myid' 屬性之元素 (id 在網頁中為獨一無二)
 'p.myclass' 選擇所有具有 class='myclass' 樣式屬性之 p 元素
 'div > a' 選擇所有 div 元素中的 a 元素


關於 CSS 選擇器用法參考 :


以上面的 bs4test2.htm 網頁為例, 讀取後傳給 BeautifulSoup() 建立 BeautifulSoup 物件 :

>>> with open("bs4test-2.htm", "r", encoding="utf8") as htmlfile:        
    soup=BeautifulSoup(htmlfile, "lxml")  

此網頁中有 4 個 a 元素, 呼叫 select_one('a') 只會選擇到第一個 a 元素, 傳回其 Tag 物件 :
    
>>> tag=soup.select_one('a')     # 選擇第一個 a 元素
>>> type(tag)   
<class 'bs4.element.Tag'>    
>>> tag   
<a href="https://yhhuang1966.blogspot.com/">超連結</a>

如果用 select() 則會傳回全部 4 個 a 元素的 Tag 物件串列 (但類別名稱是 ResultSet) : 

>>> tags=soup.select('a')   
>>> type(tags)    
<class 'bs4.element.ResultSet'>    
>>> for tag in tags:      
    print(tag)   
    
<a href="https://yhhuang1966.blogspot.com/">超連結</a>
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

如果在 a 後面緊跟著 .sister (表示所有 a 元素有 sister 樣式類別者), 則只有後面 3 個 a 元素會被選擇, 因為第一個 a 元素沒有 class='sister' 屬性 : 

>>> tags=soup.select('a.sister')    
>>> for tag in tags:      
    print(tag)  
    
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

如果使用選擇器 "p > a" 同樣會選擇到最後 3 個 a 元素 (> 表示父子關係), 因為第一個 a 元素是被包在 div 元素而非 p 元素中 : 

 >>> tags=soup.select('p > a')    # 選擇 p 元素的所有 a 子元素
>>> for tag in tags:   
    print(tag)   
    
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

也可以使用包覆最後 3 個 a 元素的 p 元素之 id=p3 先選取 p 元素, 然後再選取所有 a 子元素 :

>>> tags=soup.select('#p3 > a')    # 選擇 id=p3 元素之所有 a 子元素
>>> for tag in tags:       
    print(tag)   
    
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>


(4). 檢視 Tag 物件的成員 : 

以上不論是用 BeautifulSoup 物件的屬性或方法取得的 Tag 物件, 可以用上面自訂的 members.list_members() 函式來檢視這些 Tag 物件的成員 :

>>> from members import list_members   
>>> list_members(soup.p)    
DEFAULT_INTERESTING_STRING_TYPES <class 'tuple'>
EMPTY_ELEMENT_EVENT <class 'object'>
END_ELEMENT_EVENT <class 'object'>
START_ELEMENT_EVENT <class 'object'>
STRING_ELEMENT_EVENT <class 'object'>
append <class 'method'>
attrs <class 'dict'>
can_be_empty_element <class 'bool'>
cdata_list_attributes <class 'dict'>
childGenerator <class 'method'>
children <class 'list_iterator'>
clear <class 'method'>
contents <class 'list'>
css <class 'bs4.css.CSS'>
decode <class 'method'>
decode_contents <class 'method'>
decompose <class 'method'>
decomposed <class 'bool'>
default <class 'object'>
descendants <class 'generator'>
encode <class 'method'>
encode_contents <class 'method'>
extend <class 'method'>
extract <class 'method'>
fetchNextSiblings <class 'method'>
fetchParents <class 'method'>
fetchPrevious <class 'method'>
fetchPreviousSiblings <class 'method'>
find <class 'method'>
findAll <class 'method'>
findAllNext <class 'method'>
findAllPrevious <class 'method'>
findChild <class 'method'>
findChildren <class 'method'>
findNext <class 'method'>
findNextSibling <class 'method'>
findNextSiblings <class 'method'>
findParent <class 'method'>
findParents <class 'method'>
findPrevious <class 'method'>
findPreviousSibling <class 'method'>
findPreviousSiblings <class 'method'>
find_all <class 'method'>
find_all_next <class 'method'>
find_all_previous <class 'method'>
find_next <class 'method'>
find_next_sibling <class 'method'>
find_next_siblings <class 'method'>
find_parent <class 'method'>
find_parents <class 'method'>
find_previous <class 'method'>
find_previous_sibling <class 'method'>
find_previous_siblings <class 'method'>
format_string <class 'method'>
formatter_for_name <class 'method'>
get <class 'method'>
getText <class 'method'>
get_attribute_list <class 'method'>
get_text <class 'method'>
has_attr <class 'method'>
has_key <class 'method'>
hidden <class 'bool'>
index <class 'method'>
insert <class 'method'>
insert_after <class 'method'>
insert_before <class 'method'>
interesting_string_types <class 'tuple'>
isSelfClosing <class 'bool'>
is_empty_element <class 'bool'>
known_xml <class 'bool'>
name <class 'str'>
namespace <class 'NoneType'>
next <class 'bs4.element.NavigableString'>
nextGenerator <class 'method'>
nextSibling <class 'bs4.element.NavigableString'>
nextSiblingGenerator <class 'method'>
next_element <class 'bs4.element.NavigableString'>
next_elements <class 'generator'>
next_sibling <class 'bs4.element.NavigableString'>
next_siblings <class 'generator'>
parent <class 'bs4.element.Tag'>
parentGenerator <class 'method'>
parents <class 'generator'>
parserClass <class 'type'>
parser_class <class 'type'>
prefix <class 'NoneType'>
preserve_whitespace_tags <class 'set'>
prettify <class 'method'>
previous <class 'bs4.element.NavigableString'>
previousGenerator <class 'method'>
previousSibling <class 'bs4.element.NavigableString'>
previousSiblingGenerator <class 'method'>
previous_element <class 'bs4.element.NavigableString'>
previous_elements <class 'generator'>
previous_sibling <class 'bs4.element.NavigableString'>
previous_siblings <class 'generator'>
recursiveChildGenerator <class 'method'>
renderContents <class 'method'>
replaceWith <class 'method'>
replaceWithChildren <class 'method'>
replace_with <class 'method'>
replace_with_children <class 'method'>
select <class 'method'>
select_one <class 'method'>
self_and_descendants <class 'generator'>
setup <class 'method'>
smooth <class 'method'>
string <class 'bs4.element.NavigableString'>
strings <class 'generator'>
stripped_strings <class 'generator'>
text <class 'str'>
unwrap <class 'method'>
wrap <class 'method'>

可見 BeautifulSoup 物件的很多方法, 例如上面用來定位網頁元素的 prettify(), find(), find_all(), select_one(), 與 select() 等方法, 在 Tag 物件中也有, 亦即在擷取網頁內容時, 先用 BeautifulSoup 物件的定位方法找到 Tag 物件後, Tag 物件還可以呼叫這些方法繼續往下走訪語法樹.  

Tag 物件的常用屬性如下表 : 


 Tag 物件常用屬性 說明
 tagName 取得 tagName 標籤之 Tag 物件
 attrs 取得節點之全部屬性 (字典)
 text 取得節點之文字內容 (字串)
 string 取得節點之文字內容 (字串)
 strings 取得節點之所有子孫節點的文字內容 (可迭代生成器)
 contents 取得所有直接子節點 (串列)
 children 取得所有直接子節點 (串列迭代器)
 descendants 取得所有子孫節點 (串列迭代器)
 parent 取得父節點之 Tag 物件 (即包含本身)
 next_sibling 取得後面兄弟節點
 previous_sibling 取得前面兄弟節點
 next_siblings 取得所有後面兄弟節點 (可迭代生成器)
 previous_siblings 取得所有前面兄弟節點 (可迭代生成器)
 next_element 取得後面節點 (不分階層)
 previous_element 取得前面節點 (不分階層) 
 next_elements 取得所有後面節點 (可迭代生成器)
 previous_elements 取得所有前面節點 (可迭代生成器)


Tag 物件的常用方法如下表 : 


 Tag 物件常用方法 說明
 prettify() 重排網頁碼使其符合規格, 傳回 HTML 字串
 find() 以名稱與屬性搜尋標籤, 傳回找到的第一個 Tag 物件
 find_all() 以名稱與屬性搜尋標籤, 傳回全部符合之 Tag 物件串列
 select_one() 功能與 select() 相同, 但只傳回第一個符合之 Tag 物件
 select() 以標籤名稱, id, class 選取標籤, 傳回符合之 Tag 物件串列
 find_parent() 傳回父節點之 Tag 物件
 find_parents() 傳回所有父節點之 Tag 物件串列
 find_previous() 傳回前一節點之 Tag 物件
 find_previous_sibling() 傳回前一個兄弟節點
 find_previous_siblings() 傳回前面所有兄弟節點
 find_next_sibling() 傳回下一個兄弟節點
 find_next_siblings() 傳回後面所有兄弟節點
 get(attr, None) 傳回指定之屬性 (第一參數) 值, 若無傳回預設值 (第二參數)
 get_text() 傳回濾掉本身與所有子孫節之標籤後的文字內容 (字串) 
 get_attribute_list() 傳回屬名名稱串列
 has_attr(attr) 是否含有屬性 attr (傳回 True/False)


參考 : 


沒有留言 :