哔哩哔哩热榜爬虫程序及数据处理
完整的代码与结果在最下面
一、设计方案
1.爬虫的目标是哔哩哔哩排行榜上视频的信息(https://www.bilibili.com/ranking/all/0/1/7)
2.爬取的内容包括网页上显示的所有内容,有排名标题,播放量,弹幕数,up,得分以及视频的url
3.设计方案:根据作业的要求,制作爬虫程序爬取信息并进行数据处理,整个程序分成四个部分,包括数据爬取:(get_rank),数据清洗与处理:(rubbish),文本分析生成词云:(message),数据分析与可视化:(watch)四个部分,所用到的库有request,BeautifulSoup,csv,collections,jieba,io,wordcloud,matplotlib。使用的IDE为anaconda环境配置的pycharm
设计难点:难点主要在数据处理方面,因为哔哩哔哩排行榜上爬取到的数据全都不是单纯的数字,所以在数据处理时遇到了许多问题,这方面查找资料的时间也最长
二、主题页面的结构特征分析
通过观察网页代码,发现排行榜上的每一个视频的标签为li,类名为rank-item,并且我们通过len函数打印提取到的数量也正好100符合排行榜的视频量,所以我们可以在当中继续得到视频其他信息的属性,在代码中都有注释。
三、网络爬虫程序设计
# 代码中注释部分的print都是为了调试用的
1.数据爬取(get_rank)
在保存数据部分用了self函数创建数据对象来将数据读入video对象中并且放在一个列表里,最后保存在csv文件中
结果:
2.数据清洗与处理(rubbish)
这一部分遇到了几个小问题,包括pycharm控制窗的输出结果不会显示所有数据,以及数据的编码问题,都在网上找到解决的方法,最后将清洗过的数据保存为xls文件
3.文本分析生成词云:(message)
文本分析,包括使用jieba库进行分词和wouldcould生成词云,先用列的标题进行查询,读取标题所在的那一列保存在txt文档中,然后使用jieba库进行分词,使用wouldcould制作词云保存成图片
jieba库分词结果:
保存结果:
4.数据分析与可视化:(watch)
数据分析与可视化,包括绘制折线图,柱形图,直方图,散点图
结果:
完整代码:
1 import csv 2 import requests 3 from bs4 import BeautifulSoup 4 import pandas as pd 5 from collections import OrderedDict 6 import jieba 7 import io 8 from scipy.optimize import leastsq 9 from wordcloud import WordCloud 10 import matplotlib.pyplot as plt 11 12 13 # 注释部分的print都是为了调试用的 14 15 def get_rank(): # 数据爬取与采集 16 try: 17 # 发起网络请求 18 url = 'https://www.bilibili.com/ranking/all/0/1/7' 19 response = requests.get(url) 20 html_text = response.text 21 soup = BeautifulSoup(html_text, 'html.parser') 22 23 # 用来保存视频信息的对象 24 class Video: 25 def __init__(self, rank, title, point, visit, review, up, url): 26 self.rank = rank 27 self.title = title 28 self.point = point 29 self.visit = visit 30 self.review = review 31 self.up = up 32 self.url = url 33 def to_csv(self): 34 return [self.rank, self.title, self.point, self.visit, self.review, self.up, self.url] 35 36 # 使用静态方法 37 @staticmethod 38 def csv_title(): 39 return ['排名', '标题', '分数', '播放量', '弹幕数', 'UP', 'URL'] 40 41 # 提取列表 42 items = soup.find_all('li', {'class': 'rank-item'}) 43 videos = [] # 保存提取出来的video 44 for itm in items: 45 title = itm.find('a', {'class': 'title'}).text # 标题 46 point = itm.find('div', {'class': 'pts'}).text # 综合得分 47 rank = itm.find('div', {'class': 'num'}).text # 排名 48 visit = itm.find('span', {'class': 'data-box'}).text # 播放量 49 review = itm.find_all('span', {'class': 'data-box'})[1].text # 弹幕数 50 up = itm.find_all('span', {'class': 'data-box'})[2].text # up 51 url = itm.find('a', {'class': 'title'}).get('href') # 获取链接 52 v = Video(rank, title, point, visit, review, up, url) 53 videos.append(v) 54 # 保存 55 file_name = f'top100.csv' 56 with open(file_name, 'w', newline='') as f: 57 pen = csv.writer(f) 58 pen.writerow(Video.csv_title()) 59 # 导出数据到csv文件中 60 for v in videos: 61 pen.writerow(v.to_csv()) 62 print('保存csv成功') 63 except: 64 return "保存csv失败" 65 66 67 def rubbish(): # 对数据进行清洗和处理 68 try: 69 # pycharm控制窗的输出结果不会显示所有数据,所以在网上得到加入这三行代码进行解决方便查看结果 70 # 加了这一行那表格的一行就不会分段出现了 71 pd.set_option('display.width', 1000) 72 # 显示所有列 73 pd.set_option('display.max_columns', None) 74 # 显示所有行 75 pd.set_option('display.max_rows', None) 76 # 对齐输出结果 77 pd.set_option('display.unicode.ambiguous_as_wide', True) 78 pd.set_option('display.unicode.east_asian_width', True) 79 80 # 使用‘utf-8’会报错,使用其他解码会乱码,最终在网上得到了答案:‘在后面加入指定编译器为python即可’ 81 # 将csv格式数据写入到excel中 82 df = pd.read_csv('top100.csv', engine='python', error_bad_lines=False) # 当某行数据有问题时,不报错,直接跳过,处理脏数据时使用 83 # print(df) #输出csv表格中结果 84 data = OrderedDict() # 有序字典 85 # print(df.columns) #列名 86 # 构建excel格式 87 for line in list(df.columns): 88 data[line] = list(df[line]) 89 obj = pd.DataFrame(data) 90 obj.to_excel('top100.xls', index=False) 91 # 查看统计信息,设置参数buf来存储字符串使数据不打印出来 92 buf = io.StringIO() 93 df.info(buf=buf) 94 s = buf.getvalue() 95 print(s) 96 print('保存xls成功') 97 except: 98 return "保存xls失败" 99 100 101 rubbish() 102 103 104 def message(): # 文本分析,包括使用jieba库进行分词和wouldcould生成词云 105 try: 106 # 用DictReader读取csv的某一列,用列的标题查询 107 with open('top100.csv', 'rt') as csvfile: 108 reader = csv.DictReader(csvfile) 109 column = [row['标题'] for row in reader] 110 # print(column) 111 # 将标题列保存到txt文件中 112 file = open('top100标题.txt', 'w') 113 file.write(str(column)) 114 # 关闭文件 115 file.close() 116 print('保存txt成功') 117 except: 118 return "保存txt失败" 119 120 try: 121 # 使用jieba库进行中文分词 122 final = "" 123 # 文件夹位置 124 filename = r"top100标题.txt" 125 # 打开文件夹,读取内容,并进行分词 126 with open(filename, 'r', encoding='gb18030') as f: 127 for line in f.readlines(): 128 word = jieba.cut(line) 129 for i in word: 130 final = final + i + " " 131 # print(final) 132 print('jieba分词成功') 133 except: 134 return 'jieba分词失败' 135 136 try: 137 # 使用worldcould制作词云 138 # 打开文本 139 text = open('top100标题.txt').read() 140 # 生成对象 141 wc = WordCloud(font_path='C:\Windows\Fonts\simfang.ttf', 142 width=800, 143 height=600, 144 mode='RGBA', 145 background_color=None).generate(text) 146 # 显示词云 147 plt.imshow(wc, interpolation='bilinear') 148 plt.axis('off') 149 plt.show() 150 # 保存到文件 151 wc.to_file('标题词云.png') # 生成图像是透明的 152 print('保存词云成功') 153 except: 154 return '保存词云失败' 155 156 157 message() 158 159 160 def watch(): # 数据分析与可视化,包括绘制折线图,柱形图,直方图,散点图 161 try: 162 # 获得绘图数据 163 point = pd.read_csv('top100.csv', engine='python') 164 # print(data.isnull().sum) 165 # 将字符串数据进行去除替换 166 rank = point['排名'] 167 # print(rank) 168 points = point['分数'].map(lambda x: int(x.replace('综合得分', ''))) 169 # print(points) 170 # 用来正常显示中文标签 171 plt.rcParams['font.sans-serif'] = ['SimHei'] 172 # 用来正常显示负号 173 plt.rcParams['axes.unicode_minus'] = False 174 print('获取绘图数据成功') 175 except: 176 return '获取数据失败' 177 178 try: 179 # 根据数据绘制折线图 180 plt.plot(rank, 181 points, 182 c='red', 183 alpha=0.5) 184 # 绘图表区域着色 185 plt.fill_between(rank, 186 points, 187 facecolor='blue', 188 alpha='0.2') 189 # 设置图形的格式 190 plt.title('top100综合热度得分折线图', 191 fontsize=24) 192 plt.xlabel('排名', 193 fontsize=24) 194 plt.ylabel('热度得分', 195 fontsize=12) 196 # 参数刻度线样式设置 197 plt.tick_params(axis='both', 198 which='major', 199 labelsize=10) 200 # 保存图片 201 plt.savefig(fname="top100综合热度得分折线图.png", 202 figsize=[10, 10]) 203 # 显示折线图 204 plt.show() 205 print('折线图保存成功') 206 except: 207 return '折线图保存失败' 208 209 try: 210 # 根据数据绘制柱形图 211 # 创建基础图 212 fig = plt.figure() 213 # 在基础图上仅绘制一个图,括号中的三个参数代表基础图中的统计图布局,参数一次代表:图的行数量、图的列数量、第几个图。本例中,为1行1列,第一个图 214 bar1 = fig.add_subplot(1, 1, 1) 215 # 绘制柱形图,align表示条形与标签中间对齐。 216 bar1.bar(rank, 217 points, 218 align='center', 219 color="blue") 220 # 设置x、y轴标签 221 plt.xlabel("排名") 222 plt.ylabel("热度得分") 223 # 设置统计图标题 224 plt.title("top100综合热度得分柱形图") 225 # 保存图片 226 plt.savefig(fname="top100综合热度得分柱形图.png", 227 figsize=[10, 10]) 228 # 显示统计图 229 plt.show() 230 print('柱形图保存成功') 231 except: 232 return '柱形图保存失败' 233 234 try: 235 # 绘制直方图 236 # 绘制基础图 237 fig = plt.figure() 238 hist1 = fig.add_subplot(1, 1, 1) 239 # 绘制直方图 240 # bins=50 表示每个变量的 值应该被分成 50 份。normed=False 表示直方图显示的是频率分布 241 hist1.hist(points, 242 bins=50, 243 color="blue", 244 density=False) 245 # 确定坐标轴位置 246 hist1.xaxis.set_ticks_position("bottom") 247 hist1.yaxis.set_ticks_position("left") 248 # 设置坐标轴标签 249 plt.xlabel("热度得分") 250 plt.ylabel("人数") 251 # 设置标题 252 plt.title("top100综合热度得分直方图") 253 # 保存图片 254 plt.savefig(fname="top100综合热度得分直方图.png", figsize=[10, 10]) 255 # 显示图形 256 plt.show() 257 print('直方图保存成功') 258 except: 259 return '直方图保存失败' 260 try: 261 # 绘制散点图 262 fig = plt.figure() 263 scatter1 = fig.add_subplot(1, 1, 1) 264 # 导入数据 265 scatter1.scatter(rank, points) 266 # 确定坐标轴位置 267 scatter1.xaxis.set_ticks_position('bottom') 268 scatter1.yaxis.set_ticks_position('left') 269 # 设置坐标轴标签 270 plt.xlabel("排名") 271 plt.ylabel("热度得分") 272 # 设置图表标题 273 plt.title("top100综合热度得分散点图") 274 # 保存图片 275 plt.savefig(fname="top100综合热度得分散点图.png", 276 figsize=[10, 10]) 277 # 显示图形 278 plt.show() 279 print('散点图保存成功') 280 except: 281 return '散点图保存失败' 282 283 284 watch()
运行结果:
四、结论
通过爬虫程序的制作,认识到利用爬虫程序可以做到很多很酷的事情,能够通过爬虫爬取信息并运用其他库处理信息对工作效率的提升是多么的大,提高了自己对python的兴趣,坚定了认真学习的目标。但制作过程中仍有不足之处,今后会加强学习,多向老师同学请教,不断完善改进。