一、选题的背景
为什么要选择此选题?要达到的数据分析的预期目标是什么?(10 分)
现在大家都很喜欢b站,我也作为b站老用户,所以这个爬虫通过爬取b站播放排行榜信息,来看看最近必看的有用的好玩的任何视频。
二、主题式网络爬虫设计方案(10 分)
1.主题式网络爬虫名称:爬取b站热门播放排行榜
2.主题式网络爬虫爬取的内容与数据特征分析:
通过request爬取b站热门视频排行榜的排名、播放量、弹幕数
使用BeautifulSoup分析网页结构定位内容所在标签获取数据
使用Numpy对获取的数据进行数据清洗
使用matplotlib对数据进行可视化处理
3.主题式网络爬虫设计方案概述:
数据获取需要分为几个步骤实现:
1) 通过request获取网页资源
2) 使用BuautifulSoup解析网页,定位爬取资源
3) 编写代码将数据保存到csv文件中
三、主题页面的结构特征分析(10 分)
数据来源:
https://www.bilibili.com/v/popular/all?spm_id_from=333.851.b_7072696d61727950616765546162.3
Htmls页面解析
(1) 需要爬取的网页
(1) 按下F12打开开发者模式
(1) 按下ctrl + shift + c然后点击需要爬取的内容
(1) 从最下面的层级列表中可以看到我们需要爬取的标题最终在a.title这个标签下面
(5) 同理找到播放量和弹幕数
四、网络爬虫程序设计
1.
数据爬取及采集:
---------------------------------------------------------------------------------------------
导入程序所需要的所有第三方运行库
1 import requests #获取页面数据 2 import pandas as pd #用于数据清洗 3 from bs4 import BeautifulSoup #解析页面 4 import numpy as np 5 import matplotlib #绘图库 6 import seaborn as sns 7 from matplotlib import pyplot as plt 8 import re #用于正则表达式 9 from scipy.sparse import data 10 import matplotlib.pyplot as plt 11 from imageio import imread 12 from wordcloud import WordCloud
获取页面响应数据
1 #获取页面响应数据 2 3 def getHtmlText(url): 4 try: 5 #UA伪装 6 headers = { 7 '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 Edg/96.0.1054.62' 8 } 9 html = page_text = requests.get(url = url,headers = headers) 10 # html.encoding = 'utf-8' #设置页面编码格式为utf-8防止获取到的是乱码 11 htmlText = html.text 12 # print(htmlText) 13 return htmlText 14 except: 15 print("获取页面数据失败") #若出现异常打印字符串
BeautifulSoup进行页面解析
1 #使用BeautifulSoup进行页面解析 2 def ymjiexi(html_text): 3 soup = BeautifulSoup(html_text,'html.parser') 4 return soup
对爬取内容进行定位
1 #对爬取内容进行定位 2 def Title(): #获取标题并进行存储 3 tit = [] #创建数组进行存储 4 title = ymjiexi(getHtmlText(url)).select('.info > a') 5 for ti in title: 6 tit.append(ti.text) 7 return tit 8 #获取播放量并进行存储 9 def getBo(): 10 bofan = [] #创建数组进行存储 11 bo = ymjiexi(getHtmlText(url)).select('.detail-state > span:nth-of-type(1)') 12 for b in bo: 13 bof = re.findall(r'\d+\.\d+|\d+',b.text) 14 bofan.append(bof[0]) 15 return bofan
获取弹幕并进行存储
1 #获取弹幕并进行存储 2 def danmu(): 3 danmus = [] #存储弹幕数 4 danmu = ymjiexi(getHtmlText(url)).select('.detail-state > span:nth-of-type(2)') 5 for d in danmu: 6 bof = re.findall('\d+\.\d+|\d+',d.text) 7 if float(bof[0]) < 10: 8 bof[0] = float(bof[0]) * 10000 #如果弹幕数量 9 danmus.append(bof[0]) 10 return danmus
运行得到结果
1 if __name__ == '__main__': 2 url = 'https://www.bilibili.com/v/popular/rank/all' 3 ymjiexi(getHtmlText(url)) 4 Title() #获取标题 5 getBo() #获取播放量 6 danmu() #获取弹幕量 7 datas = [] #存储标题和播放量 8 print("{:^10}\t{:^30}\t{:^40}\t{:^30}".format( '排名','标题', '播放量','弹幕数')) 9 for i in range(10): 10 print("{:^10}\t{:^30}\t{:^40}\t{:^30}".format(i+1,Title()[i],getBo()[i],danmu()[i])) 11 datas.append([i+1,Title()[i],getBo()[i],danmu()[i]])
12 对数据进行清理: 13 14 15 df = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) #导入文件 16 #查找重复值 17 df.duplicated() 18 print(df.duplicated()) 19 删除无效行列 20 21 # 删除无效列 22 23 df.drop('标题',axis = 1,inplace = True) 24 25 print(df.head(10)) 26 27 # 查找是否有空值 28 29 print(df['播放量'].isnull().value_counts()) 30 31 print(df['弹幕数'].isnull().value_counts()) 32 33 34 35 异常值的观察 36 37 38 39 abnormal = df.describe() 40 41 42 43 print(abnormal) 44 45 # 查看相关系数 46 47 xishu = df.corr() 48 49 print(xishu)
数据分析与可视化:
散点图
1 def aScatter(): 2 plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文标签,防止数据可视化总出现中文字符不显示 3 x = df.播放量 4 y = df.弹幕数 5 plt.xlabel('播放量') 6 plt.ylabel('弹幕数') 7 plt.scatter(x, y, color = "red", label = "点", s = 50) 8 plt.title("播放量与弹幕数量的散点图") 9 plt.legend(loc = 'best') 10 plt.show() 11 aScatter()
折线图
1 # 折线图 2 # 排名与弹幕数的折线图 3 def brokenLine(): 4 plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文标签,防止数据可视化总出现中文字符不显示 5 dp = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) 6 x = dp.排名 7 y = dp.弹幕数 8 plt.xlabel("排名") 9 plt.ylabel("弹幕数") 10 plt.plot(x, y, color = "green", label = "折线") 11 plt.title("播放量与弹幕数的折线图") 12 plt.legend() 13 plt.show() 14 brokenLine()
扇形图
1 #播放量与弹幕数的扇形图 2 def pieChart(): 3 dp = pd.DataFrame(pd.read_csv('b站播放量排行榜(删除后).csv')) 4 plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文标签,防止数据可视化总出现中文字符不显示 5 x = df.播放量 6 y = df.弹幕数 7 name = [x[0], x[1], x[2], x[3], x[4]] 8 math = [y[0], y[1], y[2], y[3], y[4]] 9 explode = [0.1, 0.1, 0.1, 0.1, 0.1] 10 plt.pie(math, labels = name, colors = ["r", "g", "c", "b", "y"], explode = explode) 11 plt.axis("equal") 12 plt.title("b站热榜播放量与弹幕数的扇形图") 13 plt.show() 14 pieChart()
回归直线图
1 def back(): 2 dp = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) 3 plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文标签,防止数据可视化总出现中文字符不显示 4 plt.rcParams['font.serif'] = ['KaiTi'] 5 plt.rcParams['axes.unicode_minus'] = False 6 x = df.排名 7 y = df.弹幕数 8 # X,Y为散点图的 9 X = df.排名 10 Y = df.弹幕数 11 # 先定义所需要的数据 12 x_i2 = 0 13 x_i = 0 14 y_i = 0 15 # 用mean()方法计算出x,y的均值 16 q = x.mean() 17 w = y.mean() 18 for i in range(7): 19 x_i2 = x_i + x[i] * x[i] 20 x_i = x_i + x[i] 21 y_i = y_i + y[i] 22 m_1 = x_i * y_i - 7 * q * w 23 m_2 = x_i2 - 7 * q * q 24 k = m_1 / m_2 25 # 截距 26 b = w - q * k 27 x = np.linspace(0, 7) 28 y = k * x + b 29 print("斜率k=", k, "截距b=", b) 30 plt.figure(figsize = (6, 4)) 31 plt.xlabel('排名') 32 plt.ylabel('弹幕数') 33 plt.scatter(X, Y, color = "green", label = "散点", linewidth = 2) 34 plt.plot(x, y, color = "blue", label = "回归直线") 35 plt.title("回归直线图") 36 plt.legend() 37 plt.show() 38 back()
条形图
1 # 绘制条形图 2 def bar(): 3 plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文标签,防止数据可视化总出现中文字符不显示 4 df = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) 5 x = df.排名 6 y = df.播放量 7 plt.xlabel('排名') 8 plt.ylabel('播放量') 9 plt.bar(x,y,color='red',width = 0.8) 10 plt.title("排名与播放量的条形图") 11 plt.show() 12 bar()
线性关系图
1 # 线性关系图 2 def line(): 3 plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文标签,防止数据可视化总出现中文字符不显示 4 plt.rcParams['font.serif'] = ['KaiTi'] 5 plt.rcParams['axes.unicode_minus'] = False 6 df = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) 7 sns.lmplot(x = "排名", y = "播放量", data = df) 8 plt.show() 9 line()
标题词云图
1 #读取b站播放量排行榜.csv将其中的标题名称写到txt文本中 2 text = pd.read_csv("b站播放量排行榜.csv", encoding='utf-8') 3 with open("标题.txt",'a+', encoding='utf-8') as f: 4 for title in text.标题: 5 f.write((str(title)+'\n')) 6 #读取文本 7 text=open('标题.txt',encoding='utf-8').read() 8 #词云的背景图片 9 photo=imread('小鸡.jpg') 10 Cyun=WordCloud( 11 background_color = "white", 12 mask = photo, 13 14 width=1000, 15 16 repeat=True, 17 18 font_path=r'simfang.ttf', 19 20 height=1600).generate(text) 21 22 plt.imshow(Cyun) 23 24 plt.axis("off") 25 26 plt.show() 27 28 #保存图片 29 30 Cyun.to_file("标题词云.jpg")
爬虫源码:
1 #导入程序所需要的所有第三方运行库 2 import requests 3 import bs4 4 import pandas as pd 5 from bs4 import BeautifulSoup 6 import numpy as np 7 import matplotlib 8 import seaborn as sns 9 from matplotlib import pyplot as plt 10 import re 11 from scipy.sparse import data 12 from wordcloud import WordCloud 13 import matplotlib.pyplot as plt 14 from imageio import imread 15 16 17 plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文标签,防止画图出现中文字符不显示 18 plt.rcParams['font.serif'] = ['KaiTi'] 19 plt.rcParams['axes.unicode_minus'] = False 20 21 22 23 24 #获取页面数据 25 def getHtmlText(url): 26 try: 27 #UA伪装 28 headers = { 29 '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 Edg/96.0.1054.62' 30 } 31 html = page_text = requests.get(url = url,headers = headers) 32 # html.encoding = 'utf-8' #设置页面编码格式为utf-8防止获取到的是乱码 33 htmlText = html.text 34 # print(htmlText) 35 return htmlText 36 except: 37 print("获取页面数据失败") #若出现异常打印字符串 38 39 #使用BeautifulSoup进行页面解析 40 def ymjiexi(html_text): 41 soup = BeautifulSoup(html_text,'html.parser') 42 return soup 43 44 #对爬取内容进行定位 45 def Title(): #获取标题并进行存储 46 tit = [] #创建数组进行存储 47 title = ymjiexi(getHtmlText(url)).select('.info > a') 48 for ti in title: 49 tit.append(ti.text) 50 return tit 51 #获取播放量并进行存储 52 def getBo(): 53 bofan = [] #创建数组进行存储 54 bo = ymjiexi(getHtmlText(url)).select('.detail-state > span:nth-of-type(1)') 55 for b in bo: 56 bof = re.findall(r'\d+\.\d+|\d+',b.text) 57 bofan.append(bof[0]) 58 return bofan 59 def danmu(): 60 danmus = [] #存储弹幕数 61 danmu = ymjiexi(getHtmlText(url)).select('.detail-state > span:nth-of-type(2)') 62 63 for d in danmu: 64 bof = re.findall('\d+\.\d+|\d+',d.text) 65 # print(bof) 66 danmus.append(bof[0]) 67 return danmus 68 69 70 if __name__ == '__main__': 71 url = 'https://www.bilibili.com/v/popular/rank/all' 72 ymjiexi(getHtmlText(url)) 73 Title() #获取标题 74 getBo() #获取播放量 75 danmu() #获取弹幕量 76 datas = [] #存储标题和播放量 77 print("{:^30}\t{:^40}\t{:^30}".format( '标题', '播放量','弹幕数')) 78 for i in range(10): 79 print("{:^30}\t{:^40}\t{:^30}".format(Title()[i],getBo()[i],danmu()[i])) 80 datas.append([Title()[i],getBo()[i],danmu()[i]]) 81 print(datas) 82 83 #将爬取到的内容保存到csv文件里进存储和后续的数据清洗 84 df = pd.DataFrame(datas, columns = ["标题", '播放量', '弹幕数']) 85 df.to_csv('b站播放量排行榜.csv', index = False) #设置index = Flase防止出现未命名的列 86 print("爬取完毕!") 87 88 #进行数据清洗 89 90 df = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) #导入文件 91 # 查找重复值 92 df.duplicated() 93 print(df.duplicated()) 94 95 # 删除无效行 96 df.drop('标题',axis = 1,inplace = True) 97 98 print(df.head(10)) 99 100 # 查找是否有空值 101 print(df['标题'].isnull().value_counts()) 102 print(df['热度'].isnull().value_counts()) 103 104 # 删除弹幕数量小于20的标题 105 def drop(): 106 drop = df.drop(index=(df.loc[(df['弹幕数']<20)].index)) 107 print(drop) 108 drop.to_csv('b站播放量排行榜(删除后).csv', index = False) 109 print("保存成功") 110 drop() 111 112 113 114 # 异常值的观察 115 abnormal = df.describe() 116 print(abnormal) 117 118 119 # 查看相关系数 120 xishu = df.corr() 121 print(xishu) 122 123 # 散点图 124 def aScatter(): 125 x = df.播放量 126 y = df.弹幕数 127 plt.xlabel('播放量') 128 plt.ylabel('弹幕数') 129 plt.scatter(x, y, color = "red", label = "点", s = 50) 130 plt.title("播放量与弹幕数量的散点图") 131 plt.legend(loc = 'best') 132 plt.show() 133 aScatter() 134 135 136 # 折线图 137 # 播放量与弹幕数的折线图 138 def brokenLine(): 139 dp = pd.DataFrame(pd.read_csv('b站播放量排行榜(删除后).csv')) 140 print(df.head(10)) 141 x = dp.播放量 142 y = dp.弹幕数 143 plt.xlabel("播放量") 144 plt.ylabel("弹幕数") 145 plt.plot(x, y, color = "green", label = "折线") 146 plt.title("播放量与弹幕数的折线图") 147 plt.legend() 148 plt.show() 149 brokenLine() 150 151 152 #播放量与弹幕数的扇形图 153 def pieChart(): 154 dp = pd.DataFrame(pd.read_csv('b站播放量排行榜(删除后).csv')) 155 x = df.播放量 156 y = df.弹幕数 157 name = [x[0], x[1], x[2], x[3], x[4]] 158 math = [y[0], y[1], y[2], y[3], y[4]] 159 explode = [0.1, 0.1, 0.1, 0.1, 0.1] 160 plt.pie(math, labels = name, colors = ["r", "g", "c", "b", "y"], explode = explode) 161 plt.axis("equal") 162 plt.title("b站热榜播放量与弹幕数的扇形图") 163 plt.show() 164 pieChart() 165 166 #播放量与弹幕数的回归直线图 167 # 回归直线的图 168 def back(): 169 dp = pd.DataFrame(pd.read_csv('b站播放量排行榜(删除后).csv')) 170 171 x = df.播放量 172 y = df.弹幕数 173 # X,Y为散点图的 174 X = df.播放量 175 Y = df.弹幕数 176 # 先定义所需要的数据 177 x_i2 = 0 178 x_i = 0 179 y_i = 0 180 # 用mean()方法计算出x,y的均值 181 q = x.mean() 182 w = y.mean() 183 for i in range(7): 184 x_i2 = x_i + x[i] * x[i] 185 x_i = x_i + x[i] 186 y_i = y_i + y[i] 187 188 m_1 = x_i * y_i - 7 * q * w 189 190 m_2 = x_i2 - 7 * q * q 191 192 k = m_1 / m_2 193 # 截距 194 b = w - q * k 195 x = np.linspace(0, 7) 196 y = k * x + b 197 print("斜率k=", k, "截距b=", b) 198 plt.figure(figsize = (6, 4)) 199 plt.xlabel('播放量') 200 plt.ylabel('弹幕数') 201 plt.scatter(X, Y, color = "green", label = "散点", linewidth = 2) 202 plt.plot(x, y, color = "blue", label = "回归直线") 203 plt.title("回归直线图") 204 plt.legend() 205 plt.show() 206 back() 207 208 #绘制条形图 209 def bar(): 210 df = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) 211 x = df.播放量 212 y = df.弹幕数 213 plt.xlabel('播放量') 214 plt.ylabel('弹幕数') 215 plt.bar(x,y,color='red') 216 plt.title("播放量与弹幕数的条形图") 217 plt.show() 218 bar() 219 220 221 # 线性关系图 222 def line(): 223 df = pd.DataFrame(pd.read_csv('b站播放量排行榜.csv')) 224 sns.lmplot(x = "播放量", y = "弹幕数", data = df) 225 plt.show() 226 line() 227 228 #读取b站播放量排行榜.csv将其中的标题名称写到txt文本中 229 text = pd.read_csv("b站播放量排行榜.csv", encoding='utf-8') 230 with open("标题.txt",'a+', encoding='utf-8') as f: 231 for title in text.标题: 232 f.write((str(title)+'\n')) 233 234 #读取文本 235 text=open('标题.txt',encoding='utf-8').read() 236 #词云的背景图片 237 photo=imread('小鸡.jpg') 238 Cyun=WordCloud( 239 background_color = "white", 240 mask = photo, 241 width=1000, 242 repeat=True, 243 font_path=r'simfang.ttf', 244 height=1600).generate(text) 245 plt.imshow(Cyun) 246 plt.axis("off") 247 plt.show() 248 #保存图片 249 Cyun.to_file("标题词云.jpg")
总结:
经过爬虫和数据可视化,我们可以观察到b站排行榜的想知道的信息,虽然实用性不高,但是就是好玩,每个人所追求的东西都不一样,爬虫也并不只是为了完成任务型的工作,我们在学习的过程中应该真正做到学以致用,在玩中学,在学习中自己找乐子,才能找到枯燥学习中的乐趣,看看词云图,是不是很喜感。当然这个也不是完全没用的,b站确实是能够了解很多趋于年轻态的信息,大龄人若想了解年轻人所想,不妨也试试去b站。未来我希望能继续完善能力,在此之上有更大发展。