python
crawler
beautifulsoup
Beautiful Soup是Python中用來解析HTML、XML標籤文件的模組,並能修復含有未閉合標籤等錯誤的文件(此種文件常被稱為tag soup);解析後會為這個頁面建立一個BeautifulSoup
物件,這個物件中包含了整個頁面的結構樹,透過這個BeautifulSoup
物件的結構樹,就可以輕鬆的提取頁面內任何有興趣的資料了。
Python 3.x版本可以使用 pip3
安裝:
如果是Python 2.x版本,可以這樣安裝:
下面範例為建立一個解析HTML文件的BeautifulSoup
物件,解析完成後會產生一個soup
物件,這個就是該HTML的結構樹物件,之後所有資料的搜尋、提取等操作都是透過這個物件來進行。
解析器名稱 | 使用範例 | 說明 |
---|---|---|
Python’s html.parser | BeautifulSoup(markup, "html.parser") |
Python內建 |
lxml’s HTML parser | BeautifulSoup(markup, "lxml") |
速度較快的HTML解析器 |
lxml’s XML parser | BeautifulSoup(markup, "lxml-xml") BeautifulSoup(markup, "xml") |
速度較快的XML解析器 |
html5lib | BeautifulSoup(markup, "html5lib") |
解析後建立HTML 5物件 |
補充
lxml解析器需要另外安裝:
prettify()
呼叫BeautifulSoup
物件的prettify
方法可以輸出排版過後的HTML文件,在開發時可以用來觀察其文件的整個輪廓:
會輸出:
直接指定網頁標題標籤的名稱(例如:title
),即可將該標籤的節點抓出來:
會輸出包含標籤的文字:
透過 string
屬性,可以取得標籤內的純文字:
會輸出:
find_all()
透過 find_all
可以在文件內找出指定的所有標籤,find_all
會以list
傳回所有找到的標籤:
會輸出:
注意:
因為第二個p標籤內含有子標籤,所以如果直接使用
soup.string
只會得到None
,這時就必須使用getText()
方法來取得文字。
get()
方法透過 get()
方法,可以取得標籤內的屬性值,下面透過get()
方法來取得a
標籤內的href
屬性:
會輸出:
attrs
也可以透過attrs
屬性可以拿到標籤內全部的屬性,該屬性為一個list
,例:
會得到data-table
的屬性值
如果要同時搜尋多種HTML標籤,可以使用 list 來指定所有要搜尋的標籤名稱:
會輸出:
find_all
預設會搜尋所有符合條件的節點,如果遇到文件較大或是標籤數量很多的時候,就會執行比較多的時間,如果在應用上不需要用到全部標籤,可以用 limit
參數來限制搜尋的標籤上限值,這樣一來,就只會找出前幾個符合條件的標籤:
會輸出:
如果只需要抓出第一個符合條件的節點,可以直接使用 find()
方法即可:
會輸出:
在預設的狀況下,find_all()
方法會以遞迴的方式 往下尋找所有的子標籤,例如搜尋html
標籤下所有h1標籤,包含子標籤下的標籤:
會輸出:
上面程式碼,加上recursive=False
後,表示只會搜尋在html
標籤下第一層的標籤:
會輸出:
因為
h1
標籤是在html
標籤下的body
標籤內,也就是底下兩層,所以不會被搜尋到。
透過指定標籤內的屬性名稱,可以將所有符合屬性值的所有標籤找出來,例如搜尋 id
屬性為 article
的節點:
會輸出:
可以透過結合標籤名稱名稱與屬性名稱進行更精確的搜尋,例如搜尋 id
屬性為 article
的 h2
節點:
會輸出:
補充:
以屬性做為條件來搜尋時,也可以一次給多個屬性條件來篩選,例如:
`tags = soup.find_all(id='article', name='title')
在HTML5中有些屬性名稱中含有符號,例如 data-
開頭的屬性名稱如果直接寫在Python的方法中的話會出現錯誤:
會出現下面錯誤:
解決方法為,將屬性名稱與值放進一個字典(dict)中,再將此字典設定給 attrs
參數即可,例如:
以正規表示法來搜尋:
說明:
只要href屬性開頭為
https://www.aaronlife.com
都會被搜尋出來。
因為class
是Python程式語言的保留字,所以在Beautiful Soup內改用 class_
這個名稱來代表HTML標籤內的class屬性,例如要搜尋class為banner
的標籤:
會輸出:
補充:
- 一個class屬性可以有多個屬性值,在做
class_
屬性執比對時,只要有一個符合就算成功。- 也可以直接拿完整的class屬性值字串比對,但如果順序不對就會失敗,例如:
"banner primary"
和"primary banner"
會是不一樣的屬性值,這個情況下會建議用CSS選擇器來進行比對,例如:soup.select('h1.banner.primary')
或soup.select('*.banner.primary')
搜尋多個屬性值:
搜尋多個屬性值需要使用list將所有要搜尋的值放在一起。
使用 find_all
配合 string
參數可以根據文字進行來搜尋特定的標籤內容,例如:
會輸出:
注意:
輸出結果只會有內容,而不是完整的標籤。
find_all
和find
方法都是從外層向下搜尋子標籤,但我們也可以由下往上來搜尋上層標籤(或稱父標籤),方法find_parents
(如同find_all
網上搜尋全部符合條件的標籤)和find_parent
(如同find
指搜尋第一個符合的標籤)例如:
會輸出:
如果想要搜尋的標籤在同一層,則可以使用:
find_previous_siblings()
(全部符合的)和 find_previous_sibling()
(第一個符合的):往前搜尋。find_next_siblings()
(全部符合的)和 find_next_sibling()
(第一個符合的):往後搜尋。例如:
方法名稱 | 說明 |
---|---|
select() |
以CSS選擇器的方式來找出所有符合條件的元素 |
select_one() |
以CSS選擇器的方式來定位第一筆符合條件的元素 |
select()
select_one()
使用方式與select()一樣,直接回傳符合條件的元素,而非list。
如果我們想要用 Beautiful Soup 解析已經下載的 HTML 檔案,可以直接將開啟的檔案交給 BeautifulSoup
處理:
會輸出:
備註:
須先將最上面的HTML文件範例存成index.html檔案,並和這個範例的程式碼檔案放在同一層目錄下。
要直接從網路抓取html文件,可以使用Python內建的request
模組: