《基于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.对主题数据的分析与可视化,可以得到哪些结论,是否达到预期目的

本次爬虫以及数据分析达到初步预期:

  1. 视频的播放量和弹幕数之间相关关系为中等
  2. 大部分美食区视频的播放量以及弹幕数超过了相同排名的科技区视频,说明B站美食区的热度更大
  3. 从散点图可以看出播放量对弹幕数的解释力度不足

2.在完成此设计过程中,得到哪些收获?以及要改进的建议?

完成本次设计后,收获很多,但还有许多不足之处,数据的可视化操作还有许多不熟练的地方,对于python这门课程也有了更深的理解。

posted @ 2021-12-29 22:29  岳鹏程  阅读(319)  评论(0)    收藏  举报