小白學 Python 爬蟲(21):解析庫 Beautiful Soup(上)

小白學 Python 爬蟲(21):解析庫 Beautiful Soup(上)

人生苦短,我用 Python


如果我的文章對您有幫助,請關注支持下作者的公眾號:極客挖掘機,您的關注,是對小編堅持原創的最大鼓勵:)

前文傳送門:

小白學 Python 爬蟲(1):開篇

小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝

小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門

小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門

小白學 Python 爬蟲(5):前置準備(四)數據庫基礎

小白學 Python 爬蟲(6):前置準備(五)爬蟲框架的安裝

小白學 Python 爬蟲(7):HTTP 基礎

小白學 Python 爬蟲(8):網頁基礎

小白學 Python 爬蟲(9):爬蟲基礎

小白學 Python 爬蟲(10):Session 和 Cookies

小白學 Python 爬蟲(11):urllib 基礎使用(一)

小白學 Python 爬蟲(12):urllib 基礎使用(二)

小白學 Python 爬蟲(13):urllib 基礎使用(三)

小白學 Python 爬蟲(14):urllib 基礎使用(四)

小白學 Python 爬蟲(15):urllib 基礎使用(五)

小白學 Python 爬蟲(16):urllib 實戰之爬取妹子圖

小白學 Python 爬蟲(17):Requests 基礎使用

小白學 Python 爬蟲(18):Requests 進階操作

小白學 Python 爬蟲(19):Xpath 基操

小白學 Python 爬蟲(20):Xpath 進階

引言

首先當然是各種資料地址敬上:

  • 官方網站:https://www.crummy.com/software/BeautifulSoup/
  • 官方文檔:https://www.crummy.com/software/BeautifulSoup/bs4/doc/
  • 中文文檔:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/

先看下官方對自己的介紹:

Beautiful Soup 提供一些簡單的、 Python 式的函數來處理導航、搜索、修改分析樹等功能。它是一個工具箱,通過解析文檔為用戶提供需要抓取的數據,因為簡單,所以不需要多少代碼就可以寫出一個完整的應用程序。

Beautiful Soup 自動將輸入文檔轉換為 Unicode 編碼,輸出文檔轉換為 UTF-8 編碼。你不需要考慮編碼方式,除非文檔沒有指定一個編碼方式,這時你僅僅需要說明一下原始編碼方式就可以了。

Beautiful Soup 已成為和 lxml 、 html6lib 一樣出色的 Python 解釋器,為用戶靈活地提供不同的解析策略或強勁的速度。

講人話就是 Beautiful Soup 是一個非常好用、速度又快的 HTML 或 XML 的解析庫。

Beautiful Soup 在解析時實際上依賴解析器,它除了支持Python標準庫中的HTML解析器外,還支持一些第三方解析器。下表列出了主要的解析器,以及它們的優缺點(以下內容來自:https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/# ):

解析器使用方法優勢劣勢Python 標準庫BeautifulSoup(markup, "html.parser")Python的內置標準庫、執行速度適中、文檔容錯能力強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語言庫html5libBeautifulSoup(markup, "html5lib")最好的容錯性、以瀏覽器的方式解析文檔、生成HTML5格式的文檔速度慢、不依賴外部擴展

推薦使用 lxml 作為解析器,因為效率更高。在 Python2.7.3 之前的版本和 Python3 中 3.2.2 之前的版本,必須安裝 lxml 或 html5lib ,因為那些 Python 版本的標準庫中內置的 HTML 解析方法不夠穩定。

提示: 如果一段 HTML 或 XML 文檔格式不正確的話,那麼在不同的解析器中返回的結果可能是不一樣的,查看 解析器之間的區別 瞭解更多細節。

基本操作

爬取對象還是小編的個人博客(小編看著博客的流量在暗暗心痛)。最基本的,還是先打印首頁的 HTML 源碼,使用的類庫為 Requests + bs4。

<code>

import

requests from bs4

import

BeautifulSoup response = requests.

get

('https: soup =

BeautifulSoup

(response.content,

"html5lib"

)

print

(soup.prettify()) /<code>

結果就不貼了,太長,浪費大家翻頁的時間。

首先先解釋一下這裡為什麼選擇了 html5lib 的解析器而不是 lxml 的解析器,因為經過小編測試 lxml 的解析器無法解析某些 HTML 標籤,經過小編的測試,使用 Python 標準庫或者 html5lib 解析器都無此問題,所以這裡選擇使用 Python 標準庫。

上面這段代碼主要是調用了 prettify() ,這個方法的主要作用是把要解析的字符串以標準的縮進格式輸出。值得注意的是,這裡的輸出會自動更正 HTML 的格式,但是這一步並不是由 prettify() 這個方法來做的,而是在初始化 BeautifulSoup 時就完成了。

節點選擇

我們使用 Beautiful Soup 的目的是什麼?當然是要選擇我們需要的節點,並從節點中提取出我們需要的數據。

Beautiful Soup 將複雜 HTML 文檔轉換成一個複雜的樹形結構,每個節點都是 Python 對象,所有對象可以歸納為4種: Tag , NavigableString , BeautifulSoup , Comment 。

我們直接調用節點的名稱就可以選擇節點元素,再調用 string 屬性就可以得到節點內的文本了,這種選擇方式速度非常快。

<code>

print

(soup.title)

print

(

type

(soup.title))

print

(soup.title.

string

)

print

(soup.a) /<code>

結果如下:

<code> 

<

title

>

極客挖掘機

title

>

<

class

'

bs4.element.Tag

'>

極客挖掘機

<

a

class

=

"logo"

href

=

"/"

>

<

img

src

=

"/favicon.jpg"

style

=

"margin-right: 10px;"

/>

極客挖掘機

a

>

/<code>

可以看到,我們這裡直接輸出的 title 節點,它的類型是 bs4.element.Tag ,並且使用 string 屬性,直接得到了該節點的內容。

這裡我們打印了 a 節點,可以看到,只打印出來了第一個 a 節點,後面的節點並未打印,說明當有多個節點時,這種方式只能獲得第一個節點。

獲取名稱

每個 tag 都有自己的名字,通過 .name 來獲取:

<code>tag = soup.section

print

(tag.name) /<code>

結果如下:

<code>

section

/<code>

獲取屬性

一個 tag 可能有很多個屬性, tag 的屬性的操作方法與字典相同:

<code>

print

(tag[

'class'

]) /<code>

結果如下:

<code>

[

'content-wrap'

]

/<code>

也可以直接”點”取屬性, 比如: .attrs :

<code>

print

(tag.attrs) /<code>

結果如下:

<code>{

'class'

: [

'content-wrap'

]} /<code>

獲取內容

可以利用 string 屬性獲取節點元素包含的文本內容,比如要獲取 title 標籤的內容:

<code>

print

(

soup

.title

.string

) /<code>

結果如下:

<code>極客挖掘機
/<code>

嵌套選擇

在上面的示例中,我們的信息都是從通過 tag 的屬性獲得的,當然 tag 是可以繼續嵌套的選擇下去,比如我們剛才獲取了第一個 a 標籤,我們可以繼續獲取其中的 img 標籤:

<code>

print

(

soup

.a

.img

)

print

(

type

(

soup

.a

.img

))

print

(

soup

.a

.img

.attrs

) /<code>

結果如下:

<code>"/favicon.jpg" style= 

"margin-right: 10px;"

/> <

class

'

bs4

.

element

.

Tag

'>

{

'src'

:

'/favicon.jpg'

,

'style'

:

'margin-right: 10px;'

} /<code>

可以看到我們在 a 標籤上繼續選擇的 img 標籤,它的類型依然是 bs4.element.Tag ,並且我們成功的獲取了 img 標籤的屬性值,也就是說,我們在 Tag 類型的基礎上再次選擇得到的依然還是 Tag 類型,所以這樣就可以做嵌套選擇了。

關聯選擇

在選擇節點的時候,我們很少可以一步到位,直接選到所需要的節點,這就需要我們先選中其中的某一個節點,再已它為基準,再選擇它的子節點、父節點或者兄弟節點。

子節點

獲取子節點,我們可以選擇使用 contents 屬性,示例如下:

<code>

print

(

soup

.article

.contents

) /<code>

結果太長了,小編就不貼了,這裡輸出了第一個 article 的所有節點,並且返回結果是列表形式。 article 節點裡既包含文本,又包含節點,最後會將它們以列表形式統一返回。

這裡需要注意的是,列表中的每個元素都是 article 的直接子節點,並沒有將再下一級的元素列出來。而使用 children 也可以得到相同的效果。

<code>

for

child

in

enumerate

(

soup

.article

.children

):

print

(

child

) /<code>

結果得到的還是相同的的 HTML 文本,這裡調用了 children 屬性來選擇,返回結果是生成器類型。

想要得到所有的孫子節點的話,可以使用 descendants :

<code>

for

i

,

child

in

enumerate

(

soup

.article

.descendants

):

print

(

i

,

child

) /<code>

父節點

獲取父節點可以使用 parent 屬性,示例如下:

<code>

print

(

soup

.title

.parent

) /<code>

結果有些長,就不貼了,各位同學可以自行嘗試一下。

兄弟節點

想要獲取兄弟節點可以使用屬性 next_sibling 和 previous_sibling 。

<code>

print

(

'next_sibling:'

, soup.title.next_sibling)

print

(

'previous_sibling:'

, soup.title.previous_sibling)

print

(

'next_siblings:'

, soup.title.next_siblings)

print

(

'previous_siblings:'

, soup.title.previous_siblings) /<code>

結果如下:

<code>next_sibling: 



previous_sibling: 



next_siblings: object PageElement.next_siblings at 

0x00000183342C5D48

> previous_siblings: object PageElement.previous_siblings at

0x00000183342C5D48

> /<code>

可以看到, next_sibling 和 previous_sibling 分別獲取節點的下一個和上一個兄弟元素,在這裡並沒有獲取到值,而是獲取了很多的空行,這個是在初始化 BeautifulSoup 的時候初始化出來的,而 next_siblings 和 previous_siblings 則分別返回所有前面和後面的兄弟節點的生成器。

示例代碼

本系列的所有代碼小編都會放在代碼管理倉庫 Github 和 Gitee 上,方便大家取用。

示例代碼-Github

示例代碼-Gitee

參考

https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/#


分享到:


相關文章: