爬蟲入門二 beautifulsoup
title: 爬蟲入門二 beautifulsoup
date: 2020-03-12 14:43:00
categories: python
tags: crawler
使用beautifulsoup解析資料
1 beautifulsoup簡介
BeautifulSoup 是一個可以從HTML或XML檔案中提取資料的Python庫.它能夠通過轉換器實現文件導航、查詢、修改。
pip install beautifulsoup4
http://beautifulsoup.readthedocs.io/zh_CN/latest/
2 前端知識
HTTP:HyperText Markup Language 超文字標記語言
CSS:Cascading Style Sheets 層疊樣式表
JAVASCRIPT:一種指令碼語言,其原始碼在發往客戶端執行之前不需經過編譯,而是將文字格式的字元程式碼傳送給瀏覽器由瀏覽器解釋執行
XML:Extensible Markup Language可擴充套件標記語言
XML是被設計用來描述資料的,HTML是被設計用來顯示資料的。與HTML相比,XML支援動態更新,標準性更強
3 beautifulsoup解析器
比如BeautifulSoup(demo, 'html.parser') 中的html.parser
4 html結構
<></>構成所屬關係。樹形結構。下行,上行,平行遍歷。
5 使用beautifulsoup解析HTML
注意B和S大寫,Python對大小寫敏感
from bs4 import BeautifulSoup import requests def html(): # 得到未解析的HTML網頁內容 r = requests.get("https://whu.edu.cn/coremail/common/index_cm40.jsp") print(r.text) # 得到解析的HTML網頁內容 demo = r.text soup = BeautifulSoup(demo, 'html.parser') print(soup.prettify())
6 BeautifulSoup 類的基本元素與操作
Tag,標籤<></>開始結束
name,標籤名,
的name是p。
attributes,標籤屬性(字典形式組織)。
navigablestring,標籤內非屬性字串.
comment,標籤內字串的註釋部分
下面是例子:
可以看到,web中有很多a標籤,,而這裡只返回了第一個
from bs4 import BeautifulSoup import requests def html(): # 得到未解析的HTML網頁內容 r = requests.get("https://whu.edu.cn/coremail/common/index_cm40.jsp") print(r.text) # 得到解析的HTML網頁內容 demo = r.text soup = BeautifulSoup(demo, 'html.parser') #print(soup.prettify()) print(soup.title) print(soup.a) #<title>武漢大學郵件系統</title> #<a class="MTLinks">設為首頁</a>
HTML中a錨文字A超連結標籤格式: 被連結內容
6.1 BeautifulSoup標籤操作
#檢視標籤a的屬性
tag=soup.a
tag.attrs
#檢視標籤a的屬性中class的值
tag.attrs['class']
#檢視標籤a的屬性 的型別
type(tag.attrs)
#檢視標籤a的非屬性字串
soup.a.string
#檢視標籤a的非屬性字串屬性 的型別
type(tag.a.string)
#返回值為bs4.element.NavigableString (可以遍歷的字串)
from bs4 import BeautifulSoup
import requests
def html():
# 得到未解析的HTML網頁內容
r = requests.get("https://whu.edu.cn/coremail/common/index_cm40.jsp")
print(r.text)
# 得到解析的HTML網頁內容
demo = r.text
soup = BeautifulSoup(demo, 'html.parser')
#print(soup.prettify())
print(soup.title)
print(soup.a)
# 檢視標籤a的屬性
tag = soup.a
print(tag.attrs)
# 檢視標籤a的屬性中class的值
tag.attrs['class']
# 檢視標籤a的屬性 的型別
print(type(tag.attrs))
# 檢視標籤a的非屬性字串
print(tag.string)
# 檢視標籤a的非屬性字串屬性 的型別
print(type(tag.string))
# 返回值為bs4.element.NavigableString (可以遍歷的字串)
7 html遍歷
上行,下行,平行。
7.1 標籤樹下行遍歷 .content .children .descendants
7.1.1 .content
.contents 子節點的列表,將
soup.body.contents
#獲得孩子節點的個數
len(soup.body.contents)
#分別輸出各個子節點
soup.body.contents[0]
soup.body.contents[2]
#標號 由0開始
7.1.2 .children/.descendants
.contents 和 .children 屬性僅包含tag的直接子節點,.descendants 屬性可以對所有tag的子孫節點進行遞迴迴圈
需要遍歷獲取其中的內容。
for child in soup.body.children:
print(child)
for child in soup.body.descendants:
print(child)
def htmlergodic():
# 得到未解析的HTML網頁內容
r = requests.get("https://whu.edu.cn/coremail/common/index_cm40.jsp")
#print(r.text)
# 得到解析的HTML網頁內容
demo = r.text
soup = BeautifulSoup(demo, 'html.parser')
# 子節點的列表,將<tag>所有兒子節點存入列表
#print(soup.body.contents)
# 獲得孩子節點的個數
print(len(soup.body.contents))
# 分別輸出各個子節點
print(soup.body.contents[0])
print(soup.body.contents[2])
# 標號 由0開始
for child in soup.body.children:
print(child)
for child in soup.body.descendants:
print(child)
7.2 標籤樹上行遍歷 .parent/.parents
# a的父節點
soup.a.parent.name
# 遍歷
for parent in soup.a.parents: #注意 s
print(parent.name)
7.3 標籤樹平行遍歷 next_sibling/Previous_sibling
#獲得a節點的上一個節點和下一個節點
soup.a.next_sibling
soup.a.previous_sibling
for sibling in soup.a.next_siblings: #注意s
print(sibling)
8 bs4的庫的prettify()方法
BeautifulSoup 是bs4庫的類,prettify()是方法。
.prettify()為HTML文字<>及其內容增加換行符
可以用於整個HTML文字,也可以用於單個標籤
方法:
bs4庫將任何HTML輸入都變成utf‐8編碼Python 3.x預設支援編碼是utf‐8,解析無障礙
print(soup.body.prettify())
9 BeautifulSoup資訊檢索 注意find標籤名要加''表示字串
9.1 .find_all() 搜尋並返回全部結果
<>.find_all(name,attrs,recursive,string,**kwargs)
def jiansuo():
r = requests.get("http://www.baidu.com/")
r.encoding = r.apparent_encoding
demo = r.text
soup = BeautifulSoup(demo, 'html.parser')
print(soup.find_all('a'))
9.2 引數
name: 對標籤名稱的檢索字串
attrs: 對標籤屬性值的檢索字串,可標註屬性檢索
recursive: 是否對子孫全部檢索,預設True
string: <>…</>中字串區域的檢索字串
#搜尋 class=“mnav” 的全部a標籤
print(soup.find_all('a','mnav'))
# 搜尋字串為新聞的全部標籤
print(soup.find_all(string='新聞'))
如果要部分匹配,則需要匯入正則表示式庫
9.3 擴充套件方法
<>.find()
<>.find_parents()
<>.find_parent()
<>.find_next_sibling()
<>.find_next_siblings()
<>.find_previous_sibling()
<>.find_previous_siblings()
10 例項
10.1 爬取武漢大學官方網站,含'櫻'的新聞連結標題
匯入requests庫、BeautifulSoup類、re庫
使用for迴圈,查詢條件string=re.compile('櫻'))
搜尋型別為新聞連結,因此型別為‘a’
輸出滿足條件的tag的字串
import re
import requests
from bs4 import BeautifulSoup
def sakura():
r = requests.get("http://www.whu.edu.cn/")
r.encoding = r.apparent_encoding
demo = r.text
soup = BeautifulSoup(demo,'html.parser')
for tag in soup.find_all('a', string=re.compile('櫻')):
print(tag.string)
10.2 爬取 百度網際網路熱門人物排行
訪問百度搜索風雲榜
人物--網際網路人物
http://top.baidu.com/buzz?b=257&c=9&fr=topcategory_c9
右鍵點選 “馬雲”--檢查,檢視對應的HTML程式碼
<a class="list-title" target="_blank" href="http://www.baidu.com/baidu?cl=3&tn=SE_baiduhomet8_jmjb7mjw&rsv_dl=fyb_top&fr=top1000&wd=%C2%ED%D4%C6" href_top="./detail?b=257&c=9&w=%C2%ED%D4%C6">馬雲</a>
分析可知人物連線標籤名為a,class為list-title
使用soup.find_all(‘a’, ‘list-title’ ) 輸出class為list-title
的a標籤的字串,即可書序得到排行榜的人物名單。
同時在前面加上他們索引序號index(tag.string)+1
即可得到今日網際網路人物排行榜
def fyrw():
r=requests.get("http://top.baidu.com/buzz?b=257&fr=topboards")
r.encoding=r.apparent_encoding
demo=r.text
soup=BeautifulSoup(demo, 'html.parser')
ulist = []
for tag in soup.find_all('a', 'list-title' ):
ulist.append(tag.string)
print(ulist.index(tag.string)+1,ulist[ulist.index(tag.string)])
10.3 爬取 中國大學排行榜2016
從網路上獲取大學排名網頁內容
getHTMLText()
提取網頁內容中資訊到合適的資料結構
fillUnivList()
利用資料結構展示並輸出結果
printUnivList()
http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html
檢查網頁程式碼看到結構是
<tbody>
<tr>
<td> 排位
<td> 大學名...
<tr>
...
這樣就遍歷
標籤的每個def getHTMLText(url):
try:
r = requests.get(url, timeout=30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def fillUnivList(ulist, html):
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children: #提取tbody的每個tr標籤
if isinstance(tr,bs4.element.Tag): #判斷tr是否是tag型別
tds = tr('td') #tds[0] <td>1</td>.簡寫,等價於下一行程式碼
#tds = tr.find_all('td')
# ,然後用.string取string
ulist.append([tds[0].string, tds[1].string, tds[3].string])
def printUnivList(ulist, num):
print("{:^10}\t{:^6}\t{:^10}".format("排名", "學校名稱", "總分"))
for i in range(num):
u = ulist[i]
print("{:^10}\t{:^6}\t{:^10}".format(u[0], u[1], u[2]))
def main():
uinfo = []
url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html'
html = getHTMLText(url)
fillUnivList(uinfo, html)
printUnivList(uinfo, 20) # 20 univs
上面的問題是並沒有對齊。
原因是當中文字元寬度不夠時,採用西文字元填充;中西文字元佔用寬度不同。
解決是採用中文字元的空格填充chr(12288)
def printUnivList(ulist, num):
tplt = "{0:^4}\t{1:{3}^12}\t{2:^10}"
print(tplt.format("排名", "學校名稱", "總分", chr(12288)))
for i in range(num):
u = ulist[i]
print(tplt.format(u[0], u[1], u[2], chr(12288)))
tplt為定義的輸出格式模板變數,^代表居中,4/12/10代表輸出寬度(當輸出資料超過該數字時,以實際輸出為準),
{3}代表列印輸出時,我們使用format中的第3個變數(由0起始),也就是 chr(12288)
chr(12288)代表全形Unicode空格(即中文空格)
# :是引導符號
# : <填充><對齊><寬度>,<精度><型別> 這裡的 ','是千位分隔符
# 拿上面的程式碼舉例
#tply:0,1,2對應輸出的第0,1,2個數據。^表示居中,然後第0個數據寬4.中間{3}表示填充用format第3個變數也就是chr(12288)
10.4 最好大學2017.遇坑
同樣的程式碼爬
http://www.zuihaodaxue.cn/zuihaodaxuepaiming2017.html
報錯
TypeError: unsupported format string passed to NoneType.format
#2016
<tr class="alt"><td>1</td>
<td><div align="left">清華大學</div></td>
#2017
<tr class="alt"><td>1<td><div align="left">清華大學</div></
注意這裡有坑,chrome檢查,在console看到是沒區別的(2017也是
檢查頁面原始碼發現和2016比不同之處在於子節點“1”所對應的地方並非是一個
由於字元1仍是第一的
重新修改的程式碼如下
def fillUnivList(ulist, html):
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children: #提取tbody的每個tr標籤
if isinstance(tr,bs4.element.Tag): #判斷tr是否是tag型別
tds = tr('td') #tds[0] <td>1</td>.簡寫,等價於下一行程式碼
#tds = tr.find_all('td')
# ,然後用.string取string
ulist.append([tds[0].contents[0].string, tds[1].string, tds[3].string])
10.5 2016世界大學排名,遇坑
http://www.zuihaodaxue.cn/ARWU2016.html
先用了2016中國排名的方法,然後報錯,然後看原始碼,沒看出來不同。
就把爬的內容列印一下
發現大學名字列印的是none
然後對比2016中國和2016大學.
<td class="align-left">
<a href="World-University-Rankings/Harvard-University.html" target="_blank">哈佛大學</a>
</td>
<td><div align="left">清華大學</div></td>
不明白就去查,
https://blog.csdn.net/github_36669230/article/details/66973617
用 .string 屬性來提取標籤裡的內容時,該標籤應該是隻有單個節點的。比如上面的
那就直接提取.string就行了
def fillUnivList(ulist, html):
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children: #提取tbody的每個tr標籤
if isinstance(tr,bs4.element.Tag): #判斷tr是否是tag型別
tds = tr('td') #tds[0] <td>1</td>.簡寫,等價於下一行程式碼
#tds = tr.find_all('td')
# ,然後用.string取string
tmp=tds[1].find('a')
#print(tds[0].string, tmp.string, tds[3].string)
ulist.append([tds[0].string, tmp.string, tds[3].string])
11 相關網站
0.北理工課程
https://www.bilibili.com/video/av9784617?from=search&seid=12715531491861423376
1.廖雪峰官方網站
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
2.Python 知識庫
http://lib.csdn.net/python/node/68
- Python網路爬蟲與資訊提取(MOOC)
http://www.icourse163.org/course/BIT-1001870001#/info
4.W3School 網頁前端學習及線上編輯
http://www.w3school.com.cn/h.asp
5.BeautifulSoup 官方文件
https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/