python版本回退_Python爬蟲之BeautifulSoup解析之路

上一篇分享了正則表達式的使用,相信大家對正則也已經有了一定的了解。它可以針對任意字符串做任何的匹配并提取所需信息。

但是我們爬蟲基本上解析的都是html或者xml結構的內容,而非任意字符串。正則表達式雖然很強大靈活,但是對于html這樣結構復雜的來說,寫pattern的工作量會大大增加,并且有任意一處出錯都得不到匹配結果,比較麻煩。

本篇將介紹一款針對html和xml結構,操作簡單并容易上手的解析利器—BeautifulSoup。

format,png

BeautifulSoup的介紹

第一次使用BeautifulSoup的時候就在想:這個名字有什么含義嗎?美味的湯?于是好信也在網上查了一下。

來看,官方文檔是這么解釋的:

BeautifulSoup:?We called him Tortoise because he taught us”

意思是我們叫他烏龜因為他教了我們,當然這里Tortoise是Taught us的諧音。BeautifulSoup這個詞來自于《愛麗絲漫游仙境》,意思是“甲魚湯”。上面那個官方配圖也是來自于《愛麗絲漫游仙境》,看來是沒跑了,估計是作者可能很喜歡這部小說吧,因而由此起了這個名字。

好,讓我們看看真正的BeautifulSoup是什么?

BeautifulSoup是Python語言中的模塊,專門用于解析html/xml,非常適合像爬蟲這樣的項目。它有如下幾個使其強大的特點:

它提供了幾個超級簡單的方法和Pythonic的語句來實現強大的導航、搜索、修改解析樹的功能。

它會自動把將要處理的文檔轉化為Unicode編碼,并輸出為utf-8的編碼,不需要你再考慮編碼的問題。

支持Python標準庫中的HTML解析器,還支持第三方的模塊,如 lxml解析器 。

BeautifulSoup的安裝

目前BeautifulSoup的最新發型版本是BeautifulSoup4,在Python中以bs4模塊引入。

博主使用的Python3.x,可以使用?pip3 install bs4?來進行安裝,也可以通過官方網站下載來安裝,鏈接:https://www.crummy.com/software/BeautifulSoup/,具體安裝步驟不在此敘述了。

以為安裝完了嗎?還沒有呢。

上面介紹BeautifulSoup的特點時說到了,BeautifulSoup支持Python標準庫的解析器html5lib,純Python實現的。除此之外,BeautifulSoup還支持lxml解析器,為了能達到更好的解析效果,建議將這兩個解析器也一并安裝上。

根據操作系統不同,可以選擇下列方法來安裝lxml:

$?apt-get?install?Python-lxml

$?easy_install?lxml

$?pip?install?lxml

另一個可供選擇的解析器是純Python實現的?html5lib?, html5lib的解析方式與瀏覽器相同,可以選擇下列方法來安裝html5lib:

$?apt-get?install?Python-html5lib

$?easy_install?html5lib

$?pip?install?html5lib

下面列出上面提到解析器的使用方法。

解析器使用方法

Python標準庫BeautifulSoup(markup, "html.parser")

lxml HTML解析器BeautifulSoup(markup, "lxml")

lxml HTML解析器BeautifulSoup(markup, ["lxml", ? "xml"])

BeautifulSoup(markup, "xml")

html5libBeautifulSoup(markup, "html5lib")

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

BeautifulSoup的文檔對象創建

首先引入bs4庫,也就是BeautifulSoup在Python中的模塊。

from?bs4?import?BeautifulSoup

好了,我們來看一下官方提供的例子,這段例子引自《愛麗絲漫游記》。

html_doc?="""

The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters; and their names were

Elsie,

Lacie

andTillie;

and they lived at the bottom of a well.

...

"""

假設以上html_doc就是我們已經下載的網頁,我們需要從中解析并獲取感興趣的內容。

首先的首先,我們需要創建一個BeautifulSoup的文檔對象,依據不同需要可以傳入“字符串”或者“一個文件句柄”。

傳入“字符串”

soup = BeautifulSoup(html_doc)

傳入“文件句柄”,打開一個本地文件

soup = BeautifulSoup(open("index.html"))

文檔首先被轉換為Unicode,如果是解析html文檔,直接創建對象就可以了(像上面操作那樣),這時候BeautifulSoup會選擇一個最合適的解析器對文檔進行解析。

但同時,BeautifulSoup也支持手動選擇解析器,根據指定解析器進行解析(也就是我們安裝上面html5lib和lxml的原因)。

手動指定解析器如下:

soup = BeautifulSoup(html_doc, "lxml")

如果僅是想要解析HTML文檔,只要用文檔創建 BeautifulSoup?對象就可以了。Beautiful Soup會自動選擇一個解析器來解析文檔。但是還可以通過參數指定使用那種解析器來解析當前文檔。

BeautifulSoup?第一個參數應該是要被解析的文檔字符串或是文件句柄,第二個參數用來標識怎樣解析文檔。如果第二個參數為空,那么Beautiful Soup根據當前系統安裝的庫自動選擇解析器,解析器的優先數序: lxml, html5lib, Python標準庫。在下面兩種條件下解析器優先順序會變化:

要解析的文檔是什么類型: 目前支持, “html”, “xml”, 和 “html5”

指定使用哪種解析器: 目前支持, “lxml”, “html5lib”, 和 “html.parser”

BeautifulSoup的對象種類

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

NavigableString

BeautifulSoup

Comment

Tag就是html或者xml中的標簽,BeautifulSoup會通過一定的方法自動尋找你想要的指定標簽。查找標簽這部分會在后面“遍歷查找樹”和“搜索查找樹”中介紹,這里僅介紹對象。

soup = BeautifulSoup('Extremely bold')

tag=soup.b

type(tag)

>>>

Tag標簽下也有對象,有兩個重要的屬性對象:name和attributes。

Name

Name就是標簽tag的名字,以下簡單操作便可獲取。

tag.name

>>> u'b'

Attributes

我們都知道一個標簽下可能有很多屬性,比如上面那個標簽b有class屬性,屬性值為boldest,那么我們如何獲取這個屬性值呢?

其實標簽的屬性操作和Python中的字典操作一樣的,如下:

tag['class']

>>> u'boldest'

也可以通過“點”來獲取,比如:

tag.attrs

>>> {u'class': u'boldest'}

NavigableString是可遍歷字符串的意思,其實就是標簽內包括的字符串,在爬蟲里也是我們主要爬取的對象之一。

在BeautifulSoup中可以非常簡單的獲取標簽內這個字符串。

tag.string

>>> u'Extremely bold'

就這么簡單的完成了信息的提取,簡單吧。要說明一點,tag中包含的字符串是不能編輯的,但是可以替換。

tag.string.replace_with("No longer bold")

tag

>>>

No longer bold

BeautifulSoup對象表示的是一個文檔的全部內容。大部分時候,可以把它當作Tag對象。

soup.name

>>> u'[document]'

BeautifulSoup對象不是一個真正的tag,沒有name和attributes,但是卻可以查看它的name屬性。如上所示,“[document]”為BeautifulSoup文檔對象的特殊屬性名字。

還有一些對象也是我們需要特殊注意的,就是注釋。其實comment對象是一個特殊類型的NavigableString對象,請看下面。

markup =?""

soup = BeautifulSoup(markup)

comment?=?soup.b.string

type(comment)

>>>

comment

>>> u'Hey, buddy. Want to buy a used parser'

這和NavigableString的使用是一樣,同樣使用?.string?對標簽內字符串進行提取。但是,請看上面comment這個例子,里面字符串是一個comment,有這樣的格式,一樣使用了?.string對其進行提取,得到的結果是去掉了comment標志的里面的字符串。這樣的話,當我們并不知道它是否是comment,如果得到以上的結果很有可能不知道它是個comment。

因此,這可能會讓我們得到我們不想要的comment,擾亂我們的解析結果。

為了避免這種問題的發生,可以在使用之前首先通過以下代碼進行一個簡單的判斷,然后再進行其它操作。

iftype(soup.b.string)==bs4.element.Comment:

print(soup.b.string)

BeautifulSoup的遍歷文檔樹

仍然用最開始的《愛麗絲》中的一段話作為例子。

子節點

子節點有?.contents?和?.children?兩種用法。

contents

content屬性可以將標簽所有子節點以列表形式返回。

#

The Dormouse's story

print(soup.head.contents)

>>> [title>The Dormouse's story]

這樣就可以返回一個子節點標簽了。當然你也可以通過soup.title來實現,但是當文檔結構復雜的時候,比如有不止一個title的話,那這樣就不如contents使用來的快了。

head下只有一個標簽title,那么如果我們查看一下body下的子標簽。

print(soup.body.contents)

>>>

['\n',

The Dormouse's story

, '\n',

Once upon a time there were three little sisters; and their names were

Elsie,

Lacie

andTillie;

and they lived at the bottom of a well.

, '\n',

...

, '\n']

你會發現這些子節點列表中有很多“\n”,這是因為它把空格包括進去了,所以這里需要注意一下。

children

也可以通過?.chidren?得到相同的結果,只不過返回的children是一個生成器(generator),而不是一個列表。

print(soup.body.children)

>>>

看到這是一個生成器,因此我們可以for..in..進行遍歷,當然也可以得到以上同樣的結果。

forchildinsoup.body.children:?print(child)

子孫節點

子孫節點使用.descendants屬性。如果子節點可以直接獲取標簽的直接子節點,那么子孫節點則可以獲取所有子孫節點,注意說的是所有,也就是說孫子的孫子都得給我找出來,下用面開一個例子。

for?child?in?head_tag.descendants:?? ?print(child)

>>>

The Dormouse's story

>>> The Dormouse's stor

title是head的子節點,而title中的字符串是title的子節點,title和title所包含的字符串都是head的子孫節點,因此被循環遞歸的查找出來。.descendants?的用法和?.children?是一樣的,會返回一個生成器,需要for..in..進行遍歷。

父節點

父節點使用?.parents?屬性實現,可以得到父輩的標簽。

title_tag?=?soup.title

title_tag

>>>

The Dormouse's story

title_tag.parent

>>>

The Dormouse's story

title_tag.parent.name

>>> head

獲得全部父節點則使用.parents屬性實現,可以循環得到所有的父輩的節點。

link?=?soup.a

for?parent?in?link.parents:?? ?if?parent?is?None:?? ? ? ?print(parent)?? ?else:?? ? ? ?print(parent.name)

>>>

p

body

html

[document]

None

可以看到a節點的所有父輩標簽都被遍歷了,包括BeautifulSoup對象本身的[document]。

兄弟節點

兄弟節點使用.next_sibling和.previous_sibling屬性。

兄弟嘛,不難理解自然就是同等地位的節點了,其中next_sibling 獲取下一個兄弟節點,而previous_sibling 獲取前一個兄弟節點。

a_tag?=?soup.find("a",?id="link1")

a_tag.next_sibling

>>> ,

a_tag.previous_element

>>>

Once upon a time there were three little sisters; and their names were

兄弟節點可以通過?.next_siblings?和?.previous.sibling?獲取所有前后兄弟節點,同樣需要遍歷獲取每個元素。

回退和前進

當然還有一些其它用法,如回退和前進.next_element和.previous_element,它是針對所有節點的回退和前進,不分輩分。

a_tag?=?soup.find("a",?id="link1")

a_tag

>>>

Elsie,

a_tag.next_element

>>> Elsie

a_tag.previous_element

>>>

Once upon a time there were three little sisters; and their names were

因為使用了回退,將會尋找下一個節點對象而不分輩分,那么這個標簽的下一個節點就是它的子節點Elsie,而上一個節點就是上一個標簽的字符串對象。find用法會在后續搜索文檔樹里面詳細介紹。

回退和前進也可以尋找所有的前后節點,使用.next_elements和.previous_elements。

for?elem?in?last_a_tag.next_elements:

if?elem.nameisNone:continue

print(elem.name)

>>>

a

a

p

返回對象同樣是生成器,需要遍歷獲得元素。其中使用了if判斷去掉了不需要的None。

節點內容

前面提到過NavigableString對象的?.string?用法,這里在文檔遍歷再次體會一下其用法。

如果tag只有一個NavigableString?類型子節點,那么這個tag可以使用.string得到子節點,就像之前提到的一樣。而如果一個tag里面僅有一個子節點(比如tag里tag的字符串節點),那么這個tag也可以使用.string方法,輸出結果與當前唯一子節點的.string結果相同(如上所示)。

title_tag.string

>>> u'The Dormouse's story'

head_tag.contents

>>> [

The Dormouse's story]

head_tag.string

>>> u'The Dormouse's story'

但是如果這個tag里面有多個節點,那就不靈了。因為tag無法確定該調用哪個節點,如下面這種。

print(soup.html.string)

>>> None

如果tag中包含多個字符串,可以使用?.strings?來循環獲取,輸出的字符串中可能包含了很多空格或空行,使用.stripped_strings可以去除多余空白內容。

上面提介紹的都是如何遍歷各個節點,下面我們看看如何搜索我們我們真正想獲取的內容,如標簽屬性等。

BeautifulSoup的搜索文檔樹

搜索文檔樹有很多種用法,但使用方法都基本一致。這里只選擇介紹一種.find_all。

find_all()

find_all(name,?attrs?,?recursive?,?text?,?**kwargs)

find_all()?方法可以搜索當前標簽下的子節點,并會經過過濾條件判斷是否符合標準,先隨便看個例子。

soup.find_all("a")

>>>

[Elsie,

Lacie,

Tillie]

soup.find_all(id="link2")

>>>

[Lacie]

通過以上例子,可以發現,我們只要設定好我們的過濾條件,便可輕松的解析我們想要的內容。這些條件如何設定呢?

就是通過find_all()的這些參數來設置的,讓我們來看看。

Name參數

name參數就是標簽的名字,如上面的例子尋找所有標簽,name參數可以是字符串、True、正則表達式、列表、甚至具體方法。

下面舉個正則表達式的例子。

importre

soup?=BeautifulSoup(html_doc,?'lxml')fortag?insoup.find_all(re.compile("^t")):print(tag.name)

>>>?title

可以看到正則表達式的意思是匹配任何以“t”開頭的標簽名稱,就只有title一個。

使用“True”會匹配任何值,使用“列表”會匹配列表中所有的標簽項,如果沒有合適的過濾條件,還可以自定義一個“方法”。

Keyword參數

就如同Python中的關鍵字參數一樣,我們可以搜索指定的標簽屬性來定位標簽。

soup.find_all(id='link2')

>>>

[Lacie]

找到了id屬性為link2的標簽。

soup.find_all(href=re.compile("elsie"))

>>>

[Elsie]

找到了href屬性里含有“elsie”字樣的標簽。

也可以同時定義多個關鍵字條件來過濾匹配結果。

soup.find_all(href=re.compile("elsie"),?id='link1')

>>>

[three]

text參數

通過text參數可以搜索匹配的字符串內容,與name的用法相似,也可以使用字符串、True、正則表達式、列表、或者具體方法。

soup.find_all(text="Elsie")>>> [u'Elsie']

soup.find_all(text=re.compile("Dormouse")) >>>

[u"The Dormouse's story", u"The Dormouse's story"]

limit參數

limit參數可以限制返回匹配結果的數量,看下面這個例子。

soup.find_all("a",?limit=2)

>>>

[Elsie,

Lacie]

文檔中本來有三個標簽,但是通過限制只得到了兩個。

recursive參數

find_all()會尋找符合匹配條件的所有子孫節點,如果我們只想找直接的子節點,就可以設置recursive參數來進行限制,recursive=False。

soup.html.find_all("title")

>>> [

The Dormouse's story]

soup.html.find_all("title",?recursive=False)

>>> [ ]

上面是兩種使用recursive和沒有使用recursive的情況,可以發現它的作用。

以上就是find_all()所有參數的介紹,其它方法如find(),find_parents()等更多方法與find_all()基本一致,可以舉一反三。

總結

以上就是BeautifulSoup的使用方法介紹,主要記住三個部分內容:

BeautifulSoup對象種類

BeautifulSoup的遍歷文檔樹

BeautifulSoup的搜索文檔樹

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/372803.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/372803.shtml
英文地址,請注明出處:http://en.pswp.cn/news/372803.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

0615 團隊第二階段貢獻

0615 團隊第二階段貢獻 列志華http://www.cnblogs.com/liezhihua/ 26% 組長 黃柏堂 http://www.cnblogs.com/huang123/ 22% 團隊 韓麒麟 http://www.cnblogs.com/hanqilin/ 26% 團隊 王俊杰 http://www.cnblogs.com/wangjunjie123/ 28%團隊posted on 2016…

WebStorm 運行Rect Native 項目

今天教大家如何直接使用WebStorm這個IDE直接完成編碼運行項目工作.這樣就可以不用打開Xcode了. 1.首先點擊WebStorm右上方的下拉箭頭彈出的Edit Configurations.... 2.然后會進入一個配置頁面.點擊左上方的.在彈出的列表中選中npm.如圖. 3.在右邊的配置框中,先選擇Command為hel…

python編程比賽_用Python編程分析4W場球賽后,2018世界杯冠軍竟是…

比賽已經開始,我們不妨用 Python 來對參賽隊伍的實力情況進行分析,并大膽的預測下本屆世界杯的奪冠熱門球隊吧!通過數據分析,可以發現很多有趣的結果,比如:找出哪些隊伍是首次進入世界杯的黑馬隊伍找出2018…

GlassFish 3.1.2充滿了MOXy(EclipseLink JAXB)

我非常高興地宣布, EclipseLink JAXB(MOXy)現在是GlassFish 3.1.2中的JAXB( JSR-222 )提供程序。 我要感謝EclipseLink和GlassFish提交者為實現這一目標付出的??辛勤工作。 在本文中,我將介紹如何利用MOX…

夢斷代碼閱讀筆記03

讀完《夢斷代碼(Dream In Code)》樣書,我感覺心情有點沉重,Chandler項目的結局,它失敗了,它成了眾多失敗軟件項目中的一個。這個結局讓那個我感受到軟件實在是太難了,我覺得當初選這個專業可能到最后只是一個碼農。但是…

Java訪問權限的范圍

二、下面用表格來展示四種修飾符的訪問權限范圍: 同一個類 同一個包 不同包的子類 不同包的非子類 public √ √ √ √ protected √ √ √ 默認(default) √ √ private √ 轉載于:https://www.cnblogs.com/jianxin-lilang/p/6…

JavaFX 2 GameTutorial第2部分

介紹 ?他的是一系列與一個JavaFX 2游戲教程博客條目的第二批。 如果您尚未閱讀第1部分,請參閱JavaFX 2游戲教程的簡介部分。 在第1部分中,我提到了游戲的某些方面以及原型飛船的簡單演示(原型由簡單的形狀組成),該飛船…

sqlyog連接mysql教程_如何用SQLyog實現遠程連接MySQL

SQLyog客戶端,用root用戶遠程鏈接MySQL時,提示ldquo;訪問被拒絕rdquo;,在網上搜索了一下原因。原來是MySQL沒有授權其遠程鏈1,SQLyog客戶端,,用root用戶遠程鏈接MySQL時,提示“訪問被拒絕”&…

動態SQL+變量綁定:解決ORA-01704: 字符串文字太長的問題

最近在做一個ESB項目,有一個trigger里面執行動態SQL的時候報錯: ORA-01704: 字符串文字太長 經檢查發現SQL里面有個字段是clob類型,內容長度4009,在oracle里面, 一對引號內的字符長度如果超過4000,就會報OR…

JavaME:Google靜態地圖API

無論您是需要基于位置的應用程序的地圖還是只是出于娛樂目的,都可以使用有史以來最簡單的方法:Google Static Maps API。 在這篇文章中,我們將看到如何從緯度和經度獲得地圖作為圖像。 可以使用Location API獲得緯度和經度,我們將…

在ASP.NET中實現OAuth2.0(一)之了解OAuth

1、什么是OAuth2.0 是一個開放授權標準,允許用戶讓第三方應用訪問該用戶在某一個網站或平臺上的私密資源(如照片、視頻、聯系人等),而無須將用戶名和密碼提供給第三方應用 2、OAuth2.0授權模式 授權碼模式(authorizati…

mysql日期條件如何應用_MySQL如何使用時間作為判斷條件

背景:在開發過程中,我們經常需要根據時間作為判斷條件來查詢數據,例如:當月,當日,當前小時,幾天內......1. 當月我們只需要使用一個mysql的month(date)函數即可實現。(注意判斷年份)month(date)…

深入探討JS中的數組排序函數sort()和reverse()

最近在研究Javascript發現了其中一些比較靈異的事情。有點讓人感到無語比如: alert(typeof( NaN NaN));//結果為假。 alert(typeof( NaN ! NaN));//結果為真。 嘿嘿,當然這個不是這篇文章要討論的!!開始我們的正文 首先,我們來看一下JS中sor…

帶有謂詞的Java中的功能樣式-第1部分

您一直在聽到將要席卷全球的函數式編程,而您仍然堅持使用普通Java? 不用擔心,因為您已經可以在日常Java中添加一些功能樣式。 此外,它很有趣,可以節省許多代碼行并減少錯誤。 什么是謂詞? 實際上&#xff…

寶塔添加多占點_寶塔面板啟用WordPress多站點子域名、子目錄

其實在很早以前,陌小雨就聽說了 wordpress 的多站點功能,不過因為不清楚,所以懶得折騰,這不這幾天閑著蛋疼,好好研究了下這玩意,用起來的感覺還是相當不錯的,總結起來就是如果你準備開始用 word…

centos 6.5下安裝文件上傳下載服務

centos 6.5下安裝文件上傳下載服務 由于每次在CentOS中要下載一些配置文件到物理機,和上傳一些文件到服務器,導致來回的開啟ftp軟件有點麻煩,這里我們可以使用文件上傳下載服務,來解決上傳和下載的問題。 1.登錄服務器 2.執行命令…

Jenkins 入門系列--jenkins 介紹

第一章 Jenkins是什么? Jenkins 是一個可擴展的持續集成引擎。 主要用于: l 持續、自動地構建/測試軟件項目。 l 監控一些定時執行的任務。Jenkins擁有的特性包括: l 易于安裝-只要把jenkins.war部署到servlet容器,不需要數據庫支…

在方法參數上使用final關鍵字

經過一些自己的混淆,最終博客方法的具體含義(最終聲明的方法參數)將對此博客條目進行澄清。 至少可以將方法參數上的final關鍵字視為Java編譯器的指示符,表明該參數不能重新分配給另一個引用。 Java參數處理始終是“按值調用” &a…

PHP MySQLi 增刪改查

最近幾天,我們一直在學習利用MySQLi訪問數據庫并對其中的數據進行操作。今天給大家展現一個完整的例子,我們來制作一個新聞發布系統,利用MySQLi來操作數據庫,實現對新聞的添加、修改、刪除、查詢等基本功能。(以下代碼…

20162303《程序設計與數據結構》第一周學習總結

學號 2016-2017-2 《程序設計與數據結構》第1周學習總結 教材學習內容總結 本周學習了基本的JAVA知識,雖然比較基礎,但是在實際過程中還是出現了許許多多的問題,代碼一遍遍的敲錯,又一遍遍的修改,剛開始甚至不會切換模…