您現在的位置是:首頁 > 棋牌

Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)

  • 由 python愛好者 發表于 棋牌
  • 2022-03-23
簡介bs = BeautifulSoup(html_code, “lxml”)# 要獲得 BS4 樹上的 Tag 物件,最簡單的方法就是直接使用標籤名

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

遮蔽了底層的差異性,對外提供了統一的操作方法(查詢、遍歷、修改、新增……)。

Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)

認識

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 而言,對於沒有

結束語法結構

的標籤都認為是可以識別的。

from bs4 import BeautifulSouphtml_code = “

”bs = BeautifulSoup(html_code, “html。parser”)print(bs)‘’‘輸出結果’‘’

對於沒有開始語法結構的標籤的處理和

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

物件。

Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)

為了更好的以一個節點找到其它節點,需要理解節點與節點的關係:

主要有父子關係、兄弟關係

現以一個案例逐一理解每一個物件的作用。

案例描述:

爬取豆瓣電影排行榜上的最新電影資訊。

( https://movie。douban。com/chart),並以CSV 文件格式儲存電影資訊。

3。1 查詢目標 Tag

獲取所需資料的關鍵就是要找到

目標 Tag

BS4

提供有豐富多變的方法能幫助開發者快速、靈活找到所需 Tag 物件。透過下面的案例,讓我們感受到它的富裕變化多端的魔力。

先獲取豆瓣電影排行榜的入口頁面路徑 https://movie。douban。com/chart 。

使用谷歌瀏覽器瀏覽頁面,使用瀏覽器提供的開發者工具分析一下頁面中電影資訊的

HTML

程式碼片段。 由簡入深,從下載第一部電影的資訊開始。

Tip:這個排行榜隨時變化,大家所看到的第一部電影和下圖可能不一樣。

Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)

居然使用的是表格佈局。表格佈局非常有規則,這對於分析結構非常有利。

先下載第一部電影的圖片和電影名。圖片當然使用的是 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)

Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)

3。2 過濾方法

得到圖片後,怎麼得到電影的名字,以及其簡介。如下為電影名的程式碼片段。

青春變形記/ 熊抱青春記(港) / 青春養成記(臺)

電影名包含在一個 a 標籤中。如上所述,當使用

bs.標籤名

時,返回的是整個頁面程式碼段中的第一個同名標籤物件。

顯然,第一部電影名所在的 a 標籤不可能是頁面中的第一個(否則就是運氣爆棚了),無法直接使用

bs.a

獲取電影名所在 a 標籤,且此 a 標籤也無特別明顯的可以區分和其它 a 標籤不一樣的特徵。

這裡就要想點其它辦法。以此 a 標籤向上找到其父標籤 div。

青春變形記/ 熊抱青春記(港) / 青春養成記(臺)

2022-03-11(美國網路) / 姜晉安 / 吳珊卓 / 艾娃·摩士 / 麥特里伊·拉瑪克里斯南 / 樸惠仁 / 奧賴恩·李 / 何煒晴 / 特里斯坦·艾瑞克·陳 / 吳漢章 / 菲尼亞斯·奧康奈爾 / 喬丹·費舍 / 託菲爾-恩戈 / 格雷森·維拉紐瓦 / 喬什·列維 / 洛瑞·坦·齊恩。。。

8。2 (45853人評價)

同理,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])

Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)

是時候小結了,使用 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)

Python 從底層結構Beautiful Soup 4(內建豆瓣電影排行爬取案例)

本文主要講解 BS4 的使用,僅爬取了電影排行榜的第一頁資料。至於資料到手後,如何使用,則根據應用場景來決定。

4。 總結

BS4 還提供有很多方法,能根據當前節點找到父親節點、子節點、兄弟節點……但其原理都是一樣的。只要找到了內容所在的標籤(節點)物件,一切也就OK 了。

Top