網絡爬蟲名稱:bilibili彈幕網視頻日排行榜數據分析
網絡爬蟲爬取的內容:bilibili彈幕網視頻日排行榜
設計方案概述:
實現思路:爬取網站html源代碼,通過頁面分析得到想要的數據位置,提取數據,之后數據可視化等操作
技術難點: html源碼過於雜亂,難以提取數據
首先進行頁面分析
通過頁面分析,我們得知我們需要的關鍵數據在"li"分支
將"li"分支展開,進一步分析
通過對"li"分支的分析,我們基本確定了播放量,彈幕數,綜合得分等關鍵信息所在的位置
接下來開始爬取
import requests as rq from bs4 import BeautifulSoup import pandas as pd import matplotlib.pyplot as plt url = "https://www.bilibili.com/ranking/all/0/0/1" header = {"User-Agent":"Mozilla/5.0"} res = rq.get(url,headers=header,timeout=15) res.raise_for_status() res.encoding=res.apparent_encoding soup = BeautifulSoup(res.text)
使用requests庫爬取,並通過BeautifulSoup庫存放到變量Soup中,這里展示一下爬取到的html,可以看到是非常雜亂的
然后開始對數據進行提取,首先是獲取視頻標題,同樣的,我輸出一下提取到的標題
#獲取視頻標題 title = [] for dtitle in soup.find_all('img'): title.append(dtitle.get('alt'))
接下來同理,提取一些關鍵信息,因數據略大,就不輸出展示了
#獲取視頻的播放量,彈幕數和UP主 cl_soup=soup.find_all(attrs={'class':'data-box'}) play_view_author = [] for cl_soup in cl_soup: play_view_author.append(cl_soup.text) play = play_view_author[0:300:3] #播放量 view = play_view_author[1:300:3] #彈幕數 author = play_view_author[2:300:3] #UP主 #獲取視頻的BV號 dBV = soup.find_all(attrs={'class':'title'}) link = [] for dBV in dBV: link.append(dBV.get('href')) for i in range(link.count(None)): link.remove(None) BV = [] for k in range(len(link)): BV.append(link[k][-12:]) #獲取視頻的綜合得分 soup_pts=soup.find_all(attrs={'class':'pts'}) pts = [] for soup_pts in soup_pts: pts.append(eval(soup_pts.text[:-15]))
好的,現在關鍵信息已經獲取完成,將其通過pandas庫保存為csv表格方便稍后進行數據分析可視化調用
dt = {'排名':range(1,101),'標題':title,'播放量':play,'彈幕數':view,'綜合得分':pts,'UP主':author,'BV號':BV} #創建字典 data = pd.DataFrame(dt) #以字典形式創建DataFrame import datetime date = datetime.datetime.now().strftime('%Y-%m-%d') #獲取當前日期方便保存 data.to_csv('{}.csv'.format(date),index=False,header=True,encoding="utf-8-sig",mode="a") #保存為csv
此處應該注意,這里的保存時一定要寫編碼方式,否則很有可能保存的csv出現亂碼的現象
接下來對數據進行清洗並可視化分析
讀入csv文件
import pandas as pd import matplotlib.pyplot as plt from scipy.optimize import leastsq import numpy as np import datetime date = datetime.datetime.now().strftime('%Y-%m-%d') #獲取當前日期 filename = '{}.csv'.format(date) colnames=["ranking","title","play","view","pts","author","BV"] data=pd.read_csv(filename,skiprows=1,names=colnames) plt.rcParams['font.sans-serif']=['SimHei'] #正常顯示中文 plt.rcParams['axes.unicode_minus']=False #正常顯示負號
由於從html中獲得的播放量和彈幕數是帶有"萬"單位的,我們先對其進行轉化
play = [] pl = list(data.play) for pl in pl: play.append(eval(pl[:-1])*10000) #將播放量轉化為數字 view = [] vw = list(data.view) for i in vw: if i[-1]=="萬": view.append(eval(i[:-1])*10000) else: view.append(eval(i)) #將彈幕數轉化為數字
刪除無用的列,並將轉換好的播放量和彈幕數替換進去
del data["title"] del data["author"] del data["BV"] #刪除無用列 play_s = pd.Series(play) view_s = pd.Series(view) data["play"] = play_s data["view"] = view_s #將轉換好的彈幕數和播放量重新寫入DataFrame中
重復值處理
data.duplicated() #重復值處理 #空值已在HTML處理時刪除了,此處不再進行空值檢測
查看相關性以及統計數據
data.corr() #此處可以看出播放量與綜合得分得相關性最高 data.describe() #獲取統計信息
數據清洗到這里基本結束,接下來進行數據可視化分析
首先使用直方圖對綜合得分,播放量和彈幕數進行統計
綜合得分
plt.figure(dpi=240) ranking = data.ranking score = data.pts plt.bar(ranking,score,color=[0,0,0.8,0.6]) plt.title("綜合得分直方圖") plt.xlabel("排名") plt.ylabel("綜合得分") plt.show()
播放量:
plt.figure(dpi=240) plt.bar(ranking,play,color=[1,1,0]) plt.title("播放量直方圖") plt.xlabel("排名") plt.ylabel("播放量") plt.show()
彈幕數:
plt.figure(dpi=240) plt.bar(ranking,view,color=[1,0,1,0.8]) plt.title("彈幕數直方圖") plt.xlabel("排名") plt.ylabel("彈幕數") plt.show()
排名與播放量,彈幕數,綜合得分的散點圖和擬合直線
在此之前,先定義好回歸方程函數以及將各列表轉換為numpy數組
def ft(p,x): a,b,c=p return a*(x**2)+(b*x)+c def er_ft(p,x,y): return ft(p,x)-y play_np = np.array(play) score_np = np.array(score) view_np = np.array(view) ranking_np = np.array(ranking) p0=[2,3,4]
做好這個就可以開始了,首先是排名與綜合得分
plt.figure(dpi=240) plt.scatter(ranking,score,label=u'樣本數據',color=[0,0.6,0,0.8]) P=leastsq(er_ft,p0,args=(ranking_np,score_np)) a,b,c=P[0] x=np.linspace(0,100,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.title('綜合得分散點圖&擬合直線') plt.xlabel("排名") plt.ylabel("綜合得分") plt.legend() plt.grid() plt.show()
排名/播放量
plt.figure(dpi=240) plt.scatter(ranking,play,label=u'樣本數據',color=[0,0,0.8,0.8]) P=leastsq(er_ft,p0,args=(ranking_np,play_np)) a,b,c=P[0] x=np.linspace(0,100,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.title('播放量散點圖&擬合直線') plt.xlabel("排名") plt.ylabel("播放量") plt.legend() plt.grid() plt.show()
排名/彈幕數:
plt.figure(dpi=240) plt.scatter(ranking,view,label=u'樣本數據',color=[0.5,1,1,0.8]) P=leastsq(er_ft,p0,args=(ranking_np,view_np)) a,b,c=P[0] x=np.linspace(0,100,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.title('彈幕數散點圖&擬合直線') plt.xlabel("排名") plt.ylabel("彈幕數") plt.legend() plt.grid() plt.show()
接下來是綜合得分,播放量,彈幕數三者直接的散點圖和回歸方程
彈幕數/播放量:
plt.figure(dpi=240) plt.scatter(play,view,label=u'樣本數據',color=[0,0,0,0.6]) plt.title('彈幕數/播放量散點圖&擬合直線') P=leastsq(er_ft,p0,args=(play_np,view_np)) a,b,c=P[0] x=np.linspace(0,3500000,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.xlabel("播放量") plt.ylabel("彈幕數") plt.legend() plt.grid() plt.show()
綜合得分/播放量
plt.figure(dpi=240) plt.scatter(play,score,label=u'樣本數據',color=[1,0.5,0,0.6]) plt.title('綜合得分/播放量散點圖&擬合直線') P=leastsq(er_ft,p0,args=(play_np,score_np)) a,b,c=P[0] x=np.linspace(0,3500000,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.xlabel("播放量") plt.ylabel("綜合得分") plt.legend() plt.grid() plt.show()
綜合得分/彈幕數
plt.figure(dpi=240) plt.scatter(view,score,label=u'樣本數據',color=[1,1,0,0.6]) plt.title('綜合得分/彈幕數散點圖&擬合直線') P=leastsq(er_ft,p0,args=(view_np,score_np)) a,b,c=P[0] x=np.linspace(0,55000,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.xlabel("彈幕數") plt.ylabel("綜合得分") plt.legend() plt.grid() plt.show()
數據可視化基本完成
數據爬取提取部分代碼匯總
import requests as rq from bs4 import BeautifulSoup import pandas as pd import matplotlib.pyplot as plt url = "https://www.bilibili.com/ranking/all/0/0/1" header = {"User-Agent":"Mozilla/5.0"} res = rq.get(url,headers=header,timeout=15) res.raise_for_status() res.encoding=res.apparent_encoding soup = BeautifulSoup(res.text) #獲取視頻標題 title = [] for dtitle in soup.find_all('img'): title.append(dtitle.get('alt')) #獲取視頻的播放量,彈幕數和UP主 cl_soup=soup.find_all(attrs={'class':'data-box'}) play_view_author = [] for cl_soup in cl_soup: play_view_author.append(cl_soup.text) play = play_view_author[0:300:3] #播放量 view = play_view_author[1:300:3] #彈幕數 author = play_view_author[2:300:3] #UP主 #獲取視頻的BV號 dBV = soup.find_all(attrs={'class':'title'}) link = [] for dBV in dBV: link.append(dBV.get('href')) for i in range(link.count(None)): link.remove(None) BV = [] for k in range(len(link)): BV.append(link[k][-12:]) #獲取視頻的綜合得分 soup_pts=soup.find_all(attrs={'class':'pts'}) pts = [] for soup_pts in soup_pts: pts.append(eval(soup_pts.text[:-15])) dt = {'排名':range(1,101),'標題':title,'播放量':play,'彈幕數':view,'綜合得分':pts,'UP主':author,'BV號':BV} #創建字典 data = pd.DataFrame(dt) #以字典形式創建DataFrame import datetime date = datetime.datetime.now().strftime('%Y-%m-%d') #獲取當前日期方便保存 data.to_csv('{}.csv'.format(date),index=False,header=True,encoding="utf-8-sig",mode="a") #保存為csv
數據清洗以及可視化分析部分代碼匯總
import pandas as pd import matplotlib.pyplot as plt from scipy.optimize import leastsq import numpy as np import datetime date = datetime.datetime.now().strftime('%Y-%m-%d') #獲取當前日期 filename = '{}.csv'.format(date) colnames=["ranking","title","play","view","pts","author","BV"] data=pd.read_csv(filename,skiprows=1,names=colnames) plt.rcParams['font.sans-serif']=['SimHei'] #正常顯示中文 plt.rcParams['axes.unicode_minus']=False #正常顯示負號 play = [] pl = list(data.play) for pl in pl: play.append(eval(pl[:-1])*10000) #將播放量轉化為數字 view = [] vw = list(data.view) for i in vw: if i[-1]=="萬": view.append(eval(i[:-1])*10000) else: view.append(eval(i)) #將彈幕數轉化為數字 del data["title"] del data["author"] del data["BV"] #刪除無用列 play_s = pd.Series(play) view_s = pd.Series(view) data["play"] = play_s data["view"] = view_s #將轉換好的彈幕數和播放量重新寫入DataFrame中 data.duplicated() #重復值處理 #空值已在HTML處理時刪除了,此處不再進行空值檢測 data.corr() #此處可以看出播放量與綜合得分得相關性最高 data.describe() #獲取統計信息 plt.figure(dpi=240) ranking = data.ranking score = data.pts plt.bar(ranking,score,color=[0,0,0.8,0.6]) plt.title("綜合得分直方圖") plt.xlabel("排名") plt.ylabel("綜合得分") plt.show() plt.figure(dpi=240) plt.bar(ranking,play,color=[1,1,0]) plt.title("播放量直方圖") plt.xlabel("排名") plt.ylabel("播放量") plt.show() plt.figure(dpi=240) plt.bar(ranking,view,color=[1,0,1,0.8]) plt.title("彈幕數直方圖") plt.xlabel("排名") plt.ylabel("彈幕數") plt.show() def ft(p,x): a,b,c=p return a*(x**2)+(b*x)+c def er_ft(p,x,y): return ft(p,x)-y play_np = np.array(play) score_np = np.array(score) view_np = np.array(view) ranking_np = np.array(ranking) p0=[2,3,4] plt.figure(dpi=240) plt.scatter(ranking,score,label=u'樣本數據',color=[0,0.6,0,0.8]) P=leastsq(er_ft,p0,args=(ranking_np,score_np)) a,b,c=P[0] x=np.linspace(0,100,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.title('綜合得分散點圖&擬合直線') plt.xlabel("排名") plt.ylabel("綜合得分") plt.legend() plt.grid() plt.show() plt.figure(dpi=240) plt.scatter(ranking,play,label=u'樣本數據',color=[0,0,0.8,0.8]) P=leastsq(er_ft,p0,args=(ranking_np,play_np)) a,b,c=P[0] x=np.linspace(0,100,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.title('播放量散點圖&擬合直線') plt.xlabel("排名") plt.ylabel("播放量") plt.legend() plt.grid() plt.show() plt.figure(dpi=240) plt.scatter(ranking,view,label=u'樣本數據',color=[0.5,1,1,0.8]) P=leastsq(er_ft,p0,args=(ranking_np,view_np)) a,b,c=P[0] x=np.linspace(0,100,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.title('彈幕數散點圖&擬合直線') plt.xlabel("排名") plt.ylabel("彈幕數") plt.legend() plt.grid() plt.show() plt.figure(dpi=240) plt.scatter(play,view,label=u'樣本數據',color=[0,0,0,0.6]) plt.title('彈幕數/播放量散點圖&擬合直線') P=leastsq(er_ft,p0,args=(play_np,view_np)) a,b,c=P[0] x=np.linspace(0,3500000,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.xlabel("播放量") plt.ylabel("彈幕數") plt.legend() plt.grid() plt.show() plt.figure(dpi=240) plt.scatter(play,score,label=u'樣本數據',color=[1,0.5,0,0.6]) plt.title('綜合得分/播放量散點圖&擬合直線') P=leastsq(er_ft,p0,args=(play_np,score_np)) a,b,c=P[0] x=np.linspace(0,3500000,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.xlabel("播放量") plt.ylabel("綜合得分") plt.legend() plt.grid() plt.show() plt.figure(dpi=240) plt.scatter(view,score,label=u'樣本數據',color=[1,1,0,0.6]) plt.title('綜合得分/彈幕數散點圖&擬合直線') P=leastsq(er_ft,p0,args=(view_np,score_np)) a,b,c=P[0] x=np.linspace(0,55000,1000) y=a*(x**2)+(b*x)+c plt.plot(x,y,color="red",label=u"擬合直線",linewidth=2) plt.xlabel("彈幕數") plt.ylabel("綜合得分") plt.legend() plt.grid() plt.show()
結論:數據可視化分析可以得知,彈幕數與播放量相關性並不高,但是綜合得分是由播放量和彈幕數共同影響的
程序設計小結:此次程序設計重難點在於對html的數據提取,以前看到一大片密密麻麻的html就感到頭疼,而通過此次程序設計,我能更加敢於面對龐大雜亂的數據;而在程序設計過程中出現的各種問題得到解決都給我帶來一定的成就感,也讓我對Python這門編程語言有了更深層的了解.