天天看點

python3爬蟲(6)--使用Beautiful Soup解析資料

1、基礎概念

前言:

​​​​​Beautiful Soup 就是Python的一個HTML或XML的解析庫,可以用它來友善地從網頁中提取資料。

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

Beautiful Soup 自動将輸入文檔轉換為Unicode編碼,輸出文檔轉換為UTF-8編碼。

Beautiful Soup 的HTML和XML解析器是依賴于1xml庫,是以需要確定lxml安裝:pip install lxml ,常見錯誤提示:bs4 FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?解除安裝lxml:pip uninstall lxml ,再重裝:pip install lxml,不行就重新開機pycharm.

Beautiful Soup 子產品使用前需要確定安裝,目前最新版本是4.x版本:pip install beautifulsoup4

Beautiful Soup 在解析資料時通常使用bs4,引入:from bs4 import BeautifulSoup

Beautiful Soup在解析時實際上依賴解析器,它除了支援Python标準庫中的HTL解析器外,還支援一些第三方解析器(比如lxml)。

解析器:

Beautiful Soup在解析時實際上依賴解析器,它除了支援Python标準庫中的HTL解析器外,還支援一些第三方解析器(比如lxml)。

python3爬蟲(6)--使用Beautiful Soup解析資料
python3爬蟲(6)--使用Beautiful Soup解析資料

解析器選擇及使用方法:

通過以上對比可以看出,lxml解析器有解析HTML和XML的功能,而且速度快,容錯能力強,通常使用lxml。

使用時,在初始化Beautiful Soup時把第二個參數改為1xml即可:

from bs4 import BeautifulSoup

soup=BeautifulSoup('<p>Hello</p>,‘1xml')

print(soup.p.string)

重點使用:

2、節點選擇器

'''***********************************************節點選擇器*********************************************************'''
from bs4 import BeautifulSoup
html = '''
<div>
    <td class="nobr player desktop">
        <a href="bucks" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="ng-binding" target="_parent" 
        href1="/teams/#!/bucks"><!-- ngIf: row.clinched -->密爾沃基&nbsp;雄鹿<b>nba</b></a>
    </td>
    <tr data-ng-repeat="(i, row) in page" index="0" class="ng-scope">
        <td class="nobr center bold ng-binding" href="href01" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >6</td>
        <td class="nobr center bold desktop ng-binding">18&nbsp;-&nbsp;4</td>
        <td class="nobr center bold desktop ng-binding">勝 6</td>
        <td class="nobr center bold desktop ng-binding">119.5</td>
    </tr>
</div>
<p>
<li class="nobr player top">nba</li>
</p>
'''
soup=BeautifulSoup(html,'lxml') #初始化為BeautifulSoup的解析形式

'''*********************************節點選擇器21個知識點*************************'''
#标準化
r1=soup.prettify()#把要解析的字元串以标準的縮進格式輸出,同時有節點缺失或錯誤也可以自行更正
print("r1:",r1)

#節點簡單定位
r2=soup.div.td.a #定位到指定節點
print(type(r2)) #輸出:<class 'bs4.element.Tag'>
print("r2",r2) #輸出:<a class="ng-binding" href="bucks" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  href1="/teams/#!/bucks" target="_parent"><!-- ngIf: row.clinched -->密爾沃基 雄鹿<b>nba</b></a>

#擷取文本或者節點名稱
r3=soup.tr.td.string #調用string屬性,擷取指定标簽裡面的文本
print("r3",r3) #輸出:6。注意:有多個td節點,預設隻選擇第一個,後面的被忽略。
r4=soup.tr.td.get_text() #用.get_text(),擷取指定标簽裡面的文本
print("r4:",r4) #輸出:6
r5=soup.tr.td.name #用.name擷取節點的名稱
print('r5',r5) #輸出:td

#擷取屬性值
r6=soup.td.a['href'] #擷取某個屬性的值,簡寫式
print("r6",r6) #輸出:bucks
r7=soup.td.a.attrs  #以字典形式輸出某個節點的所有屬性-值
print('r7',r7)#{'href': 'bucks', 'class': ['ng-binding'], 'target': '_parent', 'href1': '/teams/#!/bucks'}
r8=soup.td.a.attrs['href'] #擷取某個屬性的值,字典索引式
print('r8',r8) # bucks

#擷取子節點或子孫節點
r9=soup.div.td.contents  #擷取td的直接子節點的清單,調用contents
print('r9',r9) #['\n', <a class="ng-binding" href="bucks" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  href1="/teams/#!/bucks" target="_parent"><!-- ngIf: row.clinched -->密爾沃基 雄鹿<b>nba</b></a>, '\n']
r10=soup.div.td.children  #擷取td的直接子節點的清單,調用children
print('r10',r10) #傳回生成器類型:<list_iterator object at 0x0000019741443470>
for i, child in enumerate(soup.div.td.children):
    print(i, child)#輸出:
    '''
    0 
    1 <a class="ng-binding" href="bucks" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  href1="/teams/#!/bucks" target="_parent"><!-- ngIf: row.clinched -->密爾沃基 雄鹿<b>nba</b></a>
    2 
    '''
r11=soup.div.td.descendants  #要得到所有的子孫節點的話,可以調用descendants屬性:
print("r11",r11) #傳回生成器類型:<list_iterator object at 0x0000019C618F1518>
for i01, child01 in enumerate(soup.div.td.descendants):
    print(i01, child01)#輸出:
    '''
    0 
    1 <a class="ng-binding" href="bucks" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  href1="/teams/#!/bucks" target="_parent"><!-- ngIf: row.clinched -->密爾沃基 雄鹿<b>nba</b></a>
    2  ngIf: row.clinched 
    3 密爾沃基 雄鹿
    4 <b>nba</b>
    5 nba
    6
    '''
#擷取父節點和祖先節點
r12=soup.a.parent #擷取某個節點元素的父節點,可以調用parent屬性:
print("r12",r12) #輸出:<td class="nobr player desktop">....</td>
r13=soup.a.parents #擷取所有的祖先節點,可以調用parents屬性:
print("r13",r13)#傳回生成器類型:<generator object PageElement.parents at 0x0000016669855480>
for i, parent in enumerate(soup.a.parents):
    print(i, parent) #輸出:略

#擷取兄弟節點
r14=soup.div.tr.td.next_sibling.next_sibling #擷取下面的第2個兄弟節點,很多時候把換行也算為一個節點
print('r14:',r14)#r14: <td class="nobr center bold desktop ng-binding">18 - 4</td>

r15=soup.p.previous_sibling.previous_sibling  #擷取上面的第2個兄弟節點,很多時候把換行也算為一個節點
print('r15:',r15) #r15: <div>......

r16=soup.div.tr.td.next_siblings   #擷取下面的所有兄弟節點
print(type(r16),r16) #<class 'generator'> <generator object PageElement.next_siblings at 0x000001E6F16F4390>
print(list(enumerate(soup.div.tr.td.next_siblings)))#轉化為清單元組輸出
# 輸出:[(0, '\n'), (1, <td class="nobr center bold desktop ng-binding">18 - 4</td>), (2, '\n'), (3, <td class="nobr center bold desktop ng-binding">勝 6</td>), (4, '\n'), (5, <td class="nobr center bold desktop ng-binding">119.5</td>), (6, '\n')]

r17=soup.p.previous_siblings  #擷取上面的所有兄弟節點
print(type(r17),r17)#<class 'generator'> <generator object PageElement.previous_siblings at 0x0000026F649C3390>
print(list(enumerate(soup.p.previous_siblings)))
# 輸出:[(0, '\n'), (1, <div>......)]

r18=soup.div.tr.td.next_sibling.next_sibling.string #擷取文本資訊,previous同理
print("r18:",r18) #r18: 18 - 4

r19=list(soup.div.tr.td.next_siblings)[1].string #擷取文本資訊,previous同理
print('r19:',r19)#r19: 18 - 4

r20=soup.div.tr.td.next_sibling.next_sibling.attrs["class"]#擷取屬性值,previous同理
print(r20) #['nobr', 'center', 'bold', 'desktop', 'ng-binding']

r21=list(soup.div.tr.td.next_siblings)[1].attrs["class"] #擷取擷取屬性值,previous同理
print('r21:',r21)#r18:['nobr', 'center', 'bold', 'desktop', 'ng-binding']
           

3、方法選擇器

'''*****************************************************方法選擇器****************************************************'''
from bs4 import BeautifulSoup
import re
html = '''
<div>
    <td class="nobr player desktop">
        <a href="bucks" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  class="ng-binding" target="_parent" 
        href1="/teams/#!/bucks"><!-- ngIf: row.clinched -->密爾沃基&nbsp;雄鹿<b>nba</b></a>
    </td>
    <tr data-ng-repeat="(i, row) in page" index="0" class="ng-scope">
        <td class="nobr center bold ng-binding" href="href01" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" >6</td>
        <td class="nobr center bold desktop ng-binding">18&nbsp;-&nbsp;4</td>
        <td class="nobr center bold desktop ng-binding">勝 6</td>
        <td class="nobr center bold desktop ng-binding">119.5</td>
    </tr>
</div>
<p>
<li class="nobr player top">nba</li>
</p>
'''
soup=BeautifulSoup(html,'lxml') #初始化為BeautifulSoup的解析形式

'''****************方法選擇器:find_al1()和find()的使用**********************************************'''
#find_al1(name,attrs,recursive,text,limit,**kwargs):查詢所有符合條件的元素
    # name:通常為節點名;
    # attrs:屬性限制;
    # text:text參數可用來比對節點的文本,傳入的形式可以是字元串,可以是正規表達式對象;
    # limit:該參數可以限制得到的結果的數目;
    # recursive=False/True:遞歸,False隻會找到該對象的最近後代,True預設值找到全部後代;
    # 關鍵詞參數 keyword,自己選擇那些具有指定屬性的标簽
    # 注意:多數情況下find_all()和findAll()等價,
    # 其中:bsObj.findAll(class='text')#會報錯 bsObj.findAll(class_='text')#解決方案
#案例:
ls=[]
for di in soup.find_all(name='td'):
    ls.append(di.get_text()) #di.get_text()和di.string差不多,但get_text()更強大
print(ls) #['\n密爾沃基\xa0雄鹿nba\n', '6', '18\xa0-\xa04', '勝 6', '119.5']

ats1=soup.find_all('td',{'class':"nobr center bold ng-binding"})[0].get_text()
ats2=soup.find_all(name='td',attrs={'class':"nobr center bold ng-binding",'href':"href01"})[0].string
print(ats1,ats2) # 6 6

t1=soup.find_all(text=re.compile('(.*)雄鹿(.*)'))
print(t1) #['密爾沃基\xa0雄鹿']

ks1=soup.find_all(href="href01" target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow"  target="_blank" rel="external nofollow" )[0].get_text()
print(ks1) # 6

#find(name,attrs,recursive,text,limit,**kwargs):查詢第一個符合條件的元素,方法和find_all()完全一樣,隻是傳回值的範圍不一樣
#案例
f1=soup.find(name='a',attrs={"href":"bucks","class":"ng-binding"})
print(f1.get_text()) #密爾沃基 雄鹿nba

# 其他:
# find_parents()和find_parent():前者傳回所有祖先節點,後者傳回直接父節點。
# find_next_siblings()和find_next_sibling():前者傳回後面所有的兄弟節點,後者傳回後面第一個兄弟節點。
# find_previous_siblings()和find_previous_sibling():前者傳回前面所有的兄弟節點,後者傳回前面第一個兄弟節點。
# find_al1_next()和find_next():前者傳回節點後所有符合條件的節點,後者傳回第一個符合條件的節點。
# find_all_previous()和find_previous():前者傳回節點後所有符合條件的節點,後者傳回第一個符合條件的節點。
           

Beautiful Soup解析也支援CSS選擇器,但是他并沒有使用pyquery解析強大,是以這裡我們将不再深入。

繼續閱讀