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)。
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

解析器選擇及使用方法:
通過以上對比可以看出,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" 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">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" 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" 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" 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" 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" 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">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")[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解析強大,所以這裏我們將不再深入。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章