python網絡爬蟲——爬取嗶哩嗶哩網站的番劇排行榜和其中各番劇詳情頁信息


(一)、選題的背景

因為我是個動漫愛好者,所以很喜歡看動漫劇,又叫做番劇,所以我都特別關注嗶哩嗶哩的動漫番劇排行榜的各番劇排名,評分,觀看次數等等。

但是我不知道這幾個數量值有什么關聯。

所以我選擇爬取bilibili的番劇綜合排行榜的排名,番劇名,番劇鏈接,播放量,收藏量,評分,介紹。

並分析其中的排名,播放量,收藏量,評分幾個數量之間是否有明顯的聯系。

(二)、主題式網絡爬蟲設計方案

1.主題式網絡爬蟲名稱

 python爬取嗶哩嗶哩網站的番劇排行榜和其中各番劇詳情頁信息

2.主題式網絡爬蟲爬取的內容與數據特征分析

 番劇綜合排行榜的排名,番劇名,番劇鏈接,介紹為文本類型。

播放量,收藏量,評分為數值類型。

3.主題式網絡爬蟲設計方案概述

先設計好爬取代碼,然后處理好數據后將其記錄在表格和數據表中,保存並進行數據分析。

(三)、主題頁面的結構特征分析

1.主題頁面的結構與特征分析

我的目的是要爬取bilibili的番劇綜合排行榜的排名,番劇名,番劇鏈接,播放量,收藏量,評分,介紹。

而排行榜網頁中只包含:排名,番劇名,番劇鏈接,播放量,收藏量。

 

 

 

 

 

 

若想要知道它們的評分與番劇介紹需要進入詳情頁面查找。

 

 

2.Htmls 頁面解析

排行榜里的各條信息都包含在這個li標簽中

 

詳情頁面中的評分和介紹都包含在這個div標簽中

 

 

 

 

 

 

 

 

 

3.節點(標簽)查找方法與遍歷方法

經過分析,我打算先從第一個頁面(排行榜頁面)查找出每條符合條件的li標簽,再逐個分析,從中提取想要的信息,比如說番劇名稱,播放量,收藏數,番劇鏈接。

再通過爬取上一步提取的每條番劇鏈接,從第二個頁面查找出每條符合條件的div標簽,再逐個分析,從中提取想要的信息。

(四)、網絡爬蟲程序設計

數據爬取與

首先開始編寫獲取網頁信息的函數(因為我們需要用到該代碼兩次,所以寫為函數比較方便):

 1 def get(url):#獲取網頁信息函數
 2     head = {
 3          "User-Agent": "Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome / 96.0.4664.110 Safari / 537.36"
 4     }
 5     #模擬瀏覽器頭部信息
 6     request = urllib.request.Request(url, headers=head)
 7     #攜帶着這個包含着我的設備型號的頭部信息去訪問這個網址
 8     html = ""
 9     #用字符串對它進行存儲
10     time.sleep(0.1)
11     #添加延時,防止錯誤
12     
13     try:
14         #處理可能會發生的錯誤,比如說網絡問題等,並輸出錯誤代碼,如404
15         response = urllib.request.urlopen(request)
16         html = response.read().decode("utf-8")  
17         
18     except urllib.error.URLError as e:
19         
20         if hasattr(e, "code"):
21             print(e.code)
22             
23         if hasattr(e, "reason"):
24             print(e.reason) 
25             
26     soup = BeautifulSoup(html,"html.parser")
27     #解析數據
28     return soup

測試輸出獲取的信息,分析信息內容得知該網頁可以用這種方式爬取我們想要的數據。

 

 

通過網頁檢查工具得知,排名里的番劇信息都分別包含於一個class="rank-item"的標簽li之中,所以

1 for item1 in soup.find_all('li',class_="rank-item"):
2 # 查找符合條件的li標簽,逐一分析即遍歷(在輸出的標簽li列表中,逐一進行查找標簽li中我們需要的信息,並保存於新的列表datalist中)

測試輸出了一條符合條件的li標簽,分析其中的內容:得到想要的信息位置特征。

比如說包含番劇鏈接信息的文本的前面有“<a href="文本,后面有"" target"文本

 用re庫正則表達式提取想要的信息,例如:

1 f_link = re.compile(r'(.*?)" target')
2 # 查找番劇鏈接
3 f_title = re.compile(r'<a class="title".*target="_blank">(.*?)</a>')
4 # 查找番劇標題
5 f_watchtext = re.compile(r'<img alt="play".*/>(.*?)</span> <span class="data-box">',re.S)
6 # 查找番劇播放量
7 f_liketext = re.compile(r'<img alt="follow".*/>(.*?)</span>',re.S)
8 # 查找番劇收藏數

想要獲取番劇的評分和介紹,需要點擊進入番劇詳情頁面獲取。並不能在排行榜頁面直接獲取。

所以要再分別爬取排名中各個番劇詳情鏈接,以得到評分和介紹數據。

所以要在li標簽的遍歷中用獲取到的詳情頁面鏈接link作為目標,再進行爬取,用於獲取評分和番劇介紹數據

 1 soup_1= get(link) 2 #用獲取到的鏈接link作為目標,用於獲取評分和番劇介紹數據 

並且,如果某些詳情頁面沒有評分信息,為防止報錯,將沒有介紹信息的番劇,介紹欄寫上“暫無”,將沒有評分信息的番劇的評分以總體評分的平均分來記錄,方便后期數據分析。

 1 if re.findall(f_score,item2) == []:
 2     #如果找不到番劇評分
 3     list2.append(f_no)
 4     #記錄無評分信息的番劇的排名
 5     score = "暫無"
 6     scorelist2.append("暫無")
 7     #在評分列表2中占一個位
 8 else:
 9     score = re.findall(f_score,item2)[0]
10     scorelist1.append(float(score))
11     #記錄在評分列表1中,用於求平均分
12     scorelist2.append(score)
13     #記錄下評分列表2中,用於最終輸出

 

1 score = round(mean(scorelist1),1)
2 #求番劇評分的平均值,保留一位小數
3 for i in list2:
4     scorelist2[i-1] = str(score)
5     #將沒有評分信息的項替換為評分平均值

 

 

 

 

 

總的爬取部分見完整代碼,太長了就不單獨放進來,最終爬取結果:

 

對數據進行清洗和處理:

去掉查找的結果中的換行符和空格,分析結果,發現播放量和收藏數中有“億”“萬”等單位,而非純數字,所以編寫函數進行

 1 def num_zh(num):#將含"萬"和"億"的數據轉為純數字
 2     num = str(num)
 3     numYi = num.find('')
 4     numWan = num.find('')
 5     
 6     if numYi != -1 and numWan != -1:
 7         return int(float(num[:numYi])*1e8 + float(num[numYi+1:numWan])*1e4)
 8     
 9     elif numYi != -1 and numWan == -1:
10         return int(float(num[:numYi])*1e8)
11     
12     elif numYi == -1 and numWan != -1:
13         return int(float(num[numYi+1:numWan])*1e4)
14     
15     elif numYi == -1 and numWan == -1:
16         return float(num)

轉換后:

數據持久化:

將表格保存在本地,以便數據分析。為防止錯誤,用的是相對保存路徑。並且在保存時檢查文件是否已存在,防止多次運行時因為文件已存在而發生錯誤。

1 #判斷文件是否已存在,防止錯誤
2 my_file = "嗶站番劇綜合排行榜top50.xls" # 文件路徑
3 if os.path.exists(my_file):
4     # 如果文件已存在
5     print('文件已存在,正在覆蓋!')
6     os.remove(my_file)
7     # 刪除
 1 book = xlwt.Workbook(encoding="utf-8",style_compression=0)
 2     #創建workbook對象
 3 sheet = book.add_sheet('嗶站番劇綜合排行榜top50',cell_overwrite_ok=True)
 4     #創建工作表
 5 col = ("排名","鏈接","標題","播放量","收藏數","評分","介紹")
 6 for i in range(0,7):
 7     sheet.write(0,i,col[i])
 8     #列名
 9 for i in range(0,50):
10     data = datalist[i]
11     for j in range(0,7):
12         sheet.write(i+1,j,data[j])
13         #數據
14 savepath = ".\\嗶站番劇綜合排行榜top50.xls" 
15 book.save(savepath)
16 print('已保存表格!')

輸出結果:

 

 

並且輸出一個數據表db文件,進一步保存數據。為防止錯誤,用的是相對保存路徑。

在保存輸出數據表數據的過程中,需要對數據進行特別處理,比如在每個字符串類型的數據前后加上雙引號。

1 my_file = "top50.db" # 文件路徑
2 if os.path.exists(my_file):
3     # 如果文件已存在
4     print('文件已存在,正在覆蓋!')
5     os.remove(my_file)
6     # 刪除
 1 sql = '''
 2     create table top50 
 3     (
 4     id integer primary key autoincrement,
 5     no numeric,
 6     link text,
 7     title varchar,
 8     watch numeric,
 9     like numeric,
10     score numeric,
11     info varchar
12         )
13 '''  # 創建數據表,以上分別是"排名"類型為數字,"鏈接"類型為文本,"標題"類型為字符串,"播放量"類型為數字,"收藏數"類型為數字,"評分"類型為數字,"介紹"類型為文本
14 dbpath = "top50.db"
15 conn = sqlite3.connect(dbpath)
16 cursor = conn.cursor()
17 cursor.execute(sql)
18 conn.commit()
19 conn.close()
20 
21 conn = sqlite3.connect(dbpath)
22 cur = conn.cursor()
23 for data in datalist:
24     #將所有字符串類型的數據的前后都加上雙引號
25     for index in range(len(data)):
26 
27         #跳過不需要加雙引號的數據
28         if index == 0 or index == 3 or index == 4 or index == 5:
29             continue
30         data[index] = '"'+data[index]+'"'
31     sql = '''
32             insert into top50 (
33             no,link,title,watch,like,score,info) 
34             values(%s)'''%",".join(data)
35     print(sql)
36     cur.execute(sql)
37     conn.commit()
38 
39 cur.close()
40 conn.close()
41 print('已保存數據表!')

輸出結果:

進一步數據清洗:

 1 #讀取表格信息 2 df=pd.read_excel(r'嗶站番劇綜合排行榜top50.xls') 3 R=pd.DataFrame(df) 

 1 #檢查是否有重復值
 2 print(R.duplicated())
 3 #檢查是否有空值
 4 print(R['排名'].isnull().value_counts())
 5 print(R['鏈接'].isnull().value_counts())
 6 print(R['標題'].isnull().value_counts())
 7 print(R['播放量'].isnull().value_counts())
 8 print(R['收藏數'].isnull().value_counts())
 9 print(R['評分'].isnull().value_counts())
10 print(R['介紹'].isnull().value_counts())

輸出結果:

 

說明數據沒有相關問題。

數據分析與可視化:

 1 ############排名和各類數量關系柱狀圖############
 2 plt.xticks(fontsize=8)
 3 plt.yticks(fontsize=12)
 4 plt.rcParams['font.sans-serif']=['SimHei']
 5 s = pd.Series(df.播放量,df.排名)
 6 s.plot(kind="bar",title="排名和播放量")
 7 
 8 #x標簽
 9 plt.xlabel("排名")
10 #y標簽
11 plt.ylabel("播放量")
12 plt.show()
13 
14 s = pd.Series(df.收藏數,df.排名)
15 s.plot(kind="bar",title="排名和收藏數")
16 plt.xticks(fontsize=8)
17 plt.yticks(fontsize=12)
18 #x標簽
19 plt.xlabel("排名")
20 #y標簽
21 plt.ylabel("收藏數")
22 plt.show()
23 
24 s = pd.Series(df.評分,df.排名)
25 s.plot(kind="bar",title="排名和評分")
26 plt.xticks(fontsize=8)
27 plt.yticks(fontsize=12)
28 #x標簽
29 plt.xlabel("排名")
30 #y標簽
31 plt.ylabel("評分")
32 plt.show()

 

從排名和播放量和排名和收藏數兩個表可以看推測,排名前幾的番劇都是新出的番劇,所以即使熱度很高,播放量和收藏數不會有舊的優秀番劇那么高。

相信隨時間的過去,這些排名前幾的新番劇都積累比現在高很多的播放量和收藏數,同時被新出的番劇擠下前幾名,而位於中上位置。

根據數據之間的關系,分析兩個變量之間的相關系數,畫出散點圖,並建立變

量之間的回歸方程:

 1 ############播放量和收藏數散點圖############
 2 plt.rcParams['font.sans-serif']=['Arial Unicode MS'] 
 3 plt.rcParams['axes.unicode_minus']=False 
 4 plt.xticks(fontsize=12)  
 5 plt.yticks(fontsize=12)
 6 #散點
 7 plt.scatter(df_1.播放量,df.收藏數, color='b')
 8 plt.rcParams['font.sans-serif']=['SimHei']
 9 #x標簽
10 plt.xlabel('播放量')
11 #y標簽
12 plt.ylabel('收藏數')
13 plt.show()

 1 ############排名和收藏數散點圖############
 2 plt.rcParams['font.sans-serif']=['Arial Unicode MS'] 
 3 plt.rcParams['axes.unicode_minus']=False 
 4 plt.xticks(fontsize=12)  
 5 plt.yticks(fontsize=12)
 6 #散點
 7 plt.scatter(df.排名,df.收藏數, color='r')
 8 plt.rcParams['font.sans-serif']=['SimHei']
 9 #x標簽
10 plt.xlabel('排名')
11 #y標簽
12 plt.ylabel('收藏數')
13 plt.show()

 1 ############播放量和收藏數線性回歸############
 2 predict_model=LinearRegression()
 3 x=df_1[["播放量"]]
 4 y=df["收藏數"]
 5 predict_model.fit(x,y)
 6 print("回歸方程系數為{}".format( predict_model.coef_)) 
 7 print("回歸方程截距:{0:2f}".format( predict_model.intercept_))
 8 
 9 x0=np.array(df_1['播放量'])
10 y0=np.array(df['收藏數'])
11 def func(x,c0):
12     a,b,c=c0
13     return a*x**2+b*x+c
14 def errfc(c0,x,y):
15     return y-func(x,c0)
16 c0=[0,2,3]
17 c1=opt.leastsq(errfc,c0,args=(x0,y0))[0]
18 a,b,c=c1
19 print(f"擬合方程為:y={a}*x**2+{b}*x+{c}")
20 chinese=matplotlib.font_manager.FontProperties(fname='C:\Windows\Fonts\simsun.ttc')
21 plt.plot(x0,y0,"ob",label="樣本數據")
22 plt.plot(x0,func(x0,c1),"r",label="擬合曲線")
23 #x標簽
24 plt.xlabel("播放量")
25 #y標簽
26 plt.ylabel("收藏數")
27 plt.legend(loc=3,prop=chinese)
28 plt.show() 

 1 ############排名和收藏數線性回歸############
 2 predict_model=LinearRegression()
 3 x=df[["排名"]]
 4 y=df["收藏數"]
 5 predict_model.fit(x,y)
 6 print("回歸方程系數為{}".format( predict_model.coef_)) 
 7 print("回歸方程截距:{0:2f}".format( predict_model.intercept_))
 8 
 9 x0=np.array(df['排名'])
10 y0=np.array(df['收藏數'])
11 def func(x,c0):
12     a,b,c=c0
13     return a*x**2+b*x+c
14 def errfc(c0,x,y):
15     return y-func(x,c0)
16 c0=[0,2,3]
17 c1=opt.leastsq(errfc,c0,args=(x0,y0))[0]
18 a,b,c=c1
19 print(f"擬合方程為:y={a}*x**2+{b}*x+{c}")
20 chinese=matplotlib.font_manager.FontProperties(fname='C:\Windows\Fonts\simsun.ttc')
21 plt.plot(x0,y0,"or",label="樣本數據")
22 plt.plot(x0,func(x0,c1),"b",label="擬合曲線")
23 #x標簽
24 plt.xlabel("排名")
25 #y標簽
26 plt.ylabel("收藏數")
27 plt.legend(loc=3,prop=chinese)
28 plt.show() 

 1 ############評分密度直方圖############
 2 plt.figure(figsize=(6,6))
 3 plt.suptitle('評分密度直方圖',fontsize=20)
 4 plt.xticks(fontsize=20)
 5 plt.yticks(fontsize=20)
 6 plt.xlabel("評分密度",fontsize=20)
 7 plt.ylabel(" ",fontsize=20)
 8 sns.distplot(df["評分"])
 9 plt.grid()
10 plt.show()

 1 ############評分分布餅圖############
 2 matplotlib.rcParams['font.sans-serif'] = ['SimHei']
 3 matplotlib.rcParams['axes.unicode_minus'] = False
 4 # 設置字體
 5 pf = [0, 0, 0, 0, 0, 0, 0] #用來記錄特定條件出現的次數
 6 #開始逐個分析評分
 7 for i in range(len(df.評分)):
 8     if df.評分[i] > 9.5:
 9         pf[0] = pf[0] + 1
10     if df.評分[i] > 9.0 and df.評分[i] <= 9.5:
11         pf[1] = pf[1] + 1
12     if df.評分[i] > 8.5 and df.評分[i] <= 9.0:
13         pf[2] = pf[2] + 1
14     if df.評分[i] > 8.0 and df.評分[i] <= 8.5:
15         pf[3] = pf[3] + 1
16     if df.評分[i] > 7.5 and df.評分[i] <= 8.0:
17         pf[4] = pf[4] + 1
18     if df.評分[i] > 7.0 and df.評分[i] <= 7.5:
19         pf[5] = pf[5] + 1
20     if df.評分[i] <= 7.0:
21         pf[6] = pf[6] + 1
22 #print(pf)
23 label = '>9.5', '9.0-9.5', '8.5-9.0', '8.0-8.5', '7.5-8.0', '7.0-7.5', '<7.0'#標題
24 color = 'red', 'orange', 'yellow', 'pink', 'green', 'blue'  #顏色
25 cs = [0, 0, 0, 0, 0, 0, 0]
26 #用來顯示百分比占比
27 explode = [0, 0, 0, 0, 0, 0, 0]
28 #每塊離中心的距離;
29 for i in range(0,7):  # 計算
30     #print(i)
31     cs[i] = pf[i] * 2
32     explode[i] = pf[i] / 500
33 #print(cs)
34 #開始配置圖形
35 pie = plt.pie(cs, colors=color, explode=explode, labels=label, shadow=True, autopct='%1.1f%%')
36 for font in pie[1]:
37     font.set_size(12)
38 for digit in pie[2]:
39     digit.set_size(8)
40 plt.axis('equal')
41 #配置標題,字號大小等:
42 plt.title(u'各個評分占比', fontsize=18)
43 plt.legend(loc=0, bbox_to_anchor=(0.8, 1))
44 leg = plt.gca().get_legend()
45 ltext = leg.get_texts()
46 plt.setp(ltext, fontsize=8)
47 #顯示視圖
48 plt.show()

經過觀察以上輸出視圖,我發現其中只有排名和收藏數有明顯的聯系,排名越前收藏數越高。排行榜前50名的評分都集中在比較高的位置。

 完整代碼:

  1 import os
  2 #包含判斷文件存在性功能的的庫
  3 from bs4 import BeautifulSoup
  4 #導入網頁解析相關的庫
  5 import re
  6 #導入進行文字匹配相關的庫
  7 import time
  8 #導入延時操作相關的庫
  9 import urllib.request,urllib.error
 10 #導入制定URL獲取網頁數據相關的庫
 11 import xlwt
 12 #導入excel操作相關的庫
 13 import sqlite3
 14 #導入SQLite數據庫操作相關的庫
 15 from numpy import *
 16 #用於求列表的平均值
 17 import xlrd
 18 import pandas as pd
 19 import pandas as np
 20 import matplotlib.pyplot as plt
 21 import matplotlib
 22 import scipy.optimize as opt
 23 import seaborn as sns
 24 from sklearn.linear_model import LinearRegression
 25 #導入數據清洗和分析相關的庫
 26 
 27 
 28 ############爬取信息############
 29 def get(url):#獲取網頁信息函數
 30     head = {
 31          "User-Agent": "Mozilla / 5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36(KHTML, like Gecko) Chrome / 96.0.4664.110 Safari / 537.36"
 32     }
 33     #模擬瀏覽器頭部信息
 34     request = urllib.request.Request(url, headers=head)
 35     #攜帶着這個包含着我的設備型號的頭部信息去訪問這個網址
 36     html = ""
 37     #用字符串對它進行存儲
 38     time.sleep(0.1)
 39     #添加延時,防止錯誤
 40 
 41     try:
 42         #處理可能會發生的錯誤,比如說網絡問題等,並輸出錯誤代碼,如404
 43         response = urllib.request.urlopen(request)
 44         html = response.read().decode("utf-8")
 45 
 46     except urllib.error.URLError as e:
 47 
 48         if hasattr(e, "code"):
 49             print(e.code)
 50 
 51         if hasattr(e, "reason"):
 52             print(e.reason)
 53 
 54     soup = BeautifulSoup(html,"html.parser")
 55     #解析數據
 56     return soup
 57 
 58 def num_zh(num):#將含"萬"和"億"的數據轉為純數字
 59     num = str(num)
 60     numYi = num.find('')
 61     numWan = num.find('')
 62 
 63     if numYi != -1 and numWan != -1:
 64         return int(float(num[:numYi])*1e8 + float(num[numYi+1:numWan])*1e4)
 65 
 66     elif numYi != -1 and numWan == -1:
 67         return int(float(num[:numYi])*1e8)
 68 
 69     elif numYi == -1 and numWan != -1:
 70         return int(float(num[numYi+1:numWan])*1e4)
 71 
 72     elif numYi == -1 and numWan == -1:
 73         return float(num)
 74 
 75 baseurl = "https://www.bilibili.com/v/popular/rank/bangumi"
 76 soup = get(baseurl)
 77 # 測試輸出html信息,print(soup)
 78 datalist = []
 79 # 創建列表用來保持數據
 80 f_no = 0#排名編號
 81 list1 = []
 82 list2=[]
 83 scorelist1=[]
 84 scorelist2=[]
 85 #建立列表用於存放數據
 86 
 87 for item1 in soup.find_all('li',class_="rank-item"):
 88     #查找符合條件的li標簽,逐一分析即遍歷(在輸出的標簽li列表中,逐一進行查找標簽li中我們需要的信息,並保存於新的列表datalist中)
 89     item1 = str(item1)
 90     f_no = f_no+1
 91     # 編寫排名編號
 92     print(''+str(f_no)+'')
 93     f_link = re.compile(r'<a href="(.*?)" target')
 94     # 查找番劇鏈接
 95     f_title = re.compile(r'<a class="title".*target="_blank">(.*?)</a>')
 96     # 查找番劇標題
 97     f_watchtext = re.compile(r'<img alt="play".*/>(.*?)</span> <span class="data-box">',re.S)
 98     # 查找番劇播放量
 99     f_liketext = re.compile(r'<img alt="follow".*/>(.*?)</span>',re.S)
100     # 查找番劇收藏數
101 
102     print('排名:'+str(f_no))
103     list1.append(str(f_no))
104     link = 'https:'+re.findall(f_link,item1)[0]#補充鏈接頭部缺少的https:
105     print('番劇鏈接:'+link)
106     list1.append(link)
107     title = re.findall(f_title,item1)[0]
108     print('番劇標題:'+title)
109     list1.append(title)
110     watchtext = re.findall(f_watchtext,item1)[0].replace("\n","").strip()#去除內容中的空格與換行符
111     watch = str(num_zh(watchtext))
112     print('播放量:'+watch)
113     list1.append(watch)
114     liketext = re.findall(f_liketext,item1)[0].replace("\n","").strip()#去除內容中的空格與換行符
115     like = str(num_zh(liketext))
116     print('收藏數:'+like)
117     list1.append(like)
118 
119     soup_1= get(link)
120     #用獲取到的鏈接link作為目標,爬取番劇的詳情頁面,用於獲取評分和番劇介紹數據
121     #測試輸出 print(soup_1)
122 
123     item2 = soup_1.find_all('div',class_="media-right")
124     item2 = str(item2)
125 
126     f_info = re.compile(r'<span class="absolute">(.*?)</span>',re.S)
127     # 查找番劇介紹
128     f_score = re.compile(r'<h4 class="score">(.*?)</h4>')
129     # 查找番劇評分
130     
131     if re.findall(f_info,item2) == []:
132         #如果找不到番劇介紹
133         info = '暫無'
134     else:
135         info = re.findall(f_info,item2)[0].replace("\n","")
136         
137     list1.append(info)
138 
139     if re.findall(f_score,item2) == []:
140         #如果找不到番劇評分
141         list2.append(f_no)
142         #記錄無評分信息的番劇的排名
143         score = "暫無"
144         scorelist2.append("暫無")
145         #在評分列表2中占一個位
146     else:
147         score = re.findall(f_score,item2)[0]
148         scorelist1.append(float(score))
149         #記錄在評分列表1中,用於求平均分
150         scorelist2.append(score)
151         #記錄下評分列表2中,用於最終輸出
152     print('評分:'+score)
153     print('介紹:'+info)
154 score = round(mean(scorelist1),1)
155 #求番劇評分的平均值,保留一位小數
156 for i in list2:
157     scorelist2[i-1] = str(score)
158     #將沒有評分信息的項替換為評分平均值
159     
160 for i in range(0,50):
161     #將記錄的數據,按一定格式記錄於最終的表datalist中
162     data=[]
163     data.append(list1[i*6])
164     data.append(list1[i*6+1])
165     data.append(list1[i*6+2])
166     data.append(list1[i*6+3])
167     data.append(list1[i*6+4])
168     data.append(scorelist2[i])
169     data.append(list1[i*6+5])
170     datalist.append(data)
171 #測試輸出 print(datalist)
172 
173 
174 ############將數據保存在表格中############
175 book = xlwt.Workbook(encoding="utf-8",style_compression=0)
176     #創建workbook對象
177 sheet = book.add_sheet('嗶站番劇綜合排行榜top50',cell_overwrite_ok=True)
178     #創建工作表
179 col = ("排名","鏈接","標題","播放量","收藏數","評分","介紹")
180 for i in range(0,7):
181     sheet.write(0,i,col[i])
182     #列名
183 for i in range(0,50):
184     data = datalist[i]
185     for j in range(0,7):
186         sheet.write(i+1,j,data[j])
187         #數據
188 savepath = ".\\嗶站番劇綜合排行榜top50.xls"# 文件路徑
189 
190 #判斷文件是否已存在,防止錯誤
191 my_file = "嗶站番劇綜合排行榜top50.xls" # 文件路徑
192 if os.path.exists(my_file):
193     # 如果文件已存在
194     print('文件已存在,正在覆蓋!')
195     os.remove(my_file)
196     # 刪除
197 
198 book.save(savepath)
199 print('已保存表格!')
200 
201 
202 ############將數據保存在數據表中############
203 sql = '''
204     create table top50
205     (
206     id integer primary key autoincrement,
207     no numeric,
208     link text,
209     title varchar,
210     watch numeric,
211     like numeric,
212     score numeric,
213     info varchar
214         )
215 '''  # 創建數據表,以上分別是"排名"類型為數字,"鏈接"類型為文本,"標題"類型為字符串,"播放量"類型為數字,"收藏數"類型為數字,"評分"類型為數字,"介紹"類型為文本
216 dbpath = "top50.db"# 文件路徑
217 
218 #判斷文件是否已存在,防止錯誤
219 my_file = "top50.db" # 文件路徑
220 if os.path.exists(my_file):
221     # 如果文件已存在
222     print('文件已存在,正在覆蓋!')
223     os.remove(my_file)
224     # 刪除
225 
226 conn = sqlite3.connect(dbpath)
227 cursor = conn.cursor()
228 cursor.execute(sql)
229 conn.commit()
230 conn.close()
231 
232 conn = sqlite3.connect(dbpath)
233 cur = conn.cursor()
234 for data in datalist:
235     #將所有字符串類型的數據的前后都加上雙引號
236     for index in range(len(data)):
237 
238         #跳過不需要加雙引號的數據
239         if index == 0 or index == 3 or index == 4 or index == 5:
240             continue
241         data[index] = '"'+data[index]+'"'
242     sql = '''
243             insert into top50 (
244             no,link,title,watch,like,score,info)
245             values(%s)'''%",".join(data)
246     print(sql)
247     cur.execute(sql)
248     conn.commit()
249 
250 cur.close()
251 conn.close()
252 print('已保存數據表!')
253 print("爬取完畢")
254 
255 
256 ############數據清洗############
257 #讀取表格信息
258 df=pd.read_excel(r'嗶站番劇綜合排行榜top50.xls')
259 R=pd.DataFrame(df)
260 
261 #檢查是否有重復值
262 print(R.duplicated())
263 #檢查是否有空值
264 print(R['排名'].isnull().value_counts())
265 print(R['鏈接'].isnull().value_counts())
266 print(R['標題'].isnull().value_counts())
267 print(R['播放量'].isnull().value_counts())
268 print(R['收藏數'].isnull().value_counts())
269 print(R['評分'].isnull().value_counts())
270 print(R['介紹'].isnull().value_counts())
271 
272 
273 ############數據分析############
274 df_1=df.sort_values('播放量',ascending=True)
275 #根據播放量升序排序
276 
277 
278 ############排名和各類數量關系柱狀圖############
279 plt.xticks(fontsize=8)
280 plt.yticks(fontsize=12)
281 plt.rcParams['font.sans-serif']=['SimHei']
282 s = pd.Series(df.播放量,df.排名)
283 s.plot(kind="bar",title="排名和播放量")
284 #x標簽
285 plt.xlabel("排名")
286 #y標簽
287 plt.ylabel("播放量")
288 #顯示圖形
289 plt.show()
290 
291 s = pd.Series(df.收藏數,df.排名)
292 s.plot(kind="bar",title="排名和收藏數")
293 plt.xticks(fontsize=8)
294 plt.yticks(fontsize=12)
295 #x標簽
296 plt.xlabel("排名")
297 #y標簽
298 plt.ylabel("收藏數")
299 #顯示圖形
300 plt.show()
301 
302 s = pd.Series(df.評分,df.排名)
303 s.plot(kind="bar",title="排名和評分")
304 plt.xticks(fontsize=8)
305 plt.yticks(fontsize=12)
306 #x標簽
307 plt.xlabel("排名")
308 #y標簽
309 plt.ylabel("評分")
310 #顯示圖形
311 plt.show()
312 
313 
314 ############播放量和收藏數散點圖############
315 plt.rcParams['font.sans-serif']=['Arial Unicode MS']
316 plt.rcParams['axes.unicode_minus']=False
317 plt.xticks(fontsize=12)
318 plt.yticks(fontsize=12)
319 #散點
320 plt.scatter(df_1.播放量,df.收藏數, color='b')
321 plt.rcParams['font.sans-serif']=['SimHei']
322 #x標簽
323 plt.xlabel('播放量')
324 #y標簽
325 plt.ylabel('收藏數')
326 #顯示圖形
327 plt.show()
328 
329 
330 ############排名和收藏數散點圖############
331 plt.rcParams['font.sans-serif']=['Arial Unicode MS']
332 plt.rcParams['axes.unicode_minus']=False
333 plt.xticks(fontsize=12)
334 plt.yticks(fontsize=12)
335 #散點
336 plt.scatter(df.排名,df.收藏數, color='r')
337 plt.rcParams['font.sans-serif']=['SimHei']
338 #x標簽
339 plt.xlabel('排名')
340 #y標簽
341 plt.ylabel('收藏數')
342 #顯示圖形
343 plt.show()
344 
345 
346 ############播放量和收藏數線性回歸############
347 predict_model=LinearRegression()
348 x=df_1[["播放量"]]
349 y=df["收藏數"]
350 predict_model.fit(x,y)
351 print("回歸方程系數為{}".format( predict_model.coef_))
352 print("回歸方程截距:{0:2f}".format( predict_model.intercept_))
353 
354 x0=np.array(df_1['播放量'])
355 y0=np.array(df['收藏數'])
356 def func(x,c0):
357     a,b,c=c0
358     return a*x**2+b*x+c
359 def errfc(c0,x,y):
360     return y-func(x,c0)
361 c0=[0,2,3]
362 c1=opt.leastsq(errfc,c0,args=(x0,y0))[0]
363 a,b,c=c1
364 print(f"擬合方程為:y={a}*x**2+{b}*x+{c}")
365 chinese=matplotlib.font_manager.FontProperties(fname='C:\Windows\Fonts\simsun.ttc')
366 plt.plot(x0,y0,"ob",label="樣本數據")
367 plt.plot(x0,func(x0,c1),"r",label="擬合曲線")
368 #x標簽
369 plt.xlabel("播放量")
370 #y標簽
371 plt.ylabel("收藏數")
372 plt.legend(loc=3,prop=chinese)
373 #顯示圖形
374 plt.show()
375 
376 
377 ############排名和收藏數線性回歸############
378 predict_model=LinearRegression()
379 x=df[["排名"]]
380 y=df["收藏數"]
381 predict_model.fit(x,y)
382 print("回歸方程系數為{}".format( predict_model.coef_))
383 print("回歸方程截距:{0:2f}".format( predict_model.intercept_))
384 
385 x0=np.array(df['排名'])
386 y0=np.array(df['收藏數'])
387 def func(x,c0):
388     a,b,c=c0
389     return a*x**2+b*x+c
390 def errfc(c0,x,y):
391     return y-func(x,c0)
392 c0=[0,2,3]
393 c1=opt.leastsq(errfc,c0,args=(x0,y0))[0]
394 a,b,c=c1
395 print(f"擬合方程為:y={a}*x**2+{b}*x+{c}")
396 chinese=matplotlib.font_manager.FontProperties(fname='C:\Windows\Fonts\simsun.ttc')
397 plt.plot(x0,y0,"or",label="樣本數據")
398 plt.plot(x0,func(x0,c1),"b",label="擬合曲線")
399 #x標簽
400 plt.xlabel("排名")
401 #y標簽
402 plt.ylabel("收藏數")
403 plt.legend(loc=3,prop=chinese)
404 #顯示圖形
405 plt.show()
406 
407 
408 ############評分密度直方圖############
409 plt.figure(figsize=(6,6))
410 plt.suptitle('評分密度直方圖',fontsize=20)
411 plt.xticks(fontsize=20)
412 plt.yticks(fontsize=20)
413 plt.xlabel("評分密度",fontsize=20)
414 plt.ylabel(" ",fontsize=20)
415 sns.distplot(df["評分"])
416 plt.grid()
417 #顯示圖形
418 plt.show()
419 
420 
421 ############評分分布餅圖############
422 matplotlib.rcParams['font.sans-serif'] = ['SimHei']
423 matplotlib.rcParams['axes.unicode_minus'] = False
424 # 設置字體
425 pf = [0, 0, 0, 0, 0, 0, 0] #用來記錄特定條件出現的次數
426 #開始逐個分析評分
427 for i in range(len(df.評分)):
428     if df.評分[i] > 9.5:
429         pf[0] = pf[0] + 1
430     if df.評分[i] > 9.0 and df.評分[i] <= 9.5:
431         pf[1] = pf[1] + 1
432     if df.評分[i] > 8.5 and df.評分[i] <= 9.0:
433         pf[2] = pf[2] + 1
434     if df.評分[i] > 8.0 and df.評分[i] <= 8.5:
435         pf[3] = pf[3] + 1
436     if df.評分[i] > 7.5 and df.評分[i] <= 8.0:
437         pf[4] = pf[4] + 1
438     if df.評分[i] > 7.0 and df.評分[i] <= 7.5:
439         pf[5] = pf[5] + 1
440     if df.評分[i] <= 7.0:
441         pf[6] = pf[6] + 1
442 #print(pf)
443 label = '>9.5', '9.0-9.5', '8.5-9.0', '8.0-8.5', '7.5-8.0', '7.0-7.5', '<7.0'#標題
444 color = 'red', 'orange', 'yellow', 'pink', 'green', 'blue'  #顏色
445 cs = [0, 0, 0, 0, 0, 0, 0]
446 #用來顯示百分比占比
447 explode = [0, 0, 0, 0, 0, 0, 0]
448 #每塊離中心的距離;
449 for i in range(0,7):  # 計算
450     #print(i)
451     cs[i] = pf[i] * 2
452     explode[i] = pf[i] / 500
453 #print(cs)
454 #開始配置圖形
455 pie = plt.pie(cs, colors=color, explode=explode, labels=label, shadow=True, autopct='%1.1f%%')
456 for font in pie[1]:
457     font.set_size(12)
458 for digit in pie[2]:
459     digit.set_size(8)
460 plt.axis('equal')
461 #配置標題,字號大小等:
462 plt.title(u'各個評分占比', fontsize=18)
463 plt.legend(loc=0, bbox_to_anchor=(0.8, 1))
464 leg = plt.gca().get_legend()
465 ltext = leg.get_texts()
466 plt.setp(ltext, fontsize=8)
467 #顯示視圖
468 plt.show()

 

 總結:

以上爬取了我感興趣的網站信息,讓我更加了解自己喜歡的東西,也驗證了我的猜想,果然新興的熱門番劇,往往擁有非常高的熱度卻沒有非常高的播放量,它們的播放量還需要時間積累,而積累的時間內會有新的熱門番劇發行,從而導致排名前幾的熱度高卻播放量一般,前幾偏后幾名的擁有非常高的播放量,卻熱度不再霸榜,再后面就是相對平平無奇的了。

在做這項爬取任務時,我體會到python的魅力所在,體會到編程的樂趣。

在編寫時常常因為數據類型錯誤,網絡響應沒有代碼運行的快,查找不到相關信息引發報錯,文件已存在等等一系列問題導致運行失敗,而我仔細尋找原因,一個個去修正。

如果數據類型錯誤,則一一對正。如果網絡響應問題報錯,則加入延時。通過查找不到相關信息引發報錯,則相應對策,如輸出“暫無”等等。這使我從中學到很多經驗。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM