BeautifulSoup 的使用
我們學習了正則表達式的相關用法,但是一旦正則寫的有問題,可能得到的就不是我們想要的結果了,而且對于一個網(wǎng)頁來說,都有一定的特殊的結構和層級關系,而且很多標簽都有 id 或 class 來對作區(qū)分,所以我們借助于它們的結構和屬性來提取不也是可以的嗎?
所以,這一節(jié)我們就介紹一個強大的解析工具,叫做 BeautiSoup,它就是借助網(wǎng)頁的結構和屬性等特性來解析網(wǎng)頁的工具,有了它我們不用再去寫一些復雜的正則,只需要簡單的幾條語句就可以完成網(wǎng)頁中某個元素的提取。
廢話不多說,接下來我們就來感受一下 BeautifulSoup 的強大之處吧。
BeautifulSoup 簡介
簡單來說,BeautifulSoup 就是 Python 的一個 HTML 或 XML 的解析庫,我們可以用它來方便地從網(wǎng)頁中提取數(shù)據(jù),官方的解釋如下:
BeautifulSoup 提供一些簡單的、python 式的函數(shù)用來處理導航、搜索、修改分析樹等功能。它是一個工具箱,通過解析文檔為用戶提供需要抓取的數(shù)據(jù),因為簡單,所以不需要多少代碼就可以寫出一個完整的應用程序。BeautifulSoup 自動將輸入文檔轉換為 Unicode 編碼,輸出文檔轉換為 utf-8 編碼。你不需要考慮編碼方式,除非文檔沒有指定一個編碼方式,這時你僅僅需要說明一下原始編碼方式就可以了。BeautifulSoup 已成為和 lxml、html6lib 一樣出色的 python 解釋器,為用戶靈活地提供不同的解析策略或強勁的速度。
所以說,利用它我們可以省去很多繁瑣的提取工作,提高解析效率。
安裝
使用之前,我們當然需要首先說明一下它的安裝方式。目前 BeautifulSoup 的最新版本是 4.x 版本,之前的版本已經(jīng)停止開發(fā)了,推薦使用 pip 來安裝,安裝命令如下:
pip3 install beautifulsoup4
好,安裝完成之后可以驗證一下,寫一段 Python 程序試驗一下。
from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello</p>', 'html.parser')print(soup.p.string)
運行結果
Hello
如果沒有報錯,則證明安裝沒有問題。
解析器
BeautifulSoup 在解析的時候實際上是依賴于解析器的,它除了支持 Python 標準庫中的 HTML 解析器,還支持一些第三方的解析器比如 lxml,下面我們對 BeautifulSoup 支持的解析器及它們的一些優(yōu)缺點做一個簡單的對比。
解析器使用方法優(yōu)勢劣勢
Python 標準庫 BeautifulSoup (markup, “html.parser”) Python 的內置標準庫、執(zhí)行速度適中 、文檔容錯能力強 Python 2.7.3 or 3.2.2) 前的版本中文容錯能力差
lxml HTML 解析器 BeautifulSoup (markup, “lxml”) 速度快、文檔容錯能力強需要安裝 C 語言庫
lxml XML 解析器 BeautifulSoup (markup, “xml”) 速度快、唯一支持 XML 的解析器需要安裝 C 語言庫
html5libBeautifulSoup (markup, “html5lib”) 最好的容錯性、以瀏覽器的方式解析文檔、生成 HTML5 格式的文檔速度慢、不依賴外部擴展
所以通過以上對比可以看出,lxml 這個解析器有解析 HTML 和 XML 的功能,而且速度快,容錯能力強,所以推薦使用這個庫來進行解析,但是這里的劣勢是必須安裝一個 C 語言庫,它叫做 lxml,我們在這里依然使用 pip 安裝即可,命令如下:
pip3 install lxml
安裝完成之后,我們就可以使用 lxml 這個解析器來解析了,在初始化的時候我們可以把第二個參數(shù)改為 lxml,如下:
from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello</p>', 'lxml')
print(soup.p.string)
運行結果是完全一致的。
基本使用
下面我們首先用一個實例來感受一下 BeautifulSoup 的基本使用:
html = """<html><head><title>The Dormouse's story</title></head><body><p class="title" name="dromouse"><b>The Dormouse'
s story</b></p><p class="story">Once upon a time there were three little sisters; and their names were<a href="http
://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,<a class="sister"
id="link2">Lacie</a> and<a class="sister" id="link3">Tillie</a>;and they lived at the
bottom of a well.</p><p class="story">...</p>"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.prettify())
print(soup.title.string)
運行結果
<html> <head> <title> The Dormouse's story </title> </head> <body> <p class="title" name="dromouse"> <b>
The Dormouse's story </b> </p> <p class="story"> Once upon a time there were three little sisters; and their
names were <a class="sister" id="link1"> <!-- Elsie --> </a> ,
<a class="sister" id="link2"> Lacie </a> and <a class="sister" href="
http://example.com/tillie" id="link3"> Tillie </a> ;and they lived at the bottom of a well. </p>
<p class="story"> ... </p> </body></html>The Dormouse's story
首先我們聲明了一個變量 html,它是一個 HTML 字符串,但是注意到,它并不是一個完整的 HTML 字符串, 和 標簽都沒有閉合,但是我們將它當作第一個參數(shù)傳給 BeautifulSoup 對象,第二個參數(shù)傳入的是解析器的類型,在這里我們使用 lxml,這樣就完成了 BeaufulSoup 對象的初始化,將它賦值給 soup 這個變量。
那么接下來我們就可以通過調用 soup 的各個方法和屬性對這串 HTML 代碼解析了。
我們首先調用了 prettify () 方法,這個方法可以把要解析的字符串以標準的縮進格式輸出,在這里注意到輸出結果里面包含了 和 標簽,也就是說對于不標準的 HTML 字符串 BeautifulSoup 可以自動更正格式,這一步實際上不是由 prettify () 方法做的,這個更正實際上在初始化 BeautifulSoup 時就完成了。
然后我們調用了 soup.title.string,這個實際上是輸出了 HTML 中
標簽選擇器
剛才我們選擇元素的時候直接通過調用標簽的名稱就可以選擇節(jié)點元素了,然后再調用 string 屬性就可以得到標簽內的文本了,這種選擇方式速度非常快,如果單個標簽結構話層次非常清晰,可以選用這種方式來解析。
選擇元素
下面我們再用一個例子詳細說明一下它的選擇方法。
html = """<html><head><title>The Dormouse's story</title></head><body><p class="title" name="dromouse"><b>The Dormouse'
s story</b></p><p class="story">Once upon a time there were three little sisters; and their names were<a href="http:
//example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,<a class="sister"
id="link2">Lacie</a> and<a class="sister" id="link3">Tillie</a>;and they lived at
the bottom of a well.</p><p class="story">...</p>"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(soup.head)
print(soup.p)
運行結果
<title>The Dormouse's story</title><class 'bs4.element.Tag'>The Dormouse's story<head><title>
The Dormouse's story</title></head><p class="title" name="dromouse"><b>The Dormouse's story</b></p>
在這里我們依然選用了剛才的 HTML 代碼,我們首先打印輸出了 title 標簽的選擇結果,輸出結果正是 title 標簽加里面的文字內容。接下來輸出了它的類型,是 bs4.element.Tag 類型,這是 BeautifulSoup 中的一個重要的數(shù)據(jù)結構,經(jīng)過選擇器選擇之后,選擇結果都是這種 Tag 類型,它具有一些屬性比如 string 屬性,調用 Tag 的 string 屬性,就可以得到節(jié)點的文本內容了,所以接下來的輸出結果正是節(jié)點的文本內容。
接下來我們又嘗試選擇了 head 標簽,結果也是標簽加其內部的所有內容,再接下來選擇了 p 標簽,不過這次情況比較特殊,我們發(fā)現(xiàn)結果是第一個 p 標簽的內容,后面的幾個 p 標簽并沒有選擇到,也就是說,當有多個標簽時,這種選擇方式只會選擇到第一個匹配的標簽,其他的后面的標簽都會忽略。
提取信息
在上面我們演示了調用 string 屬性來獲取文本的值,那我們要獲取標簽屬性值怎么辦呢?獲取標簽名怎么辦呢?下面我們來統(tǒng)一梳理一下信息的提取方式
獲取名稱
可以利用 name 屬性來獲取標簽的名稱。還是以上面的文本為例,我們選取 title 標簽,然后調用 name 屬性就可以得到標簽名稱。
print(soup.title.name)
運行結果
title
屬性獲取
每個標簽可能有多個屬性,比如 id,class 等等,我們選擇到這個節(jié)點元素之后,可以調用 attrs 獲取所有屬性。
print(soup.p.attrs)print(soup.p.attrs['name'])
運行結果
{'class': ['title'], 'name': 'dromouse'}dromouse