Open wpscott opened 4 years ago
非常感谢!我会仔细学这部分内容,然后重构一下代码 最近先以学自己项目相关的内容为主 没有一个明确的导师带还是有点儿难受,不过会努力的
昨天想了一下,如果只是粗略的看弹幕浓度的话,可以借鉴Bilibili视频底部弹幕曲线的思路,用matplotlib做一个折线图或者柱形图统计关键弹幕。 简单写了一个例子。
import math
import re
import requests # 第三方库,通过pip安装
import xml.etree.ElementTree as ET
from datetime import timedelta
import matplotlib.pyplot as plt # 第三方库,通过pip安装
from matplotlib.ticker import FuncFormatter, StrMethodFormatter # 第三方库,通过pip安装
plt.rcParams["font.sans-serif"] = ["SimHei"] # 使用支持显示中文及日文字符的字体
BILIBILI_API = "https://api.bilibili.com/x"
BILIBILI_API_PAGELIST_AVID = f"{BILIBILI_API}/player/pagelist?aid="
BILIBILI_API_PAGELIST_BVID = f"{BILIBILI_API}/player/pagelist?bvid="
BILIBILI_API_DANMU_LIST = f"{BILIBILI_API}/v1/dm/list.so?oid="
def timeformatter(y, pos): # 格式化柱形图时间
return str(timedelta(seconds=math.floor(y))) if y >= 0 else "无效时间"
formatter = FuncFormatter(timeformatter)
def getDanmu(vid): # 获取弹幕xml
if vid.startswith("BV"):
resp = requests.get(f"{BILIBILI_API_PAGELIST_BVID}{vid}")
elif vid.startswith("av"):
resp = requests.get(f"{BILIBILI_API_PAGELIST_AVID}{vid[2:]}")
elif vid.isdigit():
resp = requests.get(f"{BILIBILI_API_PAGELIST_AVID}{vid}")
else:
return None
if resp.ok:
cid = resp.json()["data"][0]["cid"]
return requests.get(f"{BILIBILI_API_DANMU_LIST}{cid}")
else:
return None
def strQ2B(text): # 全角字符转换成半角字符
tmp = []
for word in text:
code = ord(word)
if code == 12288: # 转换空格
code = 32
elif 65281 <= code and code <= 65374: # 转换字符、数字及字母
code -= 65248
tmp.append(chr(code))
return "".join(tmp)
keywords = "kksk|草|めあ|mea|谁|誰|??".split("|") # 关键字
similar_words = {"めあ": "めあ/mea", "mea": "めあ/mea", "谁": "谁/誰", "誰": "谁/誰"} # 同义词
matches = {
similar_words[keyword] if keyword in similar_words else keyword: []
for keyword in keywords
}
last = 0
danmuxml = getDanmu("BV1RJ411C7jy")
if danmuxml and danmuxml.ok:
root = ET.fromstring(danmuxml.content)
for danmu in root.findall("d"):
t = float(danmu.attrib["p"].split(",")[0]) # 获取弹幕时间
if t > last:
last = math.ceil(t) # 记录最后一条弹幕时间
for keyword in keywords: # 关键字匹配
if re.search(
keyword.replace("?", r"\?"), # ?为regex关键字,需要加"\"进行转义
strQ2B(danmu.text), # 把全角符号、数字及字母转换为半角
re.I, # 忽略大小写
):
matches[
similar_words[keyword] if keyword in similar_words else keyword
].append(t)
# 绘制柱形图
fig, ax = plt.subplots()
ax.set_xlabel("时间")
ax.set_ylabel("个数")
ax.set_xlim(left=0, right=last) # 可选,设置时间轴范围从00:00:00到最后一条弹幕时间
ax.set_ylim(bottom=8, top=32) # 可选,设置次数的上下限
ax.xaxis.set_major_formatter(formatter)
ax.yaxis.set_major_formatter(StrMethodFormatter("{x:.0f}"))
plt.hist( # 绘制柱形图
matches.values(), # 以时间频率为纵坐标
label=matches.keys(), # 以关键字为横坐标
bins=256, # bins指定出现的柱形个数
)
plt.legend()
ax.set_title("关键弹幕次数统计")
plt.show()
else:
print("无法获取弹幕xml")
多谢指导,受益匪浅!我先把库补齐试试效果。 柱状图之前用C写时有想试过做直观一点的图形, 后来试着拿matlab去读数据做了类似的实现 (当然没有这个实现的简洁漂亮,得做matlab和C++之间的交互) 当时做了一点就是每个柱形上都有显示对应时间(精准到秒内)。 这样就不需要zoom或者去查阅另一页里头文本框的输出, 就能在图上一眼识别定位柱形出现的地方,快速获取必要的信息量 后来觉得折线或者柱状图固然直观,但在剪辑的提升方面相对有限, 归到次级需要去了。(就是还不会写但是暂时先学学别的OTL)
通过av号或bv号获取在线弹幕
处理xml数据
EDIT: 修复了一些bug