摘要:解析器在解析的時(shí)候?qū)嶋H上是依賴于解析器的,它除了支持標(biāo)準(zhǔn)庫中的解析器,還支持一些第三方的解析器比如,下面我們對(duì)支持的解析器及它們的一些優(yōu)缺點(diǎn)做一個(gè)簡單的對(duì)比。
上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---28、解析庫的使用:XPath
下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---30、解析庫的使用:PyQuery
前面我們介紹了正則表達(dá)式的相關(guān)用法,但是一旦正則寫的有問題,可能得到的就不是我們想要的結(jié)果了,而且對(duì)于一個(gè)網(wǎng)頁來說,都有一定的特殊的結(jié)構(gòu)和層級(jí)關(guān)系,而且很多節(jié)點(diǎn)都有id或class來對(duì)作區(qū)分,所以我們借助于它們的結(jié)構(gòu)和屬性來提取不也是可以的嗎?
所以,這一節(jié)我們就介紹一個(gè)強(qiáng)大的解析工具,叫做 BeautiSoup,它就是借助網(wǎng)頁的結(jié)構(gòu)和屬性等特性來解析網(wǎng)頁的工具,有了它我們不用再去寫一些復(fù)雜的正則,只需要簡單的幾條語句就可以完成網(wǎng)頁中某個(gè)元素的提取。
廢話不多說,接下來我們就來感受一下 BeautifulSoup 的強(qiáng)大之處吧。
1. BeautifulSoup簡介簡單來說,BeautifulSoup 就是 Python 的一個(gè) HTML 或 XML 的解析庫,我們可以用它來方便地從網(wǎng)頁中提取數(shù)據(jù),官方的解釋如下:
BeautifulSoup提供一些簡單的、Python式的函數(shù)用來處理導(dǎo)航、搜索、修改分析樹等功能。它是一個(gè)工具箱,通過解析文檔為用戶提供需要抓取的數(shù)據(jù),因?yàn)楹唵?,所以不需要多少代碼就可以寫出一個(gè)完整的應(yīng)用程序。 BeautifulSoup 自動(dòng)將輸入文檔轉(zhuǎn)換為 Unicode 編碼,輸出文檔轉(zhuǎn)換為 utf-8 編碼。你不需要考慮編碼方式,除非文檔沒有指定一個(gè)編碼方式,這時(shí)你僅僅需要說明一下原始編碼方式就可以了。 BeautifulSoup 已成為和 lxml、html6lib 一樣出色的 Python 解釋器,為用戶靈活地提供不同的解析策略或強(qiáng)勁的速度。
所以說,利用它我們可以省去很多繁瑣的提取工作,提高解析效率。
2. 準(zhǔn)備工作在開始之前請(qǐng)確保已經(jīng)正確安裝好了 BeautifulSoup 和 LXML,如沒有安裝可以參考第一章的安裝過程。
3. 解析器BeautifulSoup 在解析的時(shí)候?qū)嶋H上是依賴于解析器的,它除了支持 Python 標(biāo)準(zhǔn)庫中的 HTML 解析器,還支持一些第三方的解析器比如 LXML,下面我們對(duì) BeautifulSoup 支持的解析器及它們的一些優(yōu)缺點(diǎn)做一個(gè)簡單的對(duì)比。
解析器 | 使用方法 | 優(yōu)勢(shì) | 劣勢(shì) |
---|---|---|---|
Python標(biāo)準(zhǔn)庫 | BeautifulSoup(markup, "html.parser") | Python的內(nèi)置標(biāo)準(zhǔn)庫、執(zhí)行速度適中 、文檔容錯(cuò)能力強(qiáng) | Python 2.7.3 or 3.2.2)前的版本中 中文容錯(cuò)能力差 |
LXML HTML 解析器 | BeautifulSoup(markup, "lxml") | 速度快、文檔容錯(cuò)能力強(qiáng) | 需要安裝C語言庫 |
LXML XML 解析器 | BeautifulSoup(markup, "xml") | 速度快、唯一支持XML的解析器 | 需要安裝C語言庫 |
html5lib | BeautifulSoup(markup, "html5lib") | 最好的容錯(cuò)性、以瀏覽器的方式解析文檔、生成 HTML5 格式的文檔 | 速度慢、不依賴外部擴(kuò)展 |
所以通過以上對(duì)比可以看出,LXML 這個(gè)解析器有解析 HTML 和 XML 的功能,而且速度快,容錯(cuò)能力強(qiáng),所以推薦使用這個(gè)解析器來進(jìn)行解析。
使用 LXML 這個(gè)解析器,在初始化 BeautifulSoup 的時(shí)候我們可以把第二個(gè)參數(shù)改為 lxml 即可,如下:
from bs4 import BeautifulSoup soup = BeautifulSoup("Hello
", "lxml") print(soup.p.string)
后面 BeautifulSoup 的用法實(shí)例也統(tǒng)一用這個(gè)解析器來演示。
4# . 基本使用
下面我們首先用一個(gè)實(shí)例來感受一下 BeautifulSoup 的基本使用:
html = """The Dormouse"s story The Dormouse"s story
Once upon a time there were three little sisters; and their names were , Lacie and Tillie; and they lived at the bottom of a well.
...
""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.prettify()) print(soup.title.string)
運(yùn)行結(jié)果:
The Dormouse"s story The Dormouse"s story
Once upon a time there were three little sisters; and their names were , Lacie and Tillie ; and they lived at the bottom of a well.
...
The Dormouse"s story
首先我們聲明了一個(gè)變量 html,它是一個(gè) HTML 字符串,但是注意到,它并不是一個(gè)完整的 HTML 字符串,body 和 html 節(jié)點(diǎn)都沒有閉合,但是我們將它當(dāng)作第一個(gè)參數(shù)傳給 BeautifulSoup 對(duì)象,第二個(gè)參數(shù)傳入的是解析器的類型,在這里我們使用 lxml,這樣就完成了 BeaufulSoup 對(duì)象的初始化,將它賦值給 soup 這個(gè)變量。
那么接下來我們就可以通過調(diào)用 soup 的各個(gè)方法和屬性對(duì)這串 HTML代碼解析了。
我們首先調(diào)用了 prettify() 方法,這個(gè)方法可以把要解析的字符串以標(biāo)準(zhǔn)的縮進(jìn)格式輸出,在這里注意到輸出結(jié)果里面包含了 body 和 html 節(jié)點(diǎn),也就是說對(duì)于不標(biāo)準(zhǔn)的 HTML 字符串 BeautifulSoup 可以自動(dòng)更正格式,這一步實(shí)際上不是由 prettify() 方法做的,這個(gè)更正實(shí)際上在初始化 BeautifulSoup 時(shí)就完成了。
然后我們調(diào)用了 soup.title.string ,這個(gè)實(shí)際上是輸出了 HTML 中 title 節(jié)點(diǎn)的文本內(nèi)容。所以 soup.title 就可以選擇出 HTML 中的 title 節(jié)點(diǎn),再調(diào)用 string 屬性就可以得到里面的文本了,所以我們就可以通過簡單地調(diào)用幾個(gè)屬性就可以完成文本的提取了,是不是非常方便?
5. 節(jié)點(diǎn)選擇器剛才我們選擇元素的時(shí)候直接通過調(diào)用節(jié)點(diǎn)的名稱就可以選擇節(jié)點(diǎn)元素了,然后再調(diào)用 string 屬性就可以得到節(jié)點(diǎn)內(nèi)的文本了,這種選擇方式速度非???,如果單個(gè)節(jié)點(diǎn)結(jié)構(gòu)話層次非常清晰,可以選用這種方式來解析。
選擇元素下面我們?cè)儆靡粋€(gè)例子詳細(xì)說明一下它的選擇方法:
html = """The Dormouse"s story The Dormouse"s story
Once upon a time there were three little sisters; and their names were , Lacie and Tillie; and they lived at the bottom of a well.
...
""" 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)
運(yùn)行結(jié)果:
The Dormouse"s story The Dormouse"s story The Dormouse"s story The Dormouse"s story
在這里我們依然選用了剛才的 HTML 代碼,我們首先打印輸出了 title 節(jié)點(diǎn)的選擇結(jié)果,輸出結(jié)果正是 title 節(jié)點(diǎn)加里面的文字內(nèi)容。接下來輸出了它的類型,是 bs4.element.Tag 類型,這是 BeautifulSoup 中的一個(gè)重要的數(shù)據(jù)結(jié)構(gòu),經(jīng)過選擇器選擇之后,選擇結(jié)果都是這種 Tag 類型,它具有一些屬性比如 string 屬性,調(diào)用 Tag 的 string 屬性,就可以得到節(jié)點(diǎn)的文本內(nèi)容了,所以接下來的輸出結(jié)果正是節(jié)點(diǎn)的文本內(nèi)容。
接下來我們又嘗試選擇了 head 節(jié)點(diǎn),結(jié)果也是節(jié)點(diǎn)加其內(nèi)部的所有內(nèi)容,再接下來選擇了 p 節(jié)點(diǎn),不過這次情況比較特殊,我們發(fā)現(xiàn)結(jié)果是第一個(gè) p 節(jié)點(diǎn)的內(nèi)容,后面的幾個(gè) p 節(jié)點(diǎn)并沒有選擇到,也就是說,當(dāng)有多個(gè)節(jié)點(diǎn)時(shí),這種選擇方式只會(huì)選擇到第一個(gè)匹配的節(jié)點(diǎn),其他的后面的節(jié)點(diǎn)都會(huì)忽略。
提取信息在上面我們演示了調(diào)用 string 屬性來獲取文本的值,那我們要獲取節(jié)點(diǎn)屬性值怎么辦呢?獲取節(jié)點(diǎn)名怎么辦呢?下面我們來統(tǒng)一梳理一下信息的提取方式
獲取名稱可以利用 name 屬性來獲取節(jié)點(diǎn)的名稱。還是以上面的文本為例,我們選取 title 節(jié)點(diǎn),然后調(diào)用 name 屬性就可以得到節(jié)點(diǎn)名稱。
print(soup.title.name)
運(yùn)行結(jié)果:
title獲取屬性
每個(gè)節(jié)點(diǎn)可能有多個(gè)屬性,比如 id,class 等等,我們選擇到這個(gè)節(jié)點(diǎn)元素之后,可以調(diào)用 attrs 獲取所有屬性。
print(soup.p.attrs) print(soup.p.attrs["name"])
運(yùn)行結(jié)果:
{"class": ["title"], "name": "dromouse"} dromouse
可以看到 attrs 的返回結(jié)果是字典形式,把選擇的節(jié)點(diǎn)的所有屬性和屬性值組合成一個(gè)字典,接下來如果要獲取 name 屬性,就相當(dāng)于從字典中獲取某個(gè)鍵值,只需要用中括號(hào)加屬性名稱就可以得到結(jié)果了,比如獲取 name 屬性就可以通過 attrs["name"] 得到相應(yīng)的屬性值。
其實(shí)這樣的寫法還有點(diǎn)繁瑣,還有一種更簡單的獲取方式,我們可以不用寫 attrs,直接節(jié)點(diǎn)元素后面加中括號(hào),傳入屬性名就可以達(dá)到屬性值了,樣例如下:
print(soup.p["name"]) print(soup.p["class"])
運(yùn)行結(jié)果:
dromouse ["title"]
在這里注意到有的返回結(jié)果是字符串,有的返回結(jié)果是字符串組成的列表。比如 name 屬性的值是唯一的,返回的結(jié)果就是單個(gè)字符串,而對(duì)于 class,一個(gè)節(jié)點(diǎn)元素可能由多個(gè) class,所以返回的是列表,所以在實(shí)際處理過程中要注意判斷類型。
獲取內(nèi)容可以利用 string 屬性獲取節(jié)點(diǎn)元素包含的文本內(nèi)容,比如上面的文本我們獲取第一個(gè) p 節(jié)點(diǎn)的文本:
print(soup.p.string)
運(yùn)行結(jié)果:
The Dormouse"s story
再次注意一下這里選擇到的 p 節(jié)點(diǎn)是第一個(gè) p 節(jié)點(diǎn),獲取的文本也就是第一個(gè) p 節(jié)點(diǎn)里面的文本。
嵌套選擇在上面的例子中我們知道每一個(gè)返回結(jié)果都是 bs4.element.Tag 類型,它同樣可以繼續(xù)調(diào)用節(jié)點(diǎn)進(jìn)行下一步的選擇,比如我們獲取了 head 節(jié)點(diǎn)元素,我們可以繼續(xù)調(diào)用 head 來選取其內(nèi)部的 head 節(jié)點(diǎn)元素。
html = """The Dormouse"s story """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.head.title) print(type(soup.head.title)) print(soup.head.title.string)
運(yùn)行結(jié)果:
The Dormouse"s story The Dormouse"s story
第一行結(jié)果是我們調(diào)用了 head 之后再次調(diào)用了 title 來選擇的 title 節(jié)點(diǎn)元素,然后我們緊接著打印輸出了它的類型,可以看到它仍然是 bs4.element.Tag 類型,也就是說我們?cè)?Tag 類型的基礎(chǔ)上再次選擇得到的依然還是 Tag 類型,每次返回的結(jié)果都相同,所以這樣我們就可以這樣做嵌套的選擇了。
最后輸出了一下它的 string 屬性,也就是節(jié)點(diǎn)里的文本內(nèi)容。
關(guān)聯(lián)選擇我們?cè)谧鲞x擇的時(shí)候有時(shí)候不能做到一步就可以選擇到想要的節(jié)點(diǎn)元素,有時(shí)候在選擇的時(shí)候需要先選中某一個(gè)節(jié)點(diǎn)元素,然后以它為基準(zhǔn)再選擇它的子節(jié)點(diǎn)、父節(jié)點(diǎn)、兄弟節(jié)點(diǎn)等等。所以在這里我們就介紹下如何來選擇這些節(jié)點(diǎn)元素。
子節(jié)點(diǎn)和子孫節(jié)點(diǎn)選取到了一個(gè)節(jié)點(diǎn)元素之后,如果想要獲取它的直接子節(jié)點(diǎn)可以調(diào)用 contents 屬性,我們用一個(gè)實(shí)例來感受一下:
print(soup.p.contents)
運(yùn)行結(jié)果:
[The Dormouse"s story]
contents 屬性得到的結(jié)果是直接子節(jié)點(diǎn)的列表。
同樣地我們可以調(diào)用 children 屬性,得到相應(yīng)的結(jié)果:
print(soup.p.children) for i,child in enumerate(soup.p.children): print(child)
運(yùn)行結(jié)果:
The Dormouse"s story
還是同樣的 HTML 文本,在這里我們調(diào)用了 children 屬性來進(jìn)行選擇,返回結(jié)果可以看到是生成器類型,所以接下來我們用 for 循環(huán)輸出了一下相應(yīng)的內(nèi)容,內(nèi)容其實(shí)是一樣的,只不過 children 返回的是生成器類型,而 contents 返回的是列表類型。
如果我們要得到所有的子孫節(jié)點(diǎn)的話可以調(diào)用 descendants 屬性:
print(soup.p.descendants) for i,child in enumerate(soup.p.descendants): print(child)
運(yùn)行結(jié)果:
The Dormouse"s story The Dormouse"s story
返回結(jié)果還是生成器,遍歷輸出一下可以看到descendants 會(huì)遞歸地查詢所有子節(jié)點(diǎn),得到的是所有的子孫節(jié)點(diǎn)。
父節(jié)點(diǎn)和祖先節(jié)點(diǎn)如果要獲取某個(gè)節(jié)點(diǎn)元素的父節(jié)點(diǎn),可以調(diào)用 parent 屬性:
html = """The Dormouse"s story Once upon a time there were three little sisters; and their names were Elsie
...
""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.a.parent)
運(yùn)行結(jié)果:
Once upon a time there were three little sisters; and their names were Elsie
在這里我們選擇的是第一個(gè) a 節(jié)點(diǎn)的父節(jié)點(diǎn)元素,很明顯它的父節(jié)點(diǎn)是 p 節(jié)點(diǎn),輸出結(jié)果便是 p 節(jié)點(diǎn)及其內(nèi)部的內(nèi)容。
注意到這里輸出的僅僅是 a 節(jié)點(diǎn)的直接父節(jié)點(diǎn),而沒有再向外尋找父節(jié)點(diǎn)的祖先節(jié)點(diǎn),如果我們要想獲取所有的祖先節(jié)點(diǎn),可以調(diào)用 parents 屬性:
html = """ """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(type(soup.a.parents)) print(list(enumerate(soup.a.parents)))
運(yùn)行結(jié)果:
[(0, ), (1, ), (2, ), (3, )]
返回結(jié)果是一個(gè)生成器類型,我們?cè)谶@里用列表輸出了它的索引和內(nèi)容,可以發(fā)現(xiàn)列表中的元素就是 a 節(jié)點(diǎn)的祖先節(jié)點(diǎn)。
兄弟節(jié)點(diǎn)上面說明了子節(jié)點(diǎn)和父節(jié)點(diǎn)的獲取方式,如果要獲取同級(jí)的節(jié)點(diǎn)也就是兄弟節(jié)點(diǎn)應(yīng)該怎么辦?我們先用一個(gè)實(shí)例來感受一下:
html = """Once upon a time there were three little sisters; and their names were Elsie Hello Lacie and Tillie and they lived at the bottom of a well.
""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print("Next Sibling", soup.a.next_sibling) print("Prev Sibling", soup.a.previous_sibling) print("Next Siblings", list(enumerate(soup.a.next_siblings))) print("Prev Siblings", list(enumerate(soup.a.previous_siblings)))
運(yùn)行結(jié)果:
Next Sibling Hello Prev Sibling Once upon a time there were three little sisters; and their names were Next Siblings [(0, " Hello "), (1, Lacie), (2, " and "), (3, Tillie), (4, " and they lived at the bottom of a well. ")] Prev Siblings [(0, " Once upon a time there were three little sisters; and their names were ")]
可以看到在這里我們調(diào)用了四個(gè)不同的屬性,next_sibling 和 previous_sibling 分別可以獲取節(jié)點(diǎn)的下一個(gè)和上一個(gè)兄弟元素,next_siblings 和 previous_siblings 則分別返回所有前面和后面的兄弟節(jié)點(diǎn)的生成器。
提取信息在上面我們講解了關(guān)聯(lián)元素節(jié)點(diǎn)的選擇方法,如果我們想要獲取它們的一些信息,比如文本、屬性等等也是同樣的方法。
html = """Once upon a time there were three little sisters; and their names were BobLacie
""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print("Next Sibling:") print(type(soup.a.next_sibling)) print(soup.a.next_sibling) print(soup.a.next_sibling.string) print("Parent:") print(type(soup.a.parents)) print(list(soup.a.parents)[0]) print(list(soup.a.parents)[0].attrs["class"])
運(yùn)行結(jié)果:
Next Sibling:Lacie Lacie Parent: Once upon a time there were three little sisters; and their names were BobLacie
["story"]
如果返回結(jié)果是單個(gè)節(jié)點(diǎn),那么可以直接調(diào)用 string、attrs 等屬性來獲得其文本和屬性,如果返回結(jié)果是多個(gè)節(jié)點(diǎn)的生成器,則可以轉(zhuǎn)為列表后取出某個(gè)元素,然后再調(diào)用 string、attrs 等屬性來獲取其對(duì)應(yīng)節(jié)點(diǎn)等文本和屬性。
6. 方法選擇器前面我們所講的選擇方法都是通過屬性來選擇元素的,這種選擇方法非??欤侨绻M(jìn)行比較復(fù)雜的選擇的話則會(huì)比較繁瑣,不夠靈活。所以 BeautifulSoup 還為我們提供了一些查詢的方法,比如 find_all()、find() 等方法,我們可以調(diào)用方法然后傳入相應(yīng)等參數(shù)就可以靈活地進(jìn)行查詢了。
最常用的查詢方法莫過于 find_all() 和 find() 了,下面我們對(duì)它們的用法進(jìn)行詳細(xì)的介紹。
find_all()find_all,顧名思義,就是查詢所有符合條件的元素,可以給它傳入一些屬性或文本來得到符合條件的元素,功能十分強(qiáng)大。
它的API如下:
find_all(name , attrs , recursive , text , **kwargs)name
我們可以根據(jù)節(jié)點(diǎn)名來查詢?cè)?,下面我們用一個(gè)實(shí)例來感受一下:
html="""""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.find_all(name="ul")) print(type(soup.find_all(name="ul")[0]))Hello
- Foo
- Bar
- Jay
- Foo
- Bar
運(yùn)行結(jié)果:
[
在這里我們調(diào)用了 find_all() 方法,傳入了一個(gè) name 參數(shù),參數(shù)值為 ul,也就是說我們想要查詢所有 ul 節(jié)點(diǎn),返回結(jié)果是列表類型,長度為 2,每個(gè)元素依然都是 bs4.element.Tag 類型。
因?yàn)槎际?Tag 類型,所以我們依然可以進(jìn)行嵌套查詢,還是同樣的文本,在這里我們查詢出所有 ul 節(jié)點(diǎn)后再繼續(xù)查詢其內(nèi)部的 li 節(jié)點(diǎn)。
for ul in soup.find_all(name="ul"): print(ul.find_all(name="li"))
運(yùn)行結(jié)果:
[
返回結(jié)果是列表類型,列表中的每個(gè)元素依然還是 Tag 類型。
接下來我們就可以遍歷每個(gè) li 獲取它的文本了。
for ul in soup.find_all(name="ul"): print(ul.find_all(name="li")) for li in ul.find_all(name="li"): print(li.string)
運(yùn)行結(jié)果:
[
除了根據(jù)節(jié)點(diǎn)名查詢,我們也可以傳入一些屬性來進(jìn)行查詢,我們用一個(gè)實(shí)例感受一下:
html="""""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.find_all(attrs={"id": "list-1"})) print(soup.find_all(attrs={"name": "elements"}))Hello
- Foo
- Bar
- Jay
- Foo
- Bar
運(yùn)行結(jié)果:
[
在這里我們查詢的時(shí)候傳入的是 attrs 參數(shù),參數(shù)的類型是字典類型,比如我們要查詢 id 為 list-1 的節(jié)點(diǎn),那就可以傳入attrs={"id": "list-1"} 的查詢條件,得到的結(jié)果是列表形式,包含的內(nèi)容就是符合 id 為 list-1 的所有節(jié)點(diǎn),上面的例子中符合條件的元素個(gè)數(shù)是 1,所以結(jié)果是長度為 1 的列表。
對(duì)于一些常用的屬性比如 id、class 等,我們可以不用 attrs 來傳遞,比如我們要查詢 id 為 list-1 的節(jié)點(diǎn),我們可以直接傳入 id 這個(gè)參數(shù),還是上面的文本,我們換一種方式來查詢。
from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.find_all(id="list-1")) print(soup.find_all(class_="element"))
運(yùn)行結(jié)果:
[
在這里我們直接傳入 id="list-1" 就可以查詢 id 為 list-1 的節(jié)點(diǎn)元素了。而對(duì)于 class 來說,由于 class 在 python 里是一個(gè)關(guān)鍵字,所以在這里后面需要加一個(gè)下劃線,class_="element",返回的結(jié)果依然還是 Tag 組成的列表。
texttext 參數(shù)可以用來匹配節(jié)點(diǎn)的文本,傳入的形式可以是字符串,可以是正則表達(dá)式對(duì)象,我們用一個(gè)實(shí)例來感受一下:
import re html=""" """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.find_all(text=re.compile("link")))
運(yùn)行結(jié)果:
["Hello, this is a link", "Hello, this is a link, too"]
在這里有兩個(gè) a 節(jié)點(diǎn),其內(nèi)部包含有文本信息,在這里我們調(diào)用 find_all() 方法傳入 text 參數(shù),參數(shù)為正則表達(dá)式對(duì)象,結(jié)果會(huì)返回所有匹配正則表達(dá)式的節(jié)點(diǎn)文本組成的列表。
find()除了 find_all() 方法,還有 find() 方法,只不過 find() 方法返回的是單個(gè)元素,也就是第一個(gè)匹配的元素,而 find_all() 返回的是所有匹配的元素組成的列表。
html="""""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.find(name="ul")) print(type(soup.find(name="ul"))) print(soup.find(class_="list"))Hello
- Foo
- Bar
- Jay
- Foo
- Bar
運(yùn)行結(jié)果:
返回結(jié)果不再是列表形式,而是第一個(gè)匹配的節(jié)點(diǎn)元素,類型依然是 Tag 類型。
另外還有許多的查詢方法,用法與前面介紹的 find_all()、find() 方法完全相同,只不過查詢范圍不同,在此做一下簡單的說明。
find_parents() find_parent()
find_parents() 返回所有祖先節(jié)點(diǎn),find_parent() 返回直接父節(jié)點(diǎn)。
find_next_siblings() find_next_sibling()
find_next_siblings() 返回后面所有兄弟節(jié)點(diǎn),find_next_sibling() 返回后面第一個(gè)兄弟節(jié)點(diǎn)。
find_previous_siblings() find_previous_sibling()
find_previous_siblings() 返回前面所有兄弟節(jié)點(diǎn),find_previous_sibling() 返回前面第一個(gè)兄弟節(jié)點(diǎn)。
find_all_next() find_next()
find_all_next() 返回節(jié)點(diǎn)后所有符合條件的節(jié)點(diǎn), find_next() 返回第一個(gè)符合條件的節(jié)點(diǎn)。
find_all_previous() 和 find_previous()
find_all_previous() 返回節(jié)點(diǎn)后所有符合條件的節(jié)點(diǎn), find_previous() 返回第一個(gè)符合條件的節(jié)點(diǎn)
7. CSS選擇器BeautifulSoup 還提供了另外一種選擇器,那就是 CSS 選擇器,如果對(duì) Web 開發(fā)熟悉對(duì)話,CSS 選擇器肯定也不陌生,如果不熟悉的話,可以看一下:http://www.w3school.com.cn/cs...。
使用 CSS 選擇器,只需要調(diào)用 select() 方法,傳入相應(yīng)的 CSS 選擇器即可,我們用一個(gè)實(shí)例來感受一下:
html="""""" from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") print(soup.select(".panel .panel-heading")) print(soup.select("ul li")) print(soup.select("#list-2 .element")) print(type(soup.select("ul")[0]))Hello
- Foo
- Bar
- Jay
- Foo
- Bar
運(yùn)行結(jié)果:
[] [Hello
在這里我們用了三次 CSS 選擇器,返回的結(jié)果均是符合 CSS 選擇器的節(jié)點(diǎn)組成的列表。例如 select("ul li") 則是選擇所有 ul 節(jié)點(diǎn)下面的所有 li 節(jié)點(diǎn),結(jié)果便是所有的 li 節(jié)點(diǎn)組成的列表。
最后一句我們打印輸出了列表中元素的類型,可以看到類型依然是 Tag 類型。
嵌套選擇select() 方法同樣支持嵌套選擇,例如我們先選擇所有 ul 節(jié)點(diǎn),再遍歷每個(gè) ul 節(jié)點(diǎn)選擇其 li 節(jié)點(diǎn),樣例如下:
from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") for ul in soup.select("ul"): print(ul.select("li"))
運(yùn)行結(jié)果:
[
可以看到正常輸出了遍歷每個(gè) ul 節(jié)點(diǎn)之后,其下的所有 li 節(jié)點(diǎn)組成的列表。
獲取屬性我們知道節(jié)點(diǎn)類型是 Tag 類型,所以獲取屬性還是可以用原來的方法獲取,仍然是上面的 HTML 文本,我們?cè)谶@里嘗試獲取每個(gè) ul 節(jié)點(diǎn)的 id 屬性。
from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") for ul in soup.select("ul"): print(ul["id"]) print(ul.attrs["id"])
運(yùn)行結(jié)果:
list-1 list-1 list-2 list-2
可以看到直接傳入中括號(hào)和屬性名和通過 attrs 屬性獲取屬性值都是可以成功的。
獲取文本那么獲取文本當(dāng)然也可以用前面所講的 string 屬性,還有一個(gè)方法那就是 get_text(),同樣可以獲取文本值。
from bs4 import BeautifulSoup soup = BeautifulSoup(html, "lxml") for li in soup.select("li"): print("Get Text:", li.get_text()) print("String:", li.string)
運(yùn)行結(jié)果:
Get Text: Foo String: Foo Get Text: Bar String: Bar Get Text: Jay String: Jay Get Text: Foo String: Foo Get Text: Bar String: Bar
二者的效果是完全一致的,都可以獲取到節(jié)點(diǎn)的文本值。
8. 結(jié)語到此 BeautifulSoup 的使用介紹基本就結(jié)束了,最后做一下簡單的總結(jié):
推薦使用 LXML 解析庫,必要時(shí)使用 html.parser。
節(jié)點(diǎn)選擇篩選功能弱但是速度快。
建議使用 find()、find_all() 查詢匹配單個(gè)結(jié)果或者多個(gè)結(jié)果。
如果對(duì) CSS 選擇器熟悉的話可以使用 select() 選擇法。
上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---28、解析庫的使用:XPath
下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---30、解析庫的使用:PyQuery
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/44083.html
摘要:運(yùn)行結(jié)果如果運(yùn)行結(jié)果一致則證明安裝成功。上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)請(qǐng)求庫安裝下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)數(shù)據(jù)庫的安裝 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---2、請(qǐng)求庫安裝:GeckoDriver、PhantomJS、Aiohttp下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---數(shù)據(jù)庫的安裝:MySQL、MongoDB、Redis 抓取下網(wǎng)頁代碼之后,下一步就是從網(wǎng)頁中提取信息,提取信息的方式有...
摘要:父節(jié)點(diǎn)我們可以用方法來獲取某個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn),我們用一個(gè)實(shí)例來感受一下運(yùn)行結(jié)果在這里我們首先用選取了為的節(jié)點(diǎn),然后調(diào)用了方法,得到其父節(jié)點(diǎn),類型依然是類型。 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---29、解析庫的使用:BeautifulSoup下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---31、數(shù)據(jù)存儲(chǔ):文件存儲(chǔ) 在上一節(jié)我們介紹了 BeautifulSoup 的使用,它是一個(gè)非常強(qiáng)...
摘要:的安裝是一個(gè)輕量級(jí)的關(guān)系型數(shù)據(jù)庫,以表的形式來存儲(chǔ)數(shù)據(jù),本節(jié)我們來了解下它的安裝方式。相關(guān)鏈接官方網(wǎng)站下載地址中文教程下的安裝推薦使用安裝,執(zhí)行命令即可。上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)解析庫的安裝下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)存儲(chǔ)庫的安裝 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---3、解析庫的安裝:LXML、BeautifulSoup、PyQuery、Tesserocr下一篇文章:Python3網(wǎng)絡(luò)...
摘要:上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)請(qǐng)求庫安裝下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)解析庫的安裝的安裝在上一節(jié)我們了解了的配置方法,配置完成之后我們便可以用來驅(qū)動(dòng)瀏覽器來做相應(yīng)網(wǎng)頁的抓取。上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)請(qǐng)求庫安裝下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)解析庫的安裝 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---1、請(qǐng)求庫安裝:Requests、Selenium、ChromeDriver下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)--...
摘要:上一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)與正則表達(dá)式抓取貓眼電影排行下一篇文章網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)解析庫的使用上一節(jié)我們實(shí)現(xiàn)了一個(gè)最基本的爬蟲,但提取頁面信息時(shí)我們使用的是正則表達(dá)式,用過之后我們會(huì)發(fā)現(xiàn)構(gòu)造一個(gè)正則表達(dá)式還是比較的繁瑣的,而且萬一有一點(diǎn)地 上一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---27、Requests與正則表達(dá)式抓取貓眼電影排行下一篇文章:Python3網(wǎng)絡(luò)爬蟲實(shí)戰(zhàn)---29、解析庫的使用:...
閱讀 5066·2021-09-07 09:58
閱讀 791·2019-08-30 15:55
閱讀 2929·2019-08-30 15:55
閱讀 926·2019-08-30 15:53
閱讀 1559·2019-08-29 12:57
閱讀 1821·2019-08-26 13:46
閱讀 568·2019-08-26 11:00
閱讀 3668·2019-08-23 15:42