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的魅力所在,体会到编程的乐趣。

在编写时常常因为数据类型错误,网络响应没有代码运行的快,查找不到相关信息引发报错,文件已存在等等一系列问题导致运行失败,而我仔细寻找原因,一个个去修正。

如果数据类型错误,则一一对正。如果网络响应问题报错,则加入延时。通过查找不到相关信息引发报错,则相应对策,如输出“暂无”等等。这使我从中学到很多经验。

posted @ 2021-12-28 01:36  fxy30130  阅读(1124)  评论(0编辑  收藏  举报