这里记录一下爬取b站up主视频数据的过程
** 文章为2022年写的,中间可能有变化,总的思路还是这个**
1.为了爬去视频的播放量、视频名称、发布时间等数据,首先想到来到up主页的投稿区,然后再进行分析。
这里是爬取的up主:
2.然后分析能否直接找到这些数据的json文件,如果找到了就可以直接分析,没找到就尝试爬去html然后再进行分析,下面就分析网站加载的数据:
3.运气很好,分析之后找到了数据加载的来源,具体如下
分析数据可以得到:
- aid:视频的唯一id
- bvid:视频的唯一id,这里我觉得是和aid相同的意思
- comment:视频评论量
- created:视频发布的时间,可以用时间戳转换器转成我们熟悉的时间形式
- description:作者对视频的描述
- length:视频的时间长度
- pic:视频的封面图片地址
- play:播放量
- title:视频的名字
- video_review:弹幕数
知道了有数据,我们还要想怎么能爬取完所有的视频,接下来就分析这个数据来源的URL:第一页
第二页
通过分析很容易知道pn=1
就是控制页数的,这样我们通过构造pn=?就可以控制下载的页数,下面就开始编程实现了。
4.编码分析部分
4.1首先导入基本的模块
import requests #访问页面用
import time #暂停时间,爬取速度太快容易被发现,可能会出问题,所以需要暂停
import openpyxl #数据存储到excel中会用
4.2首先编写数据爬取的测试函数
def get_Requests():
pg = 1 #一共有5页的内容
# headers用来模拟是通过浏览器发起的get请求,在刚才分析url的Headers那儿找
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
}
for i in range(1,pg+1):
# 通过构造每一页进行访问
url = f"https://api.bilibili.com/x/space/arc/search?mid=501271968&ps=30&tid=0&pn={i}&keyword=&order=pubdate&jsonp=jsonp"
res = requests.get(url=url, headers=headers)
print(res.text)
get_Requests()
这里测试了一页,发现能把数据爬取下来,然后我们就可以继续编写下去,并完善,在这之前,需要先了解json的解析器,我用的是json-handle,一个浏览器的插件,作用是方便我们分析json的信息,样子如下:
def get_Requests():
pg = 5 #一共有5页的内容
# headers用来模拟是通过浏览器发起的get请求,在刚才分析url的Headers那儿找
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
}
# 提前定义数据的列表,后面用来存储爬取的数据
comment = [] # 评论数
play = [] # 播放量
pic = [] # 封面图
description = [] # 描述
title = [] # 标题
created = [] # 上传时间
length = [] # 视频长度
video_review = [] # 弹幕量
aid = []
bvid = []
for i in range(1,pg+1):
# 通过构造每一页进行访问
url = f"https://api.bilibili.com/x/space/arc/search?mid=501271968&ps=30&tid=0&pn={i}&keyword=&order=pubdate&jsonp=jsonp"
# 通过url进行访问,并返回数据
res = requests.get(url=url, headers=headers)
# 如果有编码错误的问题,可以先解码
res.enconding = 'utf-8'
# 这一步因为我们得到了json文件,就可以直接用字典的形式访问到我们想要的数据
res_dict = res.json()
# 通过字典的方式,得到我们需要的数据
for j in range(30): # 因为数据一次可以爬取20个,需要一个一个存储下来
try: # 要用try的原因考虑到最后一页的数据可能不是有30个视频,就会报错
# 通过append把数据存储在列表中
comment.append(res_dict['data']['list']['vlist'][j]['comment'])
play.append(res_dict['data']['list']['vlist'][j]['play'])
pic.append(res_dict['data']['list']['vlist'][j]['pic'])
description.append(res_dict['data']['list']['vlist'][j]['description'])
title.append(res_dict['data']['list']['vlist'][j]['title'])
created.append(res_dict['data']['list']['vlist'][j]['created'])
length.append(res_dict['data']['list']['vlist'][j]['length'])
video_review.append(res_dict['data']['list']['vlist'][j]['video_review'])
aid.append(res_dict['data']['list']['vlist'][j]['aid'])
bvid.append(res_dict['data']['list']['vlist'][j]['bvid'])
except:
pass # 出现问题直接pass掉不管
# 至此,第一次的爬去完成,可以测试数据时候爬取时候成功
# print(bvid)
# 为了避免因为访问速度过快,这里没爬取一页暂停1s
print("--第%d页爬取完成\n"%(i))
time.sleep(1)
# 此处数据全部爬取完成,下面将通过函数存储
store_Data(comment, play, pic, description, title, created, length, video_review, aid, bvid)
承接上个函数,这里编写存储函数
def store_Data(comment, play, pic, description, title, created, length, video_review, aid, bvid):
print("--开始存储")
# 这里用到openpyxl,我先在本目录下创建了‘数据存储.xlsx’的文件用来存储
wb = openpyxl.load_workbook('数据存储.xlsx')
# 选择里面的第一个sheet就行
sheet = wb.worksheets[0]
# 这里先给数据信息取个名,放在第一行
sheet['A1'] = 'bvid'
sheet['B1'] = 'aid'
sheet['C1'] = '视频标题' # title
sheet['D1'] = '视频描述' # description
sheet['E1'] = '上传时间' # created
sheet['F1'] = '视频长度' # length
sheet['G1'] = '弹幕数量' # video_review
sheet['H1'] = '播放量' # play
sheet['I1'] = '评论量' # comment
sheet['J1'] = '封面图地址' # pic
# 由于爬取的每个信息是一一相对的,而且列表长度也是一致的,所以通过len(xx)确定长度
for i in range(len(aid)):
# 注意此时i是从0开始的,而我们的数据在excel中是从第二行开始存储,所以+2
sheet['A'+str(i+2)] = bvid[i]
sheet['B'+str(i+2)] = aid[i]
sheet['C'+str(i+2)] = title[i]
sheet['D'+str(i+2)] = description[i]
sheet['E'+str(i+2)] = created[i]
sheet['F'+str(i+2)] = length[i]
sheet['G'+str(i+2)] = video_review[i]
sheet['H'+str(i+2)] = play[i]
sheet['I'+str(i+2)] = comment[i]
sheet['J'+str(i+2)] = pic[i]
# 存取完成保存
print("完成存储!")
wb.save('数据存储.xlsx')
最后编写主函数,进行调用
if __name__ == "__main__":
get_Requests()
--第1页爬取完成
--第2页爬取完成
--第3页爬取完成
--第4页爬取完成
--第5页爬取完成
--开始存储
完成存储!
查看excel,成功得到数据
5.总结分析
- 应该是可以通过输入mid(应该是用户名的意思)直接在主函数输入mid,然后就可以完成后面的
- 应该可以通过找数据直接获取pn,而不用手动输入
为了通过输入mid和pn直接爬取,下面做一下相应的修改,mid可以在控制台alert(mid)
import requests #访问页面用
import time #暂停时间,爬取速度太快容易被发现,可能会出问题,所以需要暂停
import openpyxl #数据存储到excel中会用
import random #让暂停的时间间隔变得随机
def get_Requests(mid, pg):
# headers用来模拟是通过浏览器发起的get请求,在刚才分析url的Headers那儿找
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
}
# 提前定义数据的列表,后面用来存储爬取的数据
comment = [] # 评论数
play = [] # 播放量
pic = [] # 封面图
description = [] # 描述
title = [] # 标题
created = [] # 上传时间
length = [] # 视频长度
video_review = [] # 弹幕量
aid = []
bvid = []
for i in range(1,pg+1):
# 通过构造每一页进行访问
url = f"https://api.bilibili.com/x/space/arc/search?mid={mid}&ps=30&tid=0&pn={i}&keyword=&order=pubdate&jsonp=jsonp"
# 通过url进行访问,并返回数据
res = requests.get(url=url, headers=headers)
# 如果有编码错误的问题,可以先解码
res.enconding = 'utf-8'
# 这一步因为我们得到了json文件,就可以直接用字典的形式访问到我们想要的数据
res_dict = res.json()
# 通过字典的方式,得到我们需要的数据
for j in range(30): # 因为数据一次可以爬取20个,需要一个一个存储下来
try: # 要用try的原因考虑到最后一页的数据可能不是有30个视频,就会报错
# 通过append把数据存储在列表中
comment.append(res_dict['data']['list']['vlist'][j]['comment'])
play.append(res_dict['data']['list']['vlist'][j]['play'])
pic.append(res_dict['data']['list']['vlist'][j]['pic'])
description.append(res_dict['data']['list']['vlist'][j]['description'])
title.append(res_dict['data']['list']['vlist'][j]['title'])
created.append(res_dict['data']['list']['vlist'][j]['created'])
length.append(res_dict['data']['list']['vlist'][j]['length'])
video_review.append(res_dict['data']['list']['vlist'][j]['video_review'])
aid.append(res_dict['data']['list']['vlist'][j]['aid'])
bvid.append(res_dict['data']['list']['vlist'][j]['bvid'])
except:
pass # 出现问题直接pass掉不管
# 至此,第一次的爬去完成,可以测试数据时候爬取时候成功
# print(bvid)
# 为了避免因为访问速度过快,这里没爬取一页暂停随机1~3s
print("--第%d页爬取完成\n"%(i))
time.sleep(random.randint(1,3))
# 此处数据全部爬取完成,下面将通过函数存储
store_Data(comment, play, pic, description, title, created, length, video_review, aid, bvid,mid)
def store_Data(comment, play, pic, description, title, created, length, video_review, aid, bvid,mid):
print("--开始存储")
# 这里用到openpyxl,我先在本目录下创建了‘数据存储.xlsx’的文件用来存储
# wb = openpyxl.load_workbook('数据存储.xlsx')
# 下面测试创建一个wb
wb = openpyxl.Workbook()
# 选择里面的第一个sheet就行
sheet = wb.worksheets[0]
# 这里先给数据信息取个名,放在第一行
sheet['A1'] = 'bvid'
sheet['B1'] = 'aid'
sheet['C1'] = '视频标题' # title
sheet['D1'] = '视频描述' # description
sheet['E1'] = '上传时间' # created
sheet['F1'] = '视频长度' # length
sheet['G1'] = '弹幕数量' # video_review
sheet['H1'] = '播放量' # play
sheet['I1'] = '评论量' # comment
sheet['J1'] = '封面图地址' # pic
# 由于爬取的每个信息是一一相对的,而且列表长度也是一致的,所以通过len(xx)确定长度
for i in range(len(aid)):
# 注意此时i是从0开始的,而我们的数据在excel中是从第二行开始存储,所以+2
sheet['A'+str(i+2)] = bvid[i]
sheet['B'+str(i+2)] = aid[i]
sheet['C'+str(i+2)] = title[i]
sheet['D'+str(i+2)] = description[i]
sheet['E'+str(i+2)] = created[i]
sheet['F'+str(i+2)] = length[i]
sheet['G'+str(i+2)] = video_review[i]
sheet['H'+str(i+2)] = play[i]
sheet['I'+str(i+2)] = comment[i]
sheet['J'+str(i+2)] = pic[i]
# 存取完成保存
print("完成存储!")
wb.save('数据存储'+'mid:'+str(mid)+'.xlsx')
if __name__ == "__main__":
mid = 2
pn = 1 # 一共有多少页
get_Requests(mid, pn)
--第1页爬取完成
--开始存储
完成存储!
ls
[0m[01;34m数据[0m/ 数据存储mid:2.xlsx 数据存储.xlsx 爬取b站up主数据.ipynb
cd 数据/
/home/alien/Desktop/passage/jupyter/爬取b站数据/数据
ls
木鱼水心数据.xlsx '爬取b站up主数据 (copy).ipynb' 静bi~.xlsx
下面对木鱼水心的数据进行分析
import pandas as pd
df = pd.read_excel('木鱼水心数据.xlsx', 'Sheet1')
data = df[['aid', '视频标题', '上传时间', '弹幕数量', '播放量', '评论量']]
data
aid | 视频标题 | 上传时间 | 弹幕数量 | 播放量 | 评论量 | |
---|---|---|---|---|---|---|
0 | 258309004 | 鲁智深武松再上线!哥哥我想上梁山!《水浒传》P29 | 1657371687 | 26524 | 1144728 | 3781 |
1 | 813123558 | 画风突变!宋江:用魔法打败魔法!《水浒传》P28 | 1656770568 | 19662 | 1492966 | 3753 |
2 | 427790000 | 水浒最具争议剧情之一!《水浒传》P27 | 1656161740 | 40404 | 2259099 | 9804 |
3 | 427516374 | 全员内鬼!史诗级大乱斗之三打祝家庄!《水浒传》P26 | 1655553682 | 18625 | 1390629 | 4281 |
4 | 727459148 | 副本开启!梁山最美女将解锁!一只鸡引发的血案!《水浒传》P25 | 1654949005 | 21944 | 1568569 | 3070 |
... | ... | ... | ... | ... | ... | ... |
1182 | 746399 | 【木鱼动画教室@第五期】如何写对话【最终幻想13】 | 1378257965 | 1478 | 40995 | 103 |
1183 | 744150 | 【木鱼动画教室@第四期】怎么拍对话【龙与虎】 | 1378078717 | 2145 | 50188 | 136 |
1184 | 741112 | 【木鱼动画教室@第三期】游戏CG特点【星际争霸2虫群之心】 | 1377914578 | 1738 | 87845 | 141 |
1185 | 741106 | 【木鱼动画教室@第二期】怎么写出感人故事【龙门镖局】 | 1377914442 | 1742 | 88059 | 202 |
1186 | 739057 | 【木鱼动画教室@第一期】插入镜头【FLCL】 | 1377792640 | 1460 | 202995 | 1646 |
1187 rows × 6 columns
time = pd.to_datetime(data['上传时间'], unit='s')
danmu = data['弹幕数量']
view = data['播放量']
comment = data['评论量']
danmu.sort_values()
67 0
248 0
1079 102
1108 111
1095 117
...
564 55233
103 58507
127 59823
821 104061
215 166861
Name: 弹幕数量, Length: 1187, dtype: int64
data['视频标题'][821]
'【木鱼微剧场】《三国演义》(全集)'
import matplotlib.pyplot as plt
import numpy as np
plt.title("播放量的情况")
plt.xlabel("日期")
plt.ylabel("数量")
plt.yticks((np.arange(0,40000, 10000)))
#plt.plot(time, danmu, label='弹幕量')
#plt.plot(time, view, 'g', label='播放量')
plt.plot(time, comment, label='评论量')
plt.legend(loc='upper left')
<matplotlib.legend.Legend at 0x7f7005cfea40>
from pyecharts.charts import Bar
from pyecharts import options as opts
from pyecharts.globals import ThemeType
bar = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
.add_xaxis(time.tolist())
.add_yaxis('评论', comment.tolist())
.add_yaxis('播放量', view.tolist())
)
bar.render_notebook()
<div id="7dc030caa307413885b9d54f78aba1d6" style="width:900px; height:500px;"></div>