python BeautifulSoup庫用法總結

 

1. Beautiful Soup 簡介

簡單來講,Beautiful Soup是python的一個庫,最主要的功能是從網頁抓取數據。官方解釋以下:html

Beautiful Soup提供一些簡單的、python式的函數用來處理導航、搜索、修改分析樹等功能。它是一個工具箱,經過解析文檔爲用戶提供須要抓取的數據,由於簡單,因此不須要多少代碼就能夠寫出一個完整的應用程序。Beautiful Soup自動將輸入文檔轉換爲Unicode編碼,輸出文檔轉換爲utf-8編碼。你不須要考慮編碼方式,除非文檔沒有指定一個編碼方式,這時,Beautiful Soup就不能自動識別編碼方式了。而後,你僅僅須要說明一下原始編碼方式就能夠了。Beautiful Soup已成爲和lxml、html6lib同樣出色的python解釋器,爲用戶靈活地提供不一樣的解析策略或強勁的速度。html5

2. Beautiful Soup 安裝

Beautiful Soup 3 目前已經中止開發,推薦在如今的項目中使用Beautiful Soup 4,不過它已經被移植到BS4了,也就是說導入時咱們須要 import bs4 。python

能夠利用 pip 或者 easy_install 來安裝,如下兩種方法都可正則表達式

easy_install beautifulsoup4  
pip install beautifulsoup4  

若是想安裝最新的版本,請直接下載安裝包來手動安裝,也是十分方便的方法。下載完成以後解壓,運行下面的命令便可完成安裝(先cd到解壓後的目錄)express

python setup.py install

Beautiful Soup支持Python標準庫中的HTML解析器,還支持一些第三方的解析器,若是咱們不安裝它,則 Python 會使用 Python默認的解析器,lxml 解析器更增強大,速度更快,推薦安裝.瀏覽器

 

解析器ide

使用方法函數

優點工具

劣勢編碼

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語言庫

html5lib

BeautifulSoup(markup, 「html5lib」)

最好的容錯性

以瀏覽器的方式解析文檔

生成HTML5格式的文檔

速度慢

不依

安裝lxml 和 html5lib:

1 easy_install lxml  
2 pip install lxml 
1 easy_install html5lib  
2 pip install html5lib  

3. 建立 Beautiful Soup 對象

首先導入 bs4 庫,而後建立beautifulsoup對象

 1 from bs4 import BeautifulSoup 
 2 
 3 html = """  
 4 <html><head><title>The Dormouse's story</title></head>  
 5 <body>  
 6 <p class="title" name="dromouse"><b>The Dormouse's story</b></p>  
 7 <p class="story">Once upon a time there were three little sisters; and their names were  
 8 <a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,  
 9 <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and  
10 <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;  
11 and they lived at the bottom of a well.</p>  
12 <p class="story">...</p>  
13 """  
14 
15 soup = BeautifulSoup(html)
16 soup = BeautifulSoup(open('index.html'))  #使用本地文件建立對象

打印一下 soup 對象的內容,格式化輸出

1 print soup.prettify()

指定編碼:當html爲其餘類型編碼(非utf-8和ascii),好比GB2312的話,則須要指定相應的字符編碼,BeautifulSoup才能正確解析。

htmlCharset = "GB2312"  
soup = BeautifulSoup(respHtml, fromEncoding=htmlChars
from bs4 import BeautifulSoup
import bs4
import re

# 待分析字符串
html_doc = """ 
<html> 
    <head> 
        <title>The Dormouse's story</title> 
    </head> 
    <body> 
    <p class="title aq"> 
        <b> 
            The Dormouse's story 
        </b> 
    </p> 

    <p class="story">Once upon a time there were three little sisters; and their names were 
        <a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>, 
        <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>  
        and 
        <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; 
        and they lived at the bottom of a well. 
    </p> 

    <p class="story">...</p> 
    </body>
</html>
"""
# 每一段代碼中註釋部分即爲運行結果
# html字符串建立BeautifulSoup對象
soup = BeautifulSoup(html_doc, 'html.parser', from_encoding='utf-8')

# 輸出第一個 title 標籤
print(soup.title)
# <title>The Dormouse's story</title>

# 輸出第一個 title 標籤的標籤名稱
print(soup.title.name)
# title

# 輸出第一個 title 標籤的包含內容
print(soup.title.string)
# The Dormouse's story

# 輸出第一個 title 標籤的父標籤的標籤名稱
print(soup.title.parent.name)
# head

# 輸出第一個 p 標籤
print(soup.p)
"""
<p class="title aq">
<b> 
            The Dormouse's story 
        </b>
</p>
"""
# 輸出第一個 p 標籤的 class 屬性內容
print(soup.p['class'])
# ['title', 'aq']

# 輸出第一個 a 標籤的  href 屬性內容
print(soup.a['href'])
# http://example.com/elsie

''''' 
soup的屬性能夠被添加,刪除或修改. 操做方法與字典同樣 
'''
# 修改第一個 a 標籤的href屬性爲 http://www.baidu.com/
# soup.a['href'] = 'http://www.baidu.com/'

# 給第一個 a 標籤添加 name 屬性
# soup.a['name'] = u'百度'

# 刪除第一個 a 標籤的 class 屬性爲
# del soup.a['class']

##輸出第一個 p 標籤的全部子節點
print(soup.p.contents)
"""
['\n', <b> 
            The Dormouse's story 
        </b>, '\n']
"""

# 輸出第一個 a 標籤
print(soup.a)
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
# 輸出全部的 a 標籤,以列表形式顯示
print(soup.find_all('a'))
"""
[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, 
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, 
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
"""

# 輸出第一個 id 屬性等於  link3 的  a 標籤
print(soup.find(id="link3"))
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

# 獲取全部文字內容
print(soup.get_text())
"""
The Dormouse's story
            The Dormouse's story 
Once upon a time there were three little sisters; and their names were 
        Elsie, 
        Lacie  
        and 
        Tillie; 
        and they lived at the bottom of a well. 
...
"""
# 輸出第一個  a 標籤的全部屬性信息
print(soup.a.attrs)
# {'href': 'http://example.com/elsie', 'class': ['sister'], 'id': 'link1'}

for link in soup.find_all('a'):
    # 獲取 link 的  href 屬性內容
    print(link.get('href'))
"""
 http://example.com/elsie
 http://example.com/lacie
 http://example.com/tillie
"""
# 對soup.p的子節點進行循環輸出
for child in soup.p.children:
    print("對soup.p的子節點進行循環輸出", child)
"""
對soup.p的子節點進行循環輸出 

對soup.p的子節點進行循環輸出 <b> 
            The Dormouse's story 
        </b>
對soup.p的子節點進行循環輸出 

"""
# 正則匹配,名字中帶有b的標籤
for tag in soup.find_all(re.compile(r"b")):
    print(tag.name)
"""
body
b
"""

 

4. 四大對象種類

Beautiful Soup將複雜HTML文檔轉換成一個複雜的樹形結構,每一個節點都是Python對象,全部對象能夠概括爲4種:

  1. Tag  
  2. NavigableString  
  3. BeautifulSoup  
  4. Comment 

(1)Tag

Tag 是什麼?通俗點講就是 HTML 中的一個個標籤,例如

<title>The Dormouse's story</title>  
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>  

上面的< title>< a> 等、 標籤加上裏面包括的內容就是 Tag,利用 soup加標籤名輕鬆地獲取這些標籤的內容,是否是感受比正則表達式方便多了?不過有一點是,它查找的是在全部內容中的第一個符合要求的標籤。soup.title 獲得的是title標籤,soup.p 獲得的是文檔中的第一個p標籤,要想獲得全部標籤,得用find_all函數。find_all 函數返回的是一個序列,能夠對它進行循環,依次獲得想到的東西.。

咱們能夠驗證一下這些對象的類型

1 print type(soup.a)  
2 #<class 'bs4.element.Tag'> 

 

對於 Tag,它有兩個重要的屬性,是 name 和 attrs

name

1 print soup.name  
2 print soup.head.name  
3 #[document]  
4 #head  

soup 對象自己比較特殊,它的 name 即爲 [document],對於其餘內部標籤,輸出的值便爲標籤自己的名稱。

attrs

1 print soup.p.attrs  
2 #{'class': ['title'], 'name': 'dromouse'}

 

在這裏,咱們把 p 標籤的全部屬性打印輸出了出來,獲得的類型是一個字典。若是咱們想要單獨獲取某個屬性,能夠這樣,例如咱們獲取它的 class 叫什麼

1 print soup.p['class']  
2 #['title']  

 

還能夠這樣,利用get方法,傳入屬性的名稱,兩者是等價的

print soup.p.get('class')  
#['title'] 

 

咱們能夠對這些屬性和內容等等進行修改,例如

1 soup.p['class']="newClass"  
2 print soup.p  
3 #<p class="newClass" name="dromouse"><b>The Dormouse's story</b></p> 

 

還能夠對這個屬性進行刪除,例如

1 del soup.p['class']  
2 print soup.p  
3 #<p name="dromouse"><b>The Dormouse's story</b></p> 

 

不過,對於修改刪除的操做,不是咱們的主要用途,在此不作詳細介紹了,若是有須要,請查看前面提供的官方文檔

 

1 head = soup.find('head')  
2 #head = soup.head  
3 #head = soup.contents[0].contents[0]  
4 print head  
5   
6 html = soup.contents[0]       # <html> ... </html>  
7 head = html.contents[0]       # <head> ... </head>  
8 body = html.contents[1]       # <body> ... </body>  

能夠經過Tag.attrs訪問,返回字典結構的屬性。

或者Tag.name這樣訪問特定屬性值,若是是多值屬性則以列表形式返回。

 

(2)NavigableString

既然咱們已經獲得了標籤的內容,那麼問題來了,咱們要想獲取標籤內部的文字怎麼辦呢?很簡單,用 .string 便可,例如

 

1 print soup.p.string  
2 #The Dormouse's story 

這樣咱們就輕鬆獲取到了標籤裏面的內容,想一想若是用正則表達式要多麻煩。它的類型是一個 NavigableString,翻譯過來叫 能夠遍歷的字符串,不過咱們最好仍是稱它英文名字吧。來檢查一下它的類型

1 print type(soup.p.string)  
2 #<class 'bs4.element.NavigableString'>

 

(3)BeautifulSoup

BeautifulSoup 對象表示的是一個文檔的所有內容.大部分時候,能夠把它看成 Tag 對象,是一個特殊的 Tag,咱們能夠分別獲取它的類型,名稱,以及屬性來感覺一下

 

1 print type(soup.name)  
2 #<type 'unicode'>  
3 print soup.name   
4 # [document]  
5 print soup.attrs   
6 #{} 空字典

 

(4)Comment

Comment 對象是一個特殊類型的 NavigableString 對象,其實輸出的內容仍然不包括註釋符號,可是若是很差好處理它,可能會對咱們的文本處理形成意想不到的麻煩。

咱們找一個帶註釋的標籤

1 print soup.a  
2 print soup.a.string  
3 print type(soup.a.string)  

運行結果以下:

1 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>  
2  Elsie   
3 <class 'bs4.element.Comment'>

a 標籤裏的內容其實是註釋,可是若是咱們利用 .string 來輸出它的內容,咱們發現它已經把註釋符號去掉了,因此這可能會給咱們帶來沒必要要的麻煩。

 另外咱們打印輸出下它的類型,發現它是一個 Comment 類型,因此,咱們在使用前最好作一下判斷,判斷代碼以下: 

1 if type(soup.a.string)==bs4.element.Comment:  
2     print soup.a.string  

上面的代碼中,咱們首先判斷了它的類型,是否爲 Comment 類型,而後再進行其餘操做,如打印輸出。

 

5. 遍歷文檔樹

(1)直接子節點

Tag.Tag_child1:直接經過下標名稱訪問子節點。

Tag.contents:以列表形式返回全部子節點。

Tag.children:生成器,可用於循環訪問:for child in Tag.children

要點:.contents .children 屬性

.contents

tag 的 .content 屬性能夠將tag的子節點以列表的方式輸出。可使用 [num] 的形式得到。使用contents向後遍歷樹,使用parent向前遍歷樹: 

1 print soup.head.contents   
2 #[<title>The Dormouse's story</title>] 

輸出方式爲列表,咱們能夠用列表索引來獲取它的某一個元素

1 print soup.head.contents[0]  
2 #<title>The Dormouse's story</title> 

 

.children

它返回的不是一個 list,不過咱們能夠經過遍歷獲取全部子節點。咱們打印輸出 .children 看一下,能夠發現它是一個 list 生成器對象。

可使用list能夠將其轉化爲列表。固然可使用for 語句遍歷裏面的孩子。 

1 print soup.head.children  
2 #<listiterator object at 0x7f71457f5710> 

咱們怎樣得到裏面的內容呢?很簡單,遍歷一下就行了:

1 for child in  soup.body.children:  
2     print child  

結果以下:

 1 <p class="title" name="dromouse"><b>The Dormouse's story</b></p>  
 2   
 3 <p class="story">Once upon a time there were three little sisters; and their names were  
 4 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,  
 5 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and  
 6 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;  
 7 and they lived at the bottom of a well.</p>  
 8   
 9   
10 <p class="story">...</p>  

 

(2)全部子孫節點

知識點:.descendants 屬性

.descendants

.contents 和 .children 屬性僅包含tag的直接子節點,.descendants 屬性能夠對全部tag的子孫節點進行遞歸循環,和 children相似,咱們也須要遍歷獲取其中的內容。

Tag.descendants:生成器,可用於循環訪問:for des inTag.descendants 

1 for child in soup.descendants:  
2     print child  

 

運行結果以下,能夠發現,全部的節點都被打印出來了,先生成最外層的 HTML標籤,其次從 head 標籤一個個剝離,以此類推。

 1 <html><head><title>The Dormouse's story</title></head>  
 2 <body>  
 3 <p class="title" name="dromouse"><b>The Dormouse's story</b></p>  
 4 <p class="story">Once upon a time there were three little sisters; and their names were  
 5 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,  
 6 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and  
 7 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;  
 8 and they lived at the bottom of a well.</p>  
 9 <p class="story">...</p>  
10 </body></html>  
11 <head><title>The Dormouse's story</title></head>  
12 <title>The Dormouse's story</title>  
13 The Dormouse's story  
14   
15   
16 <body>  
17 <p class="title" name="dromouse"><b>The Dormouse's story</b></p>  
18 <p class="story">Once upon a time there were three little sisters; and their names were  
19 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,  
20 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and  
21 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;  
22 and they lived at the bottom of a well.</p>  
23 <p class="story">...</p>  
24 </body>  
25   
26   
27 <p class="title" name="dromouse"><b>The Dormouse's story</b></p>  
28 <b>The Dormouse's story</b>  
29 The Dormouse's story  
30   
31   
32 <p class="story">Once upon a time there were three little sisters; and their names were  
33 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,  
34 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and  
35 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;  
36 and they lived at the bottom of a well.</p>  
37 Once upon a time there were three little sisters; and their names were  
38   
39 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>  
40  Elsie   
41 ,  
42   
43 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>  
44 Lacie  
45  and  
46   
47 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>  
48 Tillie  
49 ;  
50 and they lived at the bottom of a well.  
51   
52   
53 <p class="story">...</p>  
54 ...  
View Code

 

(3)節點內容

知識點:.string 屬性

Tag.String:Tag只有一個String子節點時,能夠這麼訪問,不然返回None
Tag.Strings:生成器,可用於循環訪問:for str in Tag.Strings

若是tag只有一個 NavigableString 類型子節點,那麼這個tag可使用 .string 獲得子節點。若是一個tag僅有一個子節點,那麼這個tag也可使用 .string 方法,輸出結果與當前惟一子節點的 .string 結果相同。通俗點說就是:若是一個標籤裏面沒有標籤了,那麼 .string 就會返回標籤裏面的內容。若是標籤裏面只有惟一的一個標籤了,那麼 .string 也會返回最裏面的內容。若是超過一個標籤的話,那麼就會返回None。例如 

1 print soup.head.string  
2 #The Dormouse's story  
3 print soup.title.string  
4 #The Dormouse's story  
View Code

若是tag包含了多個子節點,tag就沒法肯定,string 方法應該調用哪一個子節點的內容, .string 的輸出結果是 None

1 print soup.html.string  
2 # None 
View Code

 

(4)多個內容

知識點: .strings .stripped_strings 屬性

.strings

獲取多個內容,不過須要遍歷獲取,好比下面的例子 

 1 for string in soup.strings:  
 2     print(repr(string))  
 3     # u"The Dormouse's story"  
 4     # u'\n\n'  
 5     # u"The Dormouse's story"  
 6     # u'\n\n'  
 7     # u'Once upon a time there were three little sisters; and their names were\n'  
 8     # u'Elsie'  
 9     # u',\n'  
10     # u'Lacie'  
11     # u' and\n'  
12     # u'Tillie'  
13     # u';\nand they lived at the bottom of a well.'  
14     # u'\n\n'  
15     # u'...'  
16     # u'\n'  
View Code

.stripped_strings 

輸出的字符串中可能包含了不少空格或空行,使用 .stripped_strings 能夠去除多餘空白內容: 

 1 for string in soup.stripped_strings:  
 2     print(repr(string))  
 3     # u"The Dormouse's story"  
 4     # u"The Dormouse's story"  
 5     # u'Once upon a time there were three little sisters; and their names were'  
 6     # u'Elsie'  
 7     # u','  
 8     # u'Lacie'  
 9     # u'and'  
10     # u'Tillie'  
11     # u';\nand they lived at the bottom of a well.'  
12     # u'...'  
View Code

 

(5)父節點

知識點: .parent 屬性

使用parent獲取父節點。

Tag.parent:父節點
Tag.parents:父到根的全部節點 

1 body = soup.body  
2 html = body.parent             # html是body的父親  
View Code
1 p = soup.p  
2 print p.parent.name  
3 #body  
4   
5 content = soup.head.title.string  
6 print content.parent.name  
7 #title  
View Code

 

(6)所有父節點

知識點:.parents 屬性

經過元素的 .parents 屬性能夠遞歸獲得元素的全部父輩節點,例如: 

1 content = soup.head.title.string  
2 for parent in  content.parents:  
3     print parent.name  
4   
5 title  
6 head  
7 html  
8 [document]  
View Code

 

(7)兄弟節點

知識點:.next_sibling .previous_sibling 屬性

使用nextSibling, previousSibling獲取先後兄弟

Tag.next_sibling
Tag.next_siblings

Tag.previous_sibling
Tag.previous_siblings

兄弟節點能夠理解爲和本節點處在統一級的節點,.next_sibling 屬性獲取了該節點的下一個兄弟節點,.previous_sibling 則與之相反,若是節點不存在,則返回 None。

注意:實際文檔中的tag的 .next_sibling 和 .previous_sibling 屬性一般是字符串或空白,由於空白或者換行也能夠被視做一個節點,因此獲得的結果多是空白或者換行。 

 1 print soup.p.next_sibling  
 2 #       實際該處爲空白  
 3 print soup.p.prev_sibling  
 4 #None   沒有前一個兄弟節點,返回 None  
 5 print soup.p.next_sibling.next_sibling  
 6 #<p class="story">Once upon a time there were three little sisters; and their names were  
 7 #<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,  
 8 #<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and  
 9 #<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;  
10 #and they lived at the bottom of a well.</p>  
11 #下一個節點的下一個兄弟節點是咱們能夠看到的節點  
View Code

 

.next方法:只能針對單一元素進行.next,或者說是對contents列表元素的挨個清點。

1 好比  
2 soup.contents[1]=u'HTML'  
3 soup.contents[2]=u'\n'  
4 則 soup.contents[1].next 等價於 soup.contents[2]  
5 
6 head = body.previousSibling    # head和body在同一層,是body的前一個兄弟  
7 p1 = body.contents[0]          # p1, p2都是body的兒子,咱們用contents[0]取得p1  
8 p2 = p1.nextSibling            # p2與p1在同一層,是p1的後一個兄弟, 固然body.content[1]也可獲得  
View Code

contents[]的靈活運用也能夠尋找關係節點,尋找祖先或者子孫能夠採用findParent(s), findNextSibling(s), findPreviousSibling(s)

 

(8)所有兄弟節點

知識點:.next_siblings .previous_siblings 屬性

經過 .next_siblings 和 .previous_siblings 屬性能夠對當前節點的兄弟節點迭代輸出。 

1 for sibling in soup.a.next_siblings:  
2     print(repr(sibling))  
3     # u',\n'  
4     # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>  
5     # u' and\n'  
6     # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>  
7     # u'; and they lived at the bottom of a well.'  
8     # None  
View Code

 

(9)先後節點

知識點:.next_element .previous_element 屬性

與 .next_sibling .previous_sibling 不一樣,它並非針對於兄弟節點,而是在全部節點,不分層次。好比 head 節點爲 

1 <head><title>The Dormouse's story</title></head>

那麼它的下一個節點即是 title,它是不分層次關係的

1 print soup.head.next_element  
2 #<title>The Dormouse's story</title>  
View Code

 

(10)全部先後節點

知識點:.next_elements .previous_elements 屬性

經過 .next_elements 和 .previous_elements 的迭代器就能夠向前或向後訪問文檔的解析內容,就好像文檔正在被解析同樣。 

1 for element in last_a_tag.next_elements:  
2     print(repr(element))  
3 # u'Tillie'  
4 # u';\nand they lived at the bottom of a well.'  
5 # u'\n\n'  
6 # <p class="story">...</p>  
7 # u'...'  
8 # u'\n'  
9 # None  
View Code

 

6.搜索文檔樹

最經常使用的是find_all()函數

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

find_all() 方法搜索當前tag的全部tag子節點,並判斷是否符合過濾器的條件

1)name 參數

name 參數能夠查找全部名字爲 name 的tag,字符串對象會被自動忽略掉。 

 1 #第一個參數爲Tag的名稱   
 2 tag.find_all(‘title’)    
 3 #獲得」<title>&%^&*</title>」,結果爲一個列表    
 4   
 5 第二個參數爲匹配的屬性  
 6 tag.find_all(「title」,class=」sister」)    
 7 #獲得如」<title class = 「sister」>%^*&</title>    
 8   
 9 # 第二個參數也能夠爲字符串,獲得字符串匹配的結果  
10 tag.find_all(「title」,」sister」)    
11 #獲得如」<title class = 「sister」>%^*&</title>
View Code

A.傳字符串

最簡單的過濾器是字符串.在搜索方法中傳入一個字符串參數,Beautiful Soup會查找與字符串完整匹配的內容,下面的例子用於查找文檔中全部的<b>標籤

1 soup.find_all('b')  
2 # [<b>The Dormouse's story</b>]  
3   
4 print soup.find_all('a')  
5 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]  
View Code

 

B.傳正則表達式

若是傳入正則表達式做爲參數,Beautiful Soup會經過正則表達式的 match() 來匹配內容.下面例子中找出全部以b開頭的標籤,這表示<body>和<b>標籤都應該被找到

1 import re  
2 for tag in soup.find_all(re.compile("^b")):  
3     print(tag.name)  
4 # body  
5 # b  
View Code

 

C.傳列表

若是傳入列表參數,Beautiful Soup會將與列表中任一元素匹配的內容返回.下面代碼找到文檔中全部<a>標籤和<b>標籤

1 soup.find_all(["a", "b"])  
2 # [<b>The Dormouse's story</b>,  
3 #  <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,  
4 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,  
5 #  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]  
View Code

 

D.傳 True

True 能夠匹配任何值,下面代碼查找到全部的tag,可是不會返回字符串節點

 1 for tag in soup.find_all(True):  
 2     print(tag.name)  
 3 # html  
 4 # head  
 5 # title  
 6 # body  
 7 # p  
 8 # b  
 9 # p  
10 # a  
11 # a  
View Code

 

E.傳方法

若是沒有合適過濾器,那麼還能夠定義一個方法,方法只接受一個元素參數 [4] ,若是這個方法返回 True 表示當前元素匹配而且被找到,若是不是則反回 False。下面方法校驗了當前元素,若是包含 class 屬性卻不包含 id 屬性,那麼將返回 True:

1 def has_class_but_no_id(tag):  
2     return tag.has_attr('class') and not tag.has_attr('id')
View Code

將這個方法做爲參數傳入 find_all() 方法,將獲得全部<p>標籤:

1 soup.find_all(has_class_but_no_id)  
2 # [<p class="title"><b>The Dormouse's story</b></p>,  
3 #  <p class="story">Once upon a time there were...</p>,  
4 #  <p class="story">...</p>]  
View Code

 

2)keyword 參數

注意:若是一個指定名字的參數不是搜索內置的參數名,搜索時會把該參數看成指定名字tag的屬性來搜索,若是包含一個名字爲 id 的參數,Beautiful Soup會搜索每一個tag的」id」屬性 

1 soup.find_all(id='link2')  
2 # [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]  
View Code

若是傳入 href 參數,Beautiful Soup會搜索每一個tag的」href」屬性

1 soup.find_all(href=re.compile("elsie"))  
2 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]  
View Code

使用多個指定名字的參數能夠同時過濾tag的多個屬性

1 soup.find_all(href=re.compile("elsie"), id='link1')  
2 # [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]  
View Code

在這裏咱們想用 class 過濾,不過 class 是 python 的關鍵詞,這怎麼辦?加個下劃線就能夠。

1 soup.find_all("a", class_="sister")  
2 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,  
3 # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,  
4 # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]  
View Code

有些tag屬性在搜索不能使用,好比HTML5中的 data-* 屬性。 

1 data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')  
2 data_soup.find_all(data-foo="value")  
3 # SyntaxError: keyword can't be an expression  
View Code

可是能夠經過 find_all() 方法的 attrs 參數定義一個字典參數來搜索包含特殊屬性的tag

1 data_soup.find_all(attrs={"data-foo": "value"})  
2 # [<div data-foo="value">foo!</div>]  
View Code

 

3)text 參數 

經過 text 參數能夠搜搜文檔中的字符串內容.與 name 參數的可選值同樣, text 參數接受 字符串 , 正則表達式 , 列表, True。 

1 soup.find_all(text="Elsie")  
2 # [u'Elsie']  
3   
4 soup.find_all(text=["Tillie", "Elsie", "Lacie"])  
5 # [u'Elsie', u'Lacie', u'Tillie']  
6   
7 soup.find_all(text=re.compile("Dormouse"))  
8 [u"The Dormouse's story", u"The Dormouse's story"]  
View Code

 

4)limit 參數 

find_all() 方法返回所有的搜索結構,若是文檔樹很大那麼搜索會很慢.若是咱們不須要所有結果,可使用 limit 參數限制返回結果的數量.效果與SQL中的limit關鍵字相似,當搜索到的結果數量達到 limit 的限制時,就中止搜索返回結果.

文檔樹中有3個tag符合搜索條件,但結果只返回了2個,由於咱們限制了返回數量

1 soup.find_all("a", limit=2)  
2 # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,  
3 #  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>] 
View Code

 

5)recursive 參數

 調用tag的 find_all() 方法時,Beautiful Soup會檢索當前tag的全部子孫節點,若是隻想搜索tag的直接子節點,可使用參數 recursive=False 。一段簡單的文檔: 

1 <html>  
2  <head>  
3   <title>  
4    The Dormouse's story  
5   </title>  
6  </head>  
7 ...  
View Code

是否使用 recursive 參數的搜索結果: 

1 soup.html.find_all("title")  
2 # [<title>The Dormouse's story</title>]  
3   
4 soup.html.find_all("title", recursive=False)  
5 # []  
View Code

 

(2)find(name=None, attrs={}, recursive=True, text=None, **kwargs)

 它與 find_all() 方法惟一的區別是 find_all() 方法的返回結果是值包含一個元素的列表,而 find() 方法直接返回結果。

.find('p'),.findAll('p'):find返回的是字符串值,並且是返回從頭查找到的第一個tag對。可是若是這第一個tag對包括大量的內容,父等級很高,則同時其內部所包含的,此級標籤也所有都find。findAll返回值是個列表,若是發現了一個同名標籤內含多個同名標籤,則內部的標籤一併歸於該父標籤顯示,列表其餘元素也再也不體現那些內含的同名子標籤。即findAll會返回全部符合要求的結果,並以list返回。

 1 soup.findAll(onclick='document.location...')  
 2     soup.findAll(attrs={'style':r'outline:none;'}) #用來查找屬性中有style='outline:none;的標籤體。                                 # 搜索全部tag  
 3 
 4 tag搜索  
 5 find(tagname)                                  # 直接搜索名爲tagname的tag 如:find('head')  
 6 find(list)                                     # 搜索在list中的tag,如: find(['head', 'body'])  
 7 find(dict)                                     # 搜索在dict中的tag,如:find({'head':True, 'body':True})  
 8 find(re.compile(''))                           # 搜索符合正則的tag, 如:find(re.compile('^p')) 搜索以p開頭的tag  
 9 find(lambda)                       # 搜索函數返回結果爲true的tag, 如:find(lambda name: if len(name) == 1) 搜索長度爲1的tag  
10 find(True)                                     # 搜索全部tag  
11   
12 attrs搜索  
13 find(id='xxx')                                  # 尋找id屬性爲xxx的  
14 find(attrs={id=re.compile('xxx'), algin='xxx'}) # 尋找id屬性符合正則且algin屬性爲xxx的  
15 find(attrs={id=True, algin=None})               # 尋找有id屬性可是沒有algin屬性的  
16   
17 resp1 = soup.findAll('a', attrs = {'href': match1})  
18 resp2 = soup.findAll('h1', attrs = {'class': match2})  
19 resp3 = soup.findAll('img', attrs = {'id': match3})  
20   
21 text搜索  
22    文字的搜索會致使其餘搜索給的值如:tag, attrs都失效。方法與搜索tag一致     
23 print p1.text  
24 # u'This is paragraphone.'  
25 print p2.text  
26 # u'This is paragraphtwo.'  
27 # 注意:1,每一個tag的text包括了它以及它子孫的text。2,全部text已經被自動轉爲unicode,若是須要,能夠自行轉碼encode(xxx)  
28   
29 recursive和limit屬性  
30 recursive=False表示只搜索直接兒子,不然搜索整個子樹,默認爲True。  
31 當使用findAll或者相似返回list的方法時,limit屬性用於限制返回的數量,  
32 如:findAll('p', limit=2): 返回首先找到的兩個tag  
View Code

 

(3)find_parents()  find_parent()

find_all() 和 find() 只搜索當前節點的全部子節點,孫子節點等. find_parents() 和 find_parent() 用來搜索當前節點的父輩節點,搜索方法與普通tag的搜索方法相同,搜索文檔搜索文檔包含的內容

(4)find_next_siblings()  find_next_sibling()

這2個方法經過 .next_siblings 屬性對當 tag 的全部後面解析的兄弟 tag 節點進行迭代, find_next_siblings() 方法返回全部符合條件的後面的兄弟節點,find_next_sibling() 只返回符合條件的後面的第一個tag節點

(5)find_previous_siblings()  find_previous_sibling()

這2個方法經過 .previous_siblings 屬性對當前 tag 的前面解析的兄弟 tag 節點進行迭代, find_previous_siblings()方法返回全部符合條件的前面的兄弟節點, find_previous_sibling() 方法返回第一個符合條件的前面的兄弟節點

(6)find_all_next()  find_next()

這2個方法經過 .next_elements 屬性對當前 tag 的以後的 tag 和字符串進行迭代, find_all_next() 方法返回全部符合條件的節點, find_next() 方法返回第一個符合條件的節點

(7)find_all_previous() 和 find_previous()

這2個方法經過 .previous_elements 屬性對當前節點前面的 tag 和字符串進行迭代, find_all_previous() 方法返回全部符合條件的節點, find_previous()方法返回第一個符合條件的節點

注:以上(2)(3)(4)(5)(6)(7)方法參數用法與 find_all() 徹底相同,原理均相似,在此再也不贅述。

7. CSS選擇器

在寫 CSS 時,標籤名不加任何修飾,類名前加點,id名前加 #

在這裏咱們也能夠利用相似的方法來篩選元素,用到的方法是 soup.select(),返回類型是 list

(1)經過標籤名查找 

1 print soup.select('title')   
2 #[<title>The Dormouse's story</title>] 
3 
4 print soup.select('a')  
5 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]  
6 
7 print soup.select('b')  
8 #[<b>The Dormouse's story</b>]
View Code

 

(2)經過類名查找

1 print soup.select('.sister')  
2 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]  
View Code

 

(3)經過 id 名查找

1 print soup.select('#link1')  
2 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]  
View Code

 

(4)組合查找

組合查找即和寫 class 文件時,標籤名與類名、id名進行的組合原理是同樣的,例如:查找 p 標籤中,id 等於 link1的內容,兩者須要用空格分開

1 print soup.select('p #link1')  
2 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>] 

直接子標籤查找

1 print soup.select("head > title")  
2 #[<title>The Dormouse's story</title>]  

 

(5)屬性查找

查找時還能夠加入屬性元素,屬性須要用中括號括起來,注意屬性和標籤屬於同一節點,因此中間不能加空格,不然會沒法匹配到。

1 print soup.select('a[class="sister"]')  
2 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]  
3 
4 print soup.select('a[href="http://example.com/elsie"]')  
5 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>] 
View Code

一樣,屬性仍然能夠與上述查找方式組合,不在同一節點的空格隔開,同一節點的不加空格。

1 print soup.select('p a[href="http://example.com/elsie"]')  
2 #[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]  
View Code

以上的 select 方法返回的結果都是列表形式,能夠遍歷形式輸出,而後用 get_text() 方法來獲取它的內容。

1 soup = BeautifulSoup(html, 'lxml')  
2 print type(soup.select('title'))  
3 print soup.select('title')[0].get_text()  
4   
5 for title in soup.select('title'):  
6     print title.get_text()  
View Code

這就是另外一種與 find_all 方法有殊途同歸之妙的查找方法,是否是感受很方便?

 1 print soup.find_all("a", class_="sister")  
 2 print soup.select("p.title")  
 3   
 4 # 經過屬性進行查找  
 5 print soup.find_all("a", attrs={"class": "sister"})  
 6   
 7 # 經過文本進行查找  
 8 print soup.find_all(text="Elsie")  
 9 print soup.find_all(text=["Tillie", "Elsie", "Lacie"])  
10   
11 # 限制結果個數  
12 print soup.find_all("a", limit=2)  
View Code
相關文章
相關標籤/搜索