《基于requests的B站视频排行榜的爬取及分析》
一、选题背景
爬取B站每日排行榜中科技区和美食区视频的信息,探究播放量和弹幕数之间的关系
二、主题式网络爬虫设计方案
1.主题式网络爬虫名称
《基于requests的B站视频排行榜的爬取及分析》
2.主题式网络爬虫爬取的内容与数据特征分析
爬取视频播放量,弹幕数,寻找数据之间的关联
3.主题式网络爬虫方案设计概述
思路:本次设计方案主要使用request库爬取网页信息和beautifulsoup来提取网站视频的信息。
难点:对B站视频信息进行数据采集以及数据可视化
三、主题页面的结构特征分析
1.主题页面的结构与特征分析
页面链接如下:哔哩哔哩排行榜

排行榜页面中的视频内容以列表的形式展示,每个视频中包含视频封面,视频标题,up主(制作者)名字,播放量和弹幕量等五个元素。
2. Htmls页面解析、

所需的数据存储结构为
<div class=info>
<a>title</a>
<div class=detail>
<span>upName</span>
<span>play</span>
<span>like</span>
</div>
</div>
3. 节点(标签)查找方法与遍历方法
使用beautiful soup的find_all()遍历tag的所有子tag
四、网络爬虫程序设计
导入库:
1 import requests 2 import pandas as pd 3 import re 4 import xlwt 5 import jieba 6 import matplotlib.pyplot as plt 7 import numpy as np 8 from wordcloud import WordCloud 9 from bs4 import BeautifulSoup 10 from sklearn.model_selection import train_test_split 11 from sklearn.linear_model import LinearRegression
1.数据爬取与采集
1 def get_html(url): # 获取网页源代码 2 headers = { 3 'User-Agent':'Mozilla/5.0(Windows NT 6.1) AppleWebKit/537.36(KHTML,like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE' 4 } 5 6 try: 7 r = requests.get(url,headers=headers) # 使用get来获取网页数据 8 r.raise_for_status() # 如果返回参数不为200,抛出异常 9 r.encoding = 'utf-8' # 获取网页编码方式 10 return r.text 11 12 except: 13 return 'wrong'
2. 对数据进行清洗和处理
1 def parse(html,ls): # 数据清洗处理 2 3 soup = BeautifulSoup(html,'html.parser') 4 # print(soup.prettify()) 5 6 # 找到存储视频的列表 7 ul1 = soup.find_all('ul')[1] 8 9 # 根据视频结构清洗数据,并保留前五十个视频的数据 10 for i in range(50): 11 a = [] 12 dic = {} # 将单个视频的数据以字典形式储存 13 # print(ul1.find_all('li')[i]) 14 15 dic['标题'] = ul1.find_all('li')[i].find('a',class_='title').string # 存储视频标题 16 # print(i) 17 # print(dic['标题']) 18 for string in ul1.find_all('li')[i].find('div',class_='detail').stripped_strings: 19 a.append(string) 20 # print(a) 21 dic['upName'] = a[0] # 存储up主姓名 22 23 # 存储视频播放量,以万为单位 24 b = re.search(r'\d*(.)?\d',a[1]).group() 25 dic['播放量(万)'] = float(b) 26 27 # 存储视频弹幕数,以个为单位 28 if '万' not in a[2]: 29 b = float(a[2]) 30 else: 31 b = float(re.search(r'\d*(.)?\d', a[2]).group())*10000 32 dic['弹幕数'] = b 33 34 # print(dic) 35 ls.append(dic) # 将所有视频的数据以列表形式储存
3.文本分析
1 def ciyun(ls1,ls2): 2 3 # 制作科技区词云图 4 str1 = '' 5 for i in range(50): 6 str1 += ls1[i]['标题'] 7 # print(str1) 8 word1 = jieba.cut(str1) 9 word1 = ' '.join(word1) 10 str1_wordcloud = WordCloud(background_color='black', 11 font_path='msyh.ttc').generate_from_text(word1) 12 plt.imshow(str1_wordcloud) 13 plt.axis('off') 14 plt.show() 15 plt.savefig(r'images/科技区词云图.jpg') 16 17 # 制作美食区词云图 18 str2 = '' 19 for i in range(50): 20 str2 += ls2[i]['标题'] 21 # print(str2) 22 word2 = jieba.cut(str2) 23 word2 = ' '.join(word2) 24 str2_wordcloud = WordCloud(background_color='black', 25 font_path='msyh.ttc').generate_from_text(word2) 26 plt.imshow(str2_wordcloud) 27 plt.axis('off') 28 plt.show() 29 plt.savefig(r'images/美食区词云图.jpg') 30 31 return
4. 数据分析与可视化
1 def visualize(ls1,ls2): # 数据可视化 2 # 合并列表 3 ls = ls1 + ls2 4 # print(ls) 5 # 创建dataframe 6 df = pd.DataFrame(ls) # 合并后的dataframe 7 label = [] # 给df的视频贴标签 8 for i in range(100): 9 if i<50: 10 label.append('科技区') 11 else: 12 label.append('美食区') 13 df['label'] = pd.Series(label) 14 df['排名'] = pd.Series(range(1,101)) 15 df1 = pd.DataFrame(ls1) # 科技区的dataframe 16 df1['排名'] = pd.Series(range(1,51)) 17 df2 = pd.DataFrame(ls2) # 美食区的dataframe 18 df2['排名'] = pd.Series(range(1,51)) 19 # print(df) 20 # print(df) 21 22 # 显示中文 23 plt.rcParams['font.sans-serif'] = ['SimHei'] 24 plt.rcParams['axes.unicode_minus'] = False 25 26 # 科技区和美食区播放量和弹幕数对比的箱线图 27 colorDic = dict(boxes='Green',whiskers='Orange',medians='Blue',caps='Gray') 28 bp1=df.loc[:,['播放量(万)','label']].boxplot(by='label',color=colorDic) 29 plt.title('科技区和美食区播放量箱线图') 30 plt.savefig(r'images/科技区和美食区播放量箱线图.jpg') 31 bp2=df.loc[:,['弹幕数','label']].boxplot(by='label',color=colorDic) 32 plt.title('科技区和美食区弹幕数箱线图') 33 plt.savefig(r'images/科技区和美食区弹幕数箱线图.jpg') 34 35 # 科技区和美食区播放量和弹幕数对比条形图 36 df1 = df1.loc[:,['排名','播放量(万)','弹幕数']] 37 df1.rename(columns={'播放量(万)':'播放量(科技区)','弹幕数':'弹幕数(科技区)'},inplace=True) 38 df2 = df2.loc[:,['排名','播放量(万)','弹幕数']] 39 df2.rename(columns={'播放量(万)':'播放量(美食区)','弹幕数':'弹幕数(美食区)'},inplace=True) 40 # print(df1) 41 df3 = pd.concat([df1,df2],axis=1).iloc[:,[0,1,2,4,5]] # 去掉重复的排名列 42 df01 = df3.iloc[:,[0,1,3]] 43 df01.plot(kind='bar',x='排名') 44 plt.title('同排名下科技区和美食区播放量对比图') 45 plt.xlabel('排名') 46 plt.ylabel('播放量(万)') 47 plt.xticks(rotation=0) 48 plt.savefig(r'images/同排名下科技区和美食区播放量对比图.jpg') 49 df02 = df3.iloc[:,[0,2,4]] 50 df02.plot(kind='bar',x='排名') 51 plt.title('同排名下科技区和美食区弹幕数对比图') 52 plt.xlabel('排名') 53 plt.ylabel('弹幕数') 54 plt.xticks(rotation=0) 55 plt.savefig(r'images/同排名下科技区和美食区弹幕数对比图.jpg') 56 57 # 散点图 58 # print(df1) 59 df1.plot(kind='scatter',x='播放量(科技区)',y='弹幕数(科技区)') 60 plt.title('科技区播放量和弹幕数散点图') 61 plt.xlabel('播放量(万)') 62 plt.ylabel('弹幕数') 63 plt.savefig(r'images/科技区播放量和弹幕数散点图.jpg') 64 df2.plot(kind='scatter',x='播放量(美食区)',y='弹幕数(美食区)') 65 plt.title('美食区播放量和弹幕数散点图') 66 plt.xlabel('播放量(万)') 67 plt.ylabel('弹幕数') 68 plt.savefig(r'images/美食区播放量和弹幕数散点图.jpg') 69 70 plt.show()
输出图片:






5.根据数据关系,分析变量之间的相关系数,并建立变量回归方程
1 def regression(ls1,ls2): # 回归分析 2 3 # 生成科技区的dataframe 4 df1 = pd.DataFrame(ls1) 5 # print(df1) 6 # 生成美食区的dataframe 7 df2 = pd.DataFrame(ls2) 8 # print(df2) 9 10 # 计算科技区相关系数 11 a = df1.loc[:,'播放量(万)'] 12 b = df1.loc[:,'弹幕数'] 13 # print(type(a)) 14 # print(type(b)) 15 corrTech = round(a.corr(b),4) 16 print("科技区视频播放量(万)和弹幕数的相关系数为:",corrTech) 17 18 # 计算美食区相关系数 19 c = df2.loc[:,'播放量(万)'] 20 d = df2.loc[:,'弹幕数'] 21 # print(type(c)) 22 # print(type(d)) 23 corrFood = round(c.corr(d),4) 24 print("美食区视频播放量(万)和弹幕数的相关系数为:",corrFood) 25 print('') 26 27 # 对科技区的播放量(万)和弹幕数进行线性回归 28 # 将数据拆分为训练集和测试集 29 X1_train,X1_test,Y1_train,Y1_test = train_test_split(a,b,train_size=0.8) 30 # X1_train为训练数据标签,X1_test为测试数据标签.a为样本特征,b为样本标签 31 print('科技区:') 32 print("原始数据特征:",a.shape[0]) 33 print("训练数据特征:",Y1_train.shape[0]) 34 print("测试数据特征:",X1_test.shape[0]) 35 print("原始数据标签:",b.shape[0]) 36 print("训练数据标签:",Y1_train.shape[0]) 37 print("测试数据标签:",Y1_test.shape[0]) 38 39 model = LinearRegression() 40 X1_train = X1_train.values.reshape(-1,1) 41 X1_test = X1_test.values.reshape(-1,1) 42 model.fit(X1_train,Y1_train) 43 interceptTech = model.intercept_ # 截距 44 coefTech = model.coef_ # 回归系数 45 print('最佳拟合线:截距',interceptTech,",回归系数:",coefTech) 46 print('') 47 48 # 同理,对美食区的播放量(万)和弹幕数进行线性回归 49 X2_train,X2_test,Y2_train,Y2_test = train_test_split(c,d,train_size=0.8) 50 # X1_train为训练数据标签,X1_test为测试数据标签.a为样本特征,b为样本标签 51 print('美食区') 52 print("原始数据特征:",c.shape[0]) 53 print("训练数据特征:",Y2_train.shape[0]) 54 print("测试数据特征:",X2_test.shape[0]) 55 print("原始数据标签:",d.shape[0]) 56 print("训练数据标签:",Y2_train.shape[0]) 57 print("测试数据标签:",Y2_test.shape[0]) 58 59 model = LinearRegression() 60 X2_train = X2_train.values.reshape(-1,1) 61 X2_test = X2_test.values.reshape(-1,1) 62 model.fit(X2_train,Y2_train) 63 interceptFood = model.intercept_ # 截距 64 coefFood = model.coef_ # 回归系数 65 print('最佳拟合线:截距',interceptFood,",回归系数:",coefFood) 66 67 return
6. 数据持久化
1 def write(ls1,ls2): # 数据持久化 2 3 # 合并列表 4 ls = ls1 + ls2 5 6 # 创建dataframe 7 df = pd.DataFrame(ls) 8 9 # 给视频贴标签 10 label = [] 11 for i in range(100): 12 if i<50: 13 label.append('科技区') 14 else: 15 label.append('美食区') 16 df['label'] = pd.Series(label) 17 18 # 记录视频的当天排名 19 rank = list(range(1,51)) 20 rank = rank+rank 21 df['排名'] = pd.Series(rank) 22 23 df.to_excel('data.xls') 24 25 return
7.汇总 附上完整代码
1 import requests 2 import pandas as pd 3 import re 4 import xlwt 5 import jieba 6 import matplotlib.pyplot as plt 7 import numpy as np 8 from wordcloud import WordCloud 9 from bs4 import BeautifulSoup 10 from sklearn.model_selection import train_test_split 11 from sklearn.linear_model import LinearRegression 12 13 def get_html(url): # 获取网页源代码 14 headers = { 15 'User-Agent':'Mozilla/5.0(Windows NT 6.1) AppleWebKit/537.36(KHTML,like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE' 16 } 17 18 try: 19 r = requests.get(url,headers=headers) # 使用get来获取网页数据 20 r.raise_for_status() # 如果返回参数不为200,抛出异常 21 r.encoding = 'utf-8' # 获取网页编码方式 22 return r.text 23 24 except: 25 return 'wrong' 26 27 def parse(html,ls): # 数据清洗处理 28 29 soup = BeautifulSoup(html,'html.parser') 30 # print(soup.prettify()) 31 32 # 找到存储视频的列表 33 ul1 = soup.find_all('ul')[1] 34 35 # 根据视频结构清洗数据,并保留前五十个视频的数据 36 for i in range(50): 37 a = [] 38 dic = {} # 将单个视频的数据以字典形式储存 39 # print(ul1.find_all('li')[i]) 40 41 dic['标题'] = ul1.find_all('li')[i].find('a',class_='title').string # 存储视频标题 42 # print(i) 43 # print(dic['标题']) 44 for string in ul1.find_all('li')[i].find('div',class_='detail').stripped_strings: 45 a.append(string) 46 # print(a) 47 dic['upName'] = a[0] # 存储up主姓名 48 49 # 存储视频播放量,以万为单位 50 b = re.search(r'\d*(.)?\d',a[1]).group() 51 dic['播放量(万)'] = float(b) 52 53 # 存储视频弹幕数,以个为单位 54 if '万' not in a[2]: 55 b = float(a[2]) 56 else: 57 b = float(re.search(r'\d*(.)?\d', a[2]).group())*10000 58 dic['弹幕数'] = b 59 60 # print(dic) 61 ls.append(dic) # 将所有视频的数据以列表形式储存 62 63 def ciyun(ls1,ls2): 64 65 # 制作科技区词云图 66 str1 = '' 67 for i in range(50): 68 str1 += ls1[i]['标题'] 69 # print(str1) 70 word1 = jieba.cut(str1) 71 word1 = ' '.join(word1) 72 str1_wordcloud = WordCloud(background_color='black', 73 font_path='msyh.ttc').generate_from_text(word1) 74 plt.imshow(str1_wordcloud) 75 plt.axis('off') 76 plt.show() 77 plt.savefig(r'images/科技区词云图.jpg') 78 79 # 制作美食区词云图 80 str2 = '' 81 for i in range(50): 82 str2 += ls2[i]['标题'] 83 # print(str2) 84 word2 = jieba.cut(str2) 85 word2 = ' '.join(word2) 86 str2_wordcloud = WordCloud(background_color='black', 87 font_path='msyh.ttc').generate_from_text(word2) 88 plt.imshow(str2_wordcloud) 89 plt.axis('off') 90 plt.show() 91 plt.savefig(r'images/美食区词云图.jpg') 92 93 return 94 95 def write(ls1,ls2): # 数据持久化 96 97 # 合并列表 98 ls = ls1 + ls2 99 100 # 创建dataframe 101 df = pd.DataFrame(ls) 102 103 # 给视频贴标签 104 label = [] 105 for i in range(100): 106 if i<50: 107 label.append('科技区') 108 else: 109 label.append('美食区') 110 df['label'] = pd.Series(label) 111 112 # 记录视频的当天排名 113 rank = list(range(1,51)) 114 rank = rank+rank 115 df['排名'] = pd.Series(rank) 116 117 df.to_excel('data.xls') 118 119 return 120 121 def visualize(ls1,ls2): # 数据可视化 122 # 合并列表 123 ls = ls1 + ls2 124 # print(ls) 125 # 创建dataframe 126 df = pd.DataFrame(ls) # 合并后的dataframe 127 label = [] # 给df的视频贴标签 128 for i in range(100): 129 if i<50: 130 label.append('科技区') 131 else: 132 label.append('美食区') 133 df['label'] = pd.Series(label) 134 df['排名'] = pd.Series(range(1,101)) 135 df1 = pd.DataFrame(ls1) # 科技区的dataframe 136 df1['排名'] = pd.Series(range(1,51)) 137 df2 = pd.DataFrame(ls2) # 美食区的dataframe 138 df2['排名'] = pd.Series(range(1,51)) 139 # print(df) 140 # print(df) 141 142 # 显示中文 143 plt.rcParams['font.sans-serif'] = ['SimHei'] 144 plt.rcParams['axes.unicode_minus'] = False 145 146 # 科技区和美食区播放量和弹幕数对比的箱线图 147 colorDic = dict(boxes='Green',whiskers='Orange',medians='Blue',caps='Gray') 148 bp1=df.loc[:,['播放量(万)','label']].boxplot(by='label',color=colorDic) 149 plt.title('科技区和美食区播放量箱线图') 150 plt.savefig(r'images/科技区和美食区播放量箱线图.jpg') 151 bp2=df.loc[:,['弹幕数','label']].boxplot(by='label',color=colorDic) 152 plt.title('科技区和美食区弹幕数箱线图') 153 plt.savefig(r'images/科技区和美食区弹幕数箱线图.jpg') 154 155 # 科技区和美食区播放量和弹幕数对比条形图 156 df1 = df1.loc[:,['排名','播放量(万)','弹幕数']] 157 df1.rename(columns={'播放量(万)':'播放量(科技区)','弹幕数':'弹幕数(科技区)'},inplace=True) 158 df2 = df2.loc[:,['排名','播放量(万)','弹幕数']] 159 df2.rename(columns={'播放量(万)':'播放量(美食区)','弹幕数':'弹幕数(美食区)'},inplace=True) 160 # print(df1) 161 df3 = pd.concat([df1,df2],axis=1).iloc[:,[0,1,2,4,5]] # 去掉重复的排名列 162 df01 = df3.iloc[:,[0,1,3]] 163 df01.plot(kind='bar',x='排名') 164 plt.title('同排名下科技区和美食区播放量对比图') 165 plt.xlabel('排名') 166 plt.ylabel('播放量(万)') 167 plt.xticks(rotation=0) 168 plt.savefig(r'images/同排名下科技区和美食区播放量对比图.jpg') 169 df02 = df3.iloc[:,[0,2,4]] 170 df02.plot(kind='bar',x='排名') 171 plt.title('同排名下科技区和美食区弹幕数对比图') 172 plt.xlabel('排名') 173 plt.ylabel('弹幕数') 174 plt.xticks(rotation=0) 175 plt.savefig(r'images/同排名下科技区和美食区弹幕数对比图.jpg') 176 177 # 散点图 178 # print(df1) 179 df1.plot(kind='scatter',x='播放量(科技区)',y='弹幕数(科技区)') 180 plt.title('科技区播放量和弹幕数散点图') 181 plt.xlabel('播放量(万)') 182 plt.ylabel('弹幕数') 183 plt.savefig(r'images/科技区播放量和弹幕数散点图.jpg') 184 df2.plot(kind='scatter',x='播放量(美食区)',y='弹幕数(美食区)') 185 plt.title('美食区播放量和弹幕数散点图') 186 plt.xlabel('播放量(万)') 187 plt.ylabel('弹幕数') 188 plt.savefig(r'images/美食区播放量和弹幕数散点图.jpg') 189 190 plt.show() 191 192 def regression(ls1,ls2): # 回归分析 193 194 # 生成科技区的dataframe 195 df1 = pd.DataFrame(ls1) 196 # print(df1) 197 # 生成美食区的dataframe 198 df2 = pd.DataFrame(ls2) 199 # print(df2) 200 201 # 计算科技区相关系数 202 a = df1.loc[:,'播放量(万)'] 203 b = df1.loc[:,'弹幕数'] 204 # print(type(a)) 205 # print(type(b)) 206 corrTech = round(a.corr(b),4) 207 print("科技区视频播放量(万)和弹幕数的相关系数为:",corrTech) 208 209 # 计算美食区相关系数 210 c = df2.loc[:,'播放量(万)'] 211 d = df2.loc[:,'弹幕数'] 212 # print(type(c)) 213 # print(type(d)) 214 corrFood = round(c.corr(d),4) 215 print("美食区视频播放量(万)和弹幕数的相关系数为:",corrFood) 216 print('') 217 218 # 对科技区的播放量(万)和弹幕数进行线性回归 219 # 将数据拆分为训练集和测试集 220 X1_train,X1_test,Y1_train,Y1_test = train_test_split(a,b,train_size=0.8) 221 # X1_train为训练数据标签,X1_test为测试数据标签.a为样本特征,b为样本标签 222 print('科技区:') 223 print("原始数据特征:",a.shape[0]) 224 print("训练数据特征:",Y1_train.shape[0]) 225 print("测试数据特征:",X1_test.shape[0]) 226 print("原始数据标签:",b.shape[0]) 227 print("训练数据标签:",Y1_train.shape[0]) 228 print("测试数据标签:",Y1_test.shape[0]) 229 230 model = LinearRegression() 231 X1_train = X1_train.values.reshape(-1,1) 232 X1_test = X1_test.values.reshape(-1,1) 233 model.fit(X1_train,Y1_train) 234 interceptTech = model.intercept_ # 截距 235 coefTech = model.coef_ # 回归系数 236 print('最佳拟合线:截距',interceptTech,",回归系数:",coefTech) 237 print('') 238 239 # 同理,对美食区的播放量(万)和弹幕数进行线性回归 240 X2_train,X2_test,Y2_train,Y2_test = train_test_split(c,d,train_size=0.8) 241 # X1_train为训练数据标签,X1_test为测试数据标签.a为样本特征,b为样本标签 242 print('美食区') 243 print("原始数据特征:",c.shape[0]) 244 print("训练数据特征:",Y2_train.shape[0]) 245 print("测试数据特征:",X2_test.shape[0]) 246 print("原始数据标签:",d.shape[0]) 247 print("训练数据标签:",Y2_train.shape[0]) 248 print("测试数据标签:",Y2_test.shape[0]) 249 250 model = LinearRegression() 251 X2_train = X2_train.values.reshape(-1,1) 252 X2_test = X2_test.values.reshape(-1,1) 253 model.fit(X2_train,Y2_train) 254 interceptFood = model.intercept_ # 截距 255 coefFood = model.coef_ # 回归系数 256 print('最佳拟合线:截距',interceptFood,",回归系数:",coefFood) 257 258 return 259 260 if __name__=="__main__": 261 262 # 科技区排名 263 url1 = 'https://www.bilibili.com/v/popular/rank/tech' 264 html1 = get_html(url1) 265 ls1 = [] 266 parse(html1,ls1) 267 # print(ls1) 268 269 # 美食区排名 270 url2 = 'https://www.bilibili.com/v/popular/rank/food' 271 html2 = get_html(url2) 272 ls2 = [] 273 parse(html2,ls2) 274 # print(ls2) 275 276 visualize(ls1,ls2) 277 ciyun(ls1,ls2) 278 write(ls1,ls2) 279 regression(ls1,ls2)
五、总结
1.对主题数据的分析与可视化,可以得到哪些结论,是否达到预期目的
本次爬虫以及数据分析达到初步预期:
- 视频的播放量和弹幕数之间相关关系为中等
- 大部分美食区视频的播放量以及弹幕数超过了相同排名的科技区视频,说明B站美食区的热度更大
- 从散点图可以看出播放量对弹幕数的解释力度不足
2.在完成此设计过程中,得到哪些收获?以及要改进的建议?
完成本次设计后,收获很多,但还有许多不足之处,数据的可视化操作还有许多不熟练的地方,对于python这门课程也有了更深的理解。

浙公网安备 33010602011771号