从 2019 年 3 月第 1 期开始,哔哩哔哩官方每周会汇总一次本周必看视频,截止 2024 年 1 月 22 日,已经更新了 252 期。
今天,我们就爬取“每周必看”这个栏目的 252 期视频,获取视频名称、视频封面、up 主、播放量、弹幕量、点赞投币量等信息。
目标网址https://www.bilibili.com/v/popular/weekly?num=252
请求 url 为 https://api.bilibili.com/x/web-interface/popular/series/one?number=252
通过更改number
参数,就可以构建每一期的视频列表 url
response 内就是每一期视频相关信息,包括视频名称、图片地址、播放量、点赞投币转发、up 主信息等内容。
因为更新到 252 期,建立一个 for 循环,从第 1 起爬到第 152 期。创建一个 DataFrame 类型的 content 对象,存放爬到的所有数据。
import requests
import pandas as pd
#
content = pd.DataFrame()
for num in range(1, 253):
url = f"https://api.bilibili.com/x/web-interface/popular/series/one?number={num}"
headers = {
"Cookie": "",
"User-Agent": ""
}
response_json = requests.get(url=url, headers=headers).json()
df = pd.json_normalize(response_json['data']['list'], errors='ignore')
df['num'] = num
content = pd.concat([content, df])
print("第", num, "期,本期", df.shape[0], "条,总计", content.shape[0], "条")
防止被检测到,每次循环设置一个睡眠等待时间
import time
time.sleep(3)
通过 252 次循环,就可以爬到所有的数据了,接下来对数据排列顺序进行调整,把我们想看数据排在前面,方便阅读。
由于原始的字段排序比较乱,对数据列重新排序,将列的名称按需要进行手动排序,运行代码如下:
# 列重新排序
content = content.loc[:,
['num', 'owner.name', 'owner.mid', 'title', 'desc', 'dynamic', 'rcmd_reason', 'tname', 'tid',
'dimension.width', 'dimension.height', 'short_link_v2', 'stat.view', 'stat.vv', 'stat.danmaku',
'stat.reply', 'stat.like', 'stat.favorite', 'stat.coin', 'stat.share', 'aid', 'stat.aid',
'cid', 'bvid', 'videos', 'copyright', 'owner.face', 'pic', 'pubdate', 'ctime', 'state', 'duration',
'season_type', 'is_ogv', 'ogv_info', 'enable_vt', 'ai_rcmd', 'rights.bp', 'rights.elec', 'rights.download',
'rights.movie', 'rights.pay', 'rights.hd5', 'rights.no_reprint', 'rights.autoplay', 'rights.ugc_pay',
'rights.is_cooperation', 'rights.ugc_pay_preview', 'rights.no_background', 'rights.arc_pay',
'rights.pay_free_watch', 'stat.now_rank', 'stat.his_rank', 'stat.dislike', 'stat.vt', 'dimension.rotate',
'pub_location', 'season_id', 'mission_id', 'order_id', 'up_from_v2', 'first_frame', 'redirect_url',
'premiere.state', 'premiere.start_time', 'premiere.room_id']]
将包含所有视频信息的 content 对象导出为一个 excel 文件,保存到本地。
正常情况下保存到 excel 文件都没问题,但是!这次遇到了不正常情况!没那么简单!
例如在第 143 期,由于字符串中含有特殊字符,导致保存环节报错,截图如下:
使用下面代码 pandas 写入 excel 时报错。
content.to_excel(f'小破站-每周必看-总252期-总计{content.shape[0]}条.xlsx', index=False)
由于含有不支持的字符会导致保存报错,需要再写入原文件前清除 openpyxl 不支持的字符。
参考 https://blog.csdn.net/koxb/article/details/131718681
from openpyxl.cell.cell import ILLEGAL_CHARACTERS_RE
# 写入原文件前清除openpyxl不支持的字符
for col in content.columns:
content[col] = content[col].apply(lambda x: ILLEGAL_CHARACTERS_RE.sub(r'', str(x) if not pd.isna(x) else ''))
with pd.ExcelWriter(f"小破站-每周必看-总252期-总计{content.shape[0]}条.xlsx", engine='openpyxl',
mode='w') as writer:
content.to_excel(writer, index=False)
print(f"保存完成!总计{content.shape[0]}条")
终于!找到了 url,循环爬完了数据,也顺利保存到本地了。完整代码如下:
import requests
import pandas as pd
import time
from openpyxl.cell.cell import ILLEGAL_CHARACTERS_RE
# content存放爬到的所有数据。
content = pd.DataFrame()
for num in range(1, 253):
url = f"https://api.bilibili.com/x/web-interface/popular/series/one?number={num}"
headers = {
"Cookie": "", #切换自己的
"User-Agent": "" #切换自己的
}
response_json = requests.get(url=url, headers=headers).json()
df = pd.json_normalize(response_json['data']['list'], errors='ignore')
df['num'] = num
content = pd.concat([content, df])
print("第", num, "期,本期", df.shape[0], "条,总计", content.shape[0], "条")
time.sleep(3)
# 列重新排序
content = content.loc[:,
['num', 'owner.name', 'owner.mid', 'title', 'desc', 'dynamic', 'rcmd_reason', 'tname', 'tid',
'dimension.width', 'dimension.height', 'short_link_v2', 'stat.view', 'stat.vv', 'stat.danmaku',
'stat.reply', 'stat.like', 'stat.favorite', 'stat.coin', 'stat.share', 'aid', 'stat.aid',
'cid', 'bvid', 'videos', 'copyright', 'owner.face', 'pic', 'pubdate', 'ctime', 'state', 'duration',
'season_type', 'is_ogv', 'ogv_info', 'enable_vt', 'ai_rcmd', 'rights.bp', 'rights.elec', 'rights.download',
'rights.movie', 'rights.pay', 'rights.hd5', 'rights.no_reprint', 'rights.autoplay', 'rights.ugc_pay',
'rights.is_cooperation', 'rights.ugc_pay_preview', 'rights.no_background', 'rights.arc_pay',
'rights.pay_free_watch', 'stat.now_rank', 'stat.his_rank', 'stat.dislike', 'stat.vt', 'dimension.rotate',
'pub_location', 'season_id', 'mission_id', 'order_id', 'up_from_v2', 'first_frame', 'redirect_url',
'premiere.state', 'premiere.start_time', 'premiere.room_id']]
# 写入原文件前清除openpyxl不支持的字符
for col in content.columns:
content[col] = content[col].apply(lambda x: ILLEGAL_CHARACTERS_RE.sub(r'', str(x) if not pd.isna(x) else ''))
with pd.ExcelWriter(f"小破站-每周必看-总252期-总计{content.shape[0]}条.xlsx", engine='openpyxl',
mode='w') as writer:
content.to_excel(writer, index=False)
print(f"保存完成!总计{content.shape[0]}条")
pycharm 控制台输出如下,爬完 252 期,获取到 8697 条数据:
保存的 excel 表如下,数据维度还是很丰富的:
哈哈,数据分析这一部分留着下一期再写。
之前一直用 pyecharts 库进行数据可视化,下期准备试试大名鼎鼎的 matplotlib 库。
本次爬虫还是很简单的一个案例,但是在最后保存数据环节翻了船。
可以采用每爬一页数据就保存一个 excel 文件的方式,减少重复爬取一次的损失。
更好的方式是在进行数据保存之前,做一下数据处理,删除特殊字符。
本文首发在“程序员coding”公众号,欢迎关注与我一起交流学习。