您現在的位置是:首頁 > 棋牌
Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)
- 由 python愛好者 發表于 棋牌
- 2022-03-23
css搜尋欄怎麼做
1。 前言
什麼是 Beautiful Soup 4 ?
Beautiful Soup 4(簡稱 BS4,後面的 4 表示最新版本)是一個
Python
第三方庫,具有解析
HTML
頁面的功能,爬蟲程式可以使用
BS4
分析頁面無素、精準查找出所需要的頁面資料。有 BS4 的爬蟲程式爬行過程愜意且輕快。
BS4特點是功能強大、使用簡單。相比較只使用正則表示式的費心費力,BS4 有著彈指一揮間的豪邁和瀟灑。
2。 安裝 Beautiful Soup 4
BS4是 Python 第三庫,使用之前需要安裝。
pip install beautifulsoup4
2。1 BS4 的工作原理
要真正認識、掌握 BS4 ,則需要對其底層工作機制有所瞭解。
BS4 查詢頁面資料之前,需要載入
HTML 檔案
或
HTML 片段
,並在記憶體中構建一棵與 HTML 文件完全一一對映的樹形物件(類似於 W3C 的 DOM 解析。為了方便,後面簡稱 BS 樹),這個過程稱為解析。
BS4 自身並沒有提供解析的實現,而是提供了介面,用來對接第三方的解析器(這點是很牛逼的,BS4 具有很好的擴充套件性和開發性)。無論使用何種解析器,
BS4
遮蔽了底層的差異性,對外提供了統一的操作方法(查詢、遍歷、修改、新增……)。
認識
BS4
先從構造
BeautifulSoup
物件開始。
BeautifulSoup
是對整個文件樹的引用,或是進入文件樹的入口物件。
分析
BeautifulSoup
構造方法,可發現在構造
BeautifulSoup
物件時,可以傳遞很多引數。但一般只需要考慮前 2 個引數。其它引數採用預設值,BS4 就能工作很好(約定大於配置的典範)。
def __init__(self, markup=“”, features=None, builder=None, parse_only=None, from_encoding=None, exclude_encodings=None,element_classes=None, **kwargs):
markup:
HTML 文件。可以是字串格式的 HTML 片段、也可以是一個檔案物件。
from bs4 import BeautifulSoup# 使用 HTML 程式碼片段html_code = “
BeautifulSoup 4 簡介
”bs = BeautifulSoup(html_code, “lxml”)print(bs)以下使用檔案物件做為引數。
from bs4 import BeautifulSoupfile = open(“d:/hello。html”, encoding=“utf-8”)bs = BeautifulSoup(file, “lxml”)print(bs)
Tip:使用檔案物件時,編碼方式請選擇 unicode 編碼(utf-8 是 unicode 的具體實現)。
features:指定解析器程式。解析器是 BS4 的靈魂所在,否則 BS4 就是一個無本之源的空殼子。
BS4支援 Python 內建的 HTML 解析器 ,還支援第三方解析器:lxml、 html5lib……
Tip:任何人都可以定製一個自己的解析器,但請務必遵循 BS4 的介面規範。
所以說即使
谷歌瀏覽器
的解析引擎很牛逼,但因和
BS4
介面不吻合,彼此之間也只能惺惺相惜一番。
如果要使用是第三方解析器,使用之前請提前安裝:
安裝 lxml :
pip install lxml
安裝 html5lib:
pip install html5lib
幾種解析器的縱橫比較:
解析器
使用方法
優勢
劣勢
Python標準庫
BeautifulSoup(markup, “html。parser”)
執行速度適中
文件容錯能力強
Python 2。7。3 or 3。2。2 前的版本文件容錯能力差
lxml HTML 解析器
BeautifulSoup(markup, “lxml”)
速度快
文件容錯能力強
需要 C 語言庫的支援
lxml XML 解析器
BeautifulSoup(markup, [“lxml-xml”]) BeautifulSoup(markup, “xml”)
速度快
唯一支援 XML 的解析器
需要 C 語言庫的支援
html5lib
BeautifulSoup(markup, “html5lib”)
最好的容錯性
以瀏覽器的方式解析文件
生成HTML5格式的文件
速度慢
不依賴外部擴充套件
每一種解析器都有自己的優點,如
html5lib
的容錯性就非常好,但一般優先使用
lxml
解析器,更多時候速度更重要。
2。2 解析器的差異性
解析器的功能是載入 HTML(XML) 程式碼,在記憶體中構建一棵層次分明的物件樹(後面簡稱 BS 樹)。雖然
BS4
從應用層面統一了各種解析器的使用規範,但各有自己的底層實現邏輯。
當然,解析器在解析格式正確、完全符合 HTML 語法規範的文件時,除了速度上的差異性,大家表現的還是可圈可點的。想想,這也是它們應該提供的最基礎功能。
但是,當文件格式不標準時,不同的解析器在解析時會遵循自己的底層設計,會弱顯出差異性。
看來,
BS4
也無法掌管人家底層邏輯的差異性。
2。2。1 lxml
使用
lxml
解析HTML程式碼段。
from bs4 import BeautifulSouphtml_code = “ ”bs = BeautifulSoup(html_code, “lxml”)print(bs)‘’‘輸出結果
lxml在解析時,會自動新增上
html、body
標籤。並自動補全沒有
結束語法結構
的標籤。 如上 a 標籤是後面 2 個標籤的父標籤,第一個 p 標籤是第二 p 標籤的為兄弟關係。
使用
lxml
解析如下HTML 程式碼段。
from bs4 import BeautifulSouphtml_code = “
”bs = BeautifulSoup(html_code, “lxml”)print(bs)‘’‘輸出結果’‘’lxml會認定
只有結束語法沒有開始語法
的標籤結構是非法的,拒絕解析(也是挺剛的)。即使是非法,丟棄是理所當然的。
2。2。2 html5lib
使用
html5lib
解析不完整的HTML程式碼段。
from bs4 import BeautifulSouphtml_code = “ ”bs = BeautifulSoup(html_code, “html5lib”)print(bs)‘’‘輸出結果
html5lib在解析j時,會自動加上 html、head、body 標籤。 除此之外如上解析結果和
lxml
沒有太大區別,在沒有結束標籤語法上,大家還是英雄所見略同的。
使用
html5lib
解析下面的HTML 程式碼段。
from bs4 import BeautifulSouphtml_code = “
”bs = BeautifulSoup(html_code, “html5lib”)print(bs)‘’‘輸出結果:’‘’html5lib對於沒有
結束語法結構
的標籤,會為其補上
開始語法結構
,
html5lib
遵循的是 HTML5 的部分標準。意思是既然都來了,也就不要走了,html5lib 都會盡可能補全。
2。2。3 pyhton 內建解析器
from bs4 import BeautifulSouphtml_code = “ ”bs = BeautifulSoup(html_code, “html。parser”)print(bs)‘’‘輸出結果’‘’ 與前面 2 類解析器相比較,沒有新增 、、 任一標籤,會自動補全結束標籤結構。但最終結構與前 2 類解析器不同。a 標籤是後 2 個標籤的父親,第一個 p 標籤是第二個 p 標籤的父親,而不是兄弟關係。 歸納可知:對於 lxml、html5lib、html。parser 而言,對於沒有 結束語法結構 的標籤都認為是可以識別的。
對於沒有開始語法結構的標籤的處理和
lxml
解析器相似,會丟棄掉。
從上面的程式碼的執行結果可知,
html5lib
的容錯能力是最強的,在對於文件要求不高的場景下,可考慮使用
html5lib
。在對文件格式要求高的應用場景下,可選擇
lxml
。
3。 BS4 樹物件
BS4 記憶體樹是對
HTML
文件或程式碼段的記憶體對映,記憶體樹由 4 種類型的 python 物件組成。分別是
BeautifulSoup
、
Tag
、
NavigableString
和
Comment
。
BeautifulSoup物件是對整個
html
文件結構的對映,提供對整個
BS4
樹操作的全域性方法和屬性。也是入口物件。
class
BeautifulSoup
(Tag):
pass
Tag物件(標籤物件)是對
HTML
文件中標籤的對映,或稱其為節點(
物件名與標籤名一樣
)物件,提供對頁面標籤操作的方法和屬性。本質上
BeautifulSoup
物件也
Tag
物件。
Tip:解析頁面資料的關鍵,便是找到包含內容的
標籤物件(Tag)
。
BS4
提供了很多靈活、簡潔的方法。
使用
BS4
就是以
BeautifulSoup
物件開始,逐步查詢目標標籤物件的過程。
NavigableString物件是對 HTML 標籤中所包含的內容體的對映,提供有對文字資訊操作的方法和屬性。
Tip:對於開發者而言,分析頁面,最終就要要獲取資料,所以,掌握此物件的方法和屬性尤為重要。
使用 標籤物件的 string 屬性就可以獲取。
Comment是對文件註釋內容的對映物件。此物件用的不多。
再總結一下:使用
BS4
的的關鍵就是如何以一個
Tag
物件(節點物件)為參考,找到與其關聯的其它
Tag 物件
。剛開始出場時就一個
BeautifulSoup
物件。
為了更好的以一個節點找到其它節點,需要理解節點與節點的關係:
主要有父子關係、兄弟關係
。
現以一個案例逐一理解每一個物件的作用。
案例描述:
爬取豆瓣電影排行榜上的最新電影資訊。
( https://movie。douban。com/chart),並以CSV 文件格式儲存電影資訊。
3。1 查詢目標 Tag
獲取所需資料的關鍵就是要找到
目標 Tag
。
BS4
提供有豐富多變的方法能幫助開發者快速、靈活找到所需 Tag 物件。透過下面的案例,讓我們感受到它的富裕變化多端的魔力。
先獲取豆瓣電影排行榜的入口頁面路徑 https://movie。douban。com/chart 。
使用谷歌瀏覽器瀏覽頁面,使用瀏覽器提供的開發者工具分析一下頁面中電影資訊的
HTML
程式碼片段。 由簡入深,從下載第一部電影的資訊開始。
Tip:這個排行榜隨時變化,大家所看到的第一部電影和下圖可能不一樣。
居然使用的是表格佈局。表格佈局非常有規則,這對於分析結構非常有利。
先下載第一部電影的圖片和電影名。圖片當然使用的是 img 標籤,使用 BS4 解析後,
BS4 樹
上會有一個對應的
img Tag
物件。
樹上的 img Tag 物件有很多,怎麼找到第一部電影的圖片標籤?
from bs4 import BeautifulSoupimport requests# 伺服器地址url = “https://movie。douban。com/chart”# 偽裝成瀏覽器headers = { ‘User-Agent’: ‘Mozilla/5。0 (Windows NT 10。0; Win64; x64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/99。0。4844。51 Safari/537。36’}# 傳送請求resp = requests。get(url, headers=headers)html_code = resp。text# 得到 BeautifulSoup 物件。萬里長征的第一步。bs = BeautifulSoup(html_code, “lxml”)# 要獲得 BS4 樹上的 Tag 物件,最簡單的方法就是直接使用標籤名。簡單的不要不要的。img_tag = bs。img# 返回的是 BS4 樹上的第一個 img Tag 物件print(type(img_tag))print(img_tag)‘’‘輸出結果
這裡有一個運氣成分,
bs.img
返回的恰好是第一部電影的圖片標籤(也意味著第一部電影的圖片標籤是整個頁面的第一個圖片標籤)。
找到了
img 標籤物件
,再分析出其圖片路徑就容易多了,圖片路徑儲存在 img 標籤的 src 屬性中,現在只需要獲取到 img 標籤物件的 src 屬性值就可以了。
Tag物件提供有
attrs
屬性,可以很容易得到一個
Tag
物件的任一屬性值。
使用語法:
Tag[“屬性名”]或者使用 Tag。attrs 獲取到 Tag 物件的所有屬性。
下面使用
atts
獲取標籤物件的所有屬性資訊,返回的是一個
python
字典物件。
# 省略上面程式碼段img_tag_attrs = img_tag。attrsprint(img_tag_attrs)‘’‘輸出結果:以字典格式返回 img Tag 物件的所有屬性{’src‘: ’https://img1。doubanio。com/view/photo/s_ratio_poster/public/p2670448229。jpg‘, ’width‘: ’75‘, ’alt‘: ’青春變形記‘, ’class‘: []}’‘’
單值屬性返回的是單值,因 class 屬性(多值屬性)可以設定多個類樣式,返回的是一個數組。現在只想得到圖片的路徑,可以使用如下方式。
img_tag_attrs = img_tag。attrs# 第一種方案img_tag_alt="Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)" data-isLoading="0" src="/static/img/blank.gif" data-src=img_tag_attrs[“src”] # 第二種方案img_tag_src = img_tag[“src”]print(img_tag_src)‘’‘輸出結果https://img1。doubanio。com/view/photo/s_ratio_poster/public/p2670448229。jpg’‘’
上述程式碼中提供 2 種方案,其本質是一樣的。有了圖片路徑,剩下的事情就好辦了。
完整的程式碼:
from bs4 import BeautifulSoupimport requests# 伺服器地址url = “https://movie。douban。com/chart”# 偽裝成瀏覽器headers = { ‘User-Agent’: ‘Mozilla/5。0 (Windows NT 10。0; Win64; x64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/99。0。4844。51 Safari/537。36’}# 傳送請求resp = requests。get(url, headers=headers)html_code = resp。textbs = BeautifulSoup(html_code, “lxml”)img_tag = bs。img# img_tag_attrs = img_tag。attrs# img_tag_alt="Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)" data-isLoading="0" src="/static/img/blank.gif" data-src=img_tag_attrs[“src”]img_tag_src = img_tag[“src”]# 根據圖片路徑下載圖片並儲存到本地img_resp = requests。get(img_tag_src, headers=headers)with open(“D:/movie/movie01。jpg”, “wb”) as f: f。write(img_resp。content)
3。2 過濾方法
得到圖片後,怎麼得到電影的名字,以及其簡介。如下為電影名的程式碼片段。
電影名包含在一個 a 標籤中。如上所述,當使用
bs.標籤名
時,返回的是整個頁面程式碼段中的第一個同名標籤物件。
顯然,第一部電影名所在的 a 標籤不可能是頁面中的第一個(否則就是運氣爆棚了),無法直接使用
bs.a
獲取電影名所在 a 標籤,且此 a 標籤也無特別明顯的可以區分和其它 a 標籤不一樣的特徵。
這裡就要想點其它辦法。以此 a 標籤向上找到其父標籤 div。
2022-03-11(美國網路) / 姜晉安 / 吳珊卓 / 艾娃·摩士 / 麥特里伊·拉瑪克里斯南 / 樸惠仁 / 奧賴恩·李 / 何煒晴 / 特里斯坦·艾瑞克·陳 / 吳漢章 / 菲尼亞斯·奧康奈爾 / 喬丹·費舍 / 託菲爾-恩戈 / 格雷森·維拉紐瓦 / 喬什·列維 / 洛瑞·坦·齊恩。。。
同理,div 標籤在整個頁面程式碼中也有很多,又如何獲到到電影名所在的 div 標籤,分析發現此 div 有一個與其它 div 不同的屬性特徵。
class="pl2"
。 可以透過這個屬性特徵對 div 標籤進行過濾。
什麼是過濾方法?
過濾方法是 BS4 Tag 標籤物件的方法,用來對其子節點進行篩選。
BS4提供有
find( )、find_all( )
等過濾方法。此類方法的作用如其名可以在一個群體(所有子節點)中根據個體的特徵進行篩選。
Tip:如果使用
BeautifulSoup物件
呼叫這類方法,則是對整個
BS4
樹上的節點進行篩選。
如果以某一個具體的 Tag 標籤物件呼叫此類方法以,則是對 Tag 標籤下的子節點進行篩選。
find()和 find_all( )方法的引數是一樣的。兩者的區別:前者搜尋到第一個滿足條件就返回,後者會搜尋所有滿足條件的物件。
find_all( name , attrs , recursive , string , **kwargs )find( name , attrs , recursive , string , **kwargs )
引數說明
name:
可以是標籤名、正則表示式、列表、布林值或一個自定義方法。變化多端。
# 標籤名:查詢頁面中的第一個 div 標籤物件div_tag = bs。find(“div”)# 正則表示式:搜尋所有以 d 開始的標籤div_tag = bs。find_all(re。compile(“^d”))# 列表:查詢 div 或 a 標籤div_tag = bs。find_all([“div”,“a”])# 布林值:查詢所有子節點bs。find_all(True)#自定義方法:搜尋有 class 屬性而沒有 id 屬性的標籤物件。def has_class_but_no_id(tag): return tag。has_attr(‘class’) and not tag。has_attr(‘id’)bs。find_all(has_class_but_no_id)
attrs:
可以接收一個字典型別。以鍵、值對的方式描述要搜尋的標籤物件的屬性特徵。
# 在整個樹結果中查詢 class 屬性值是 pl2 的標籤物件div_tag = bs。find(attrs={“class”: “pl2”})
Tip:使用此屬性時,可以結合 name 引數把範圍收窄。
div_tag = bs。find(“div”,attrs={“class”: “pl2”})
查詢 class 屬性值是 pl2 的 div 標籤物件。
string 引數:
此引數可以是 字串、正則表示式、列表 布林值。透過標籤內容匹配查詢。
# 搜尋標籤內容是‘青春’ 2 字開頭的 span 標籤物件div_tag = bs。find_all(“span”, string=re。compile(r“青春。*”))
limit 引數:可以使用
limit
引數限制返回結果的數量。
recursive 引數:是否遞迴查詢節點下面的子節點,預設 是
True
,設定
False
時,只查詢直接子節點。
簡單介紹過濾方法後,重新回到問題上來,查詢第一部電影的電影名、簡介。靈活使用過濾方法,則能很輕鬆搜尋到所需要的標籤物件。
from bs4 import BeautifulSoupimport requests# 伺服器地址url = “https://movie。douban。com/chart”# 偽裝成瀏覽器headers = { ‘User-Agent’: ‘Mozilla/5。0 (Windows NT 10。0; Win64; x64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/99。0。4844。51 Safari/537。36’}# 傳送請求resp = requests。get(url, headers=headers)html_code = resp。text# 使得解析器構建 BeautifulSoup 物件bs = BeautifulSoup(html_code, “lxml”)# 使用過濾方法在整個樹結構中查詢 class 屬性值為 pl2 的 div 物件。其實有多個,這裡查詢第一個div_tag = bs。find(“div”, class_=“pl2”)# 查詢 div 標籤物件下的第一個 a 標籤div_a = div_tag。find(“a”)# 得到 a 標籤下所有子節點name = div_a。contents# 得到 文字print(name[0]。replace(“/”, ‘’)。strip())‘’‘輸出結果:青春變形記’‘’
程式碼分析:
使用 bs。find(“div”, class_=“pl2”) 方法搜尋到包含第一部電影的 div 標籤。
電影名包含在 div 標籤的子標籤 a 中,繼續使用 div_tag。find(“a”) 找到 a 標籤。
a 標籤中的內容就是電影名。
BS4
為標籤物件提供有
string
屬性,可以獲取其內容,返回
NavigableString
物件。但是如果標籤中既有文字又有子標籤時, 則不能使用 string 屬性。如上 a 標籤的 string 返回為 None。
在
BS4
樹結構中文字也是節點,可以以子節點的方式獲取。標籤物件有
contents
和
children
屬性獲取子節點。前者返回一個列表,後者返回一個迭代器。另有
descendants
可以獲取其直接子節點和孫子節點。
使用
contents
屬性,從返回的列表中獲取第一個子節點,即文字節點。文字節點沒有 string 屬性。
獲取電影簡介相對而言就簡單的多,其內容包含在 div 標籤的 p 子標籤中。
# 獲取電影的簡介div_p = div_tag。find(“p”)movie_desc = div_p。string。strip()print(movie_desc)
下面可以把電影名和電影簡介以 CSV 的方式儲存在檔案中。完整程式碼:
from bs4 import BeautifulSoupimport requestsimport csv# 伺服器地址url = “https://movie。douban。com/chart”# 偽裝成瀏覽器headers = { ‘User-Agent’: ‘Mozilla/5。0 (Windows NT 10。0; Win64; x64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/99。0。4844。51 Safari/537。36’}# 傳送請求resp = requests。get(url, headers=headers)html_code = resp。textbs = BeautifulSoup(html_code, “lxml”)div_tag = bs。find(“div”, class_=“pl2”)div_a = div_tag。find(“a”)div_a_name = div_a。contents# 電影名movie_name = div_a_name[0]。replace(“/”, ‘’)。strip()# 獲取電影的簡介div_p = div_tag。find(“p”)movie_desc = div_p。string。strip()with open(“d:/movie/movies。csv”, “w”, newline=‘’) as f: csv_writer = csv。writer(f) csv_writer。writerow([“電影名”, “電影簡介”]) csv_writer。writerow([movie_name, movie_desc])
是時候小結了,使用 BS4 的基本流程:
透過指定解析器獲取到 BS4 物件。
指定一個標籤名獲取到標籤物件。如果無法直接獲取所需要的標籤物件,則使用過濾器方法進行一層一層向下過濾。
找到目標標籤物件後,可以使用
string
屬性獲取其中的文字,或使用
atrts
獲取屬性值。
使用獲取到的資料。
3。3 遍歷所有的目標
如上僅僅是找到了第一部電影的資訊。如果需要查詢到所有電影資訊,則只需要在上面程式碼的基礎之上新增迭代便可。
from bs4 import BeautifulSoupimport requestsimport csvall_movies = []# 伺服器地址url = “https://movie。douban。com/chart”# 偽裝成瀏覽器headers = { ‘User-Agent’: ‘Mozilla/5。0 (Windows NT 10。0; Win64; x64) AppleWebKit/537。36 (KHTML, like Gecko) Chrome/99。0。4844。51 Safari/537。36’}# 傳送請求resp = requests。get(url, headers=headers)html_code = resp。textbs = BeautifulSoup(html_code, “lxml”)# 查詢到所有
div_tag = bs。find_all(“div”, class_=“pl2”)for div in div_tag: div_a = div。find(“a”) div_a_name = div_a。contents # 電影名 movie_name = div_a_name[0]。replace(“/”, ‘’)。strip() # 獲取電影的簡介 div_p = div。find(“p”) movie_desc = div_p。string。strip() all_movies。append([movie_name, movie_desc])with open(“d:/movie/movies。csv”, “w”, newline=‘’) as f: csv_writer = csv。writer(f) csv_writer。writerow([“電影名”, “電影簡介”]) for movie in all_movies: csv_writer。writerow(movie)本文主要講解 BS4 的使用,僅爬取了電影排行榜的第一頁資料。至於資料到手後,如何使用,則根據應用場景來決定。
4。 總結
BS4 還提供有很多方法,能根據當前節點找到父親節點、子節點、兄弟節點……但其原理都是一樣的。只要找到了內容所在的標籤(節點)物件,一切也就OK 了。