2021年7月19日 bilibili互动视频分析展示

该博客详细介绍了如何使用Python分析B站互动视频的逻辑,包括获取选项信息、判断视频结束、保存视频逻辑及展示分析结果。通过网络请求获取edge_id,递归遍历所有可能的剧情路径,并利用Graphviz生成展示图,展示了从BV号获取所需信息的完整流程。
摘要由CSDN通过智能技术生成

仅供学习使用,请勿用于商业或违法用途,否则产生的一切后果由使用者自负。

bilibili互动视频分析展示

本文示范的视频为【喜羊羊与灰太狼互动游戏】重启营救奇猫国之路,奇怪的剧情增加了!
Python所需的库:

  • requets(可通过pip安装)
  • json(可通过pip安装)
  • graphviz(通过pip安装后还需下载安装graphvizr软件并配置环境变量)

互动视频分析

分析互动视频逻辑获取方式

按F12打开开发者工具,然后刷新视频页面,在开发者工具中选择网络(Network),逐个检查请求和响应,发现其中一个响应中包含了选项信息
包含选项信息的会话
展开后可看到详细的选项信息
详细内容
在视频中点击一个选项后再次检查网络选项卡中,可再次看到类似名称的请求
这次更多了
观察请求参数可以发现有这些东西
请求参数
由此可知edge_id是视频的标识,每个选项都指向一个新的edge_id,因此可以确定使用递归来遍历每种选择路径,同时要考虑到可能会有的重复情况(即出现重复的edge_id)。
当视频结束时,最后一个edgeinfo的请求是这样的。
视频结束时的edgeinfo请求
与之前的比较可以观察到edges里没有questions了,所以可以以此作为视频结束的判断。

保存互动视频逻辑

互动视频逻辑的保存这里使用了字典,键为当前的edge_id,值为【当前视频片段的标题、(选项所指向的edge_id、选项名称)】。
代码如下:

zidian={}
def fenzhi(id=default_edge_id):
    res=requests.get(url3%id) # 这里不需要heades也能正常响应
    data=json.loads(res.text)['data']
    if id=="":
        id=data['edge_id']
    title=data['title']
    global zidian
    if 'questions' not in data['edges'].keys():
        zidian[id]=[title,'结束']
        print('结束')
    else:
        ch=data['edges']['questions'][0]['choices']
        if id not in zidian.keys():
            zidian[id]=[title]
            for i in ch:
                print(i['id'],i['option'])
                zidian[id].append((i['id'],i['option']))
                fenzhi(i['id'])
        else:
            print('重复/循环')

从BV号获取edgeinfo的网址

将edgeinfo的请求参数一个个去除并尝试请求。
edgeinfo的请求参数
之后发现bvid和graph_version是必要的,edge_id如果没有就会返回开始界面的视频片段信息。
因此查看edgeinfo之前的请求,找到能获取graph_version的请求。
v2的请求
通过v2这个请求可以获取到graph_version,同理,再次观察v2的请求参数,并逐个去除后测试,发现aid不是必要的。
再次寻找获取cid的请求。
pagelist的请求
发现pagelist这个请求可以直接通过BV号获取cid,至此,目标已完成。
BV号 -> cid -> graph_version -> edge_info开始递归

def main(bvid):
    url1="https://api.bilibili.com/x/player/pagelist?bvid={}&jsonp=jsonp".format(bvid)
    url2="https://api.bilibili.com/x/player/v2?cid=%s&bvid={}".format(bvid)
    cid=json.loads(requests.get(url1).text)["data"][0]["cid"]
    graph_version=json.loads(requests.get(url2%cid).text)["data"]["interaction"]["graph_version"]
    global url3
    url3=url3.format(bvid,graph_version)
    fenzhi()
    with open(filename,'w',encoding='utf-8') as f:
        f.write(str(zidian))

分析部分完整代码

import requests,json

###################

bvid=""
graph_version=""
buvid="" # buvid是cookies里的,不是必要的
default_edge_id=""
filename=r'互动视频.txt'
url3="https://api.bilibili.com/x/stein/edgeinfo_v2?bvid={}&edge_id=%s&graph_version={}&platform=pc&portal=0&screen=0&choices="

###################

zidian={}
def fenzhi(id=default_edge_id):
    res=requests.get(url3%id) # 这里不需要heades也能正常响应
    data=json.loads(res.text)['data']
    if id=="":
        id=data['edge_id']
    title=data['title']
    global zidian
    if 'questions' not in data['edges'].keys():
        zidian[id]=[title,'结束']
        print('结束')
    else:
        ch=data['edges']['questions'][0]['choices']
        if id not in zidian.keys():
            zidian[id]=[title]
            for i in ch:
                print(i['id'],i['option'])
                zidian[id].append((i['id'],i['option']))
                fenzhi(i['id'])
        else:
            print('重复/循环')

def main(bvid):
    url1="https://api.bilibili.com/x/player/pagelist?bvid={}&jsonp=jsonp".format(bvid)
    url2="https://api.bilibili.com/x/player/v2?cid=%s&bvid={}".format(bvid)
    cid=json.loads(requests.get(url1).text)["data"][0]["cid"]
    graph_version=json.loads(requests.get(url2%cid).text)["data"]["interaction"]["graph_version"]
    global url3
    url3=url3.format(bvid,graph_version)
    fenzhi()
    with open(filename,'w',encoding='utf-8') as f:
        f.write(str(zidian))

if __name__=='__main__':
    main(bvid)

互动视频展示

由于每个视频片段有各自的标题,每个选项也有名称,因此都应出现在最终的图中,这里将视频片段的标题用椭圆形的框,选项名称用圆角矩形的框,刚开始的视频片段标题用蓝色的框,结束用红色的框。
之后根据字典中保存的内容,遍历并添加节点和边。

展示部分完整代码

from graphviz import Digraph
import json

############

name="互动视频展示"
filename=r'互动视频.txt'

############

def B2Q(uchar): # 有些视频的剧情里的描述带有英文冒号等,保存成gv文件没问题,但是之后解析生成图片会出现问题。
    """单个字符 半角转全角
    参考:https://blog.csdn.net/huanghaocs/article/details/90384163"""
    inside_code = ord(uchar)
    if inside_code < 0x0020 or inside_code > 0x7e: # 不是半角字符就返回原来的字符
        return uchar 
    if inside_code == 0x0020: # 除了空格其他的全角半角的公式为: 半角 = 全角 - 0xfee0
        inside_code = 0x3000
    else:
        inside_code += 0xfee0
    return chr(inside_code)


def main():
    g=Digraph(name,'comment',None,None,'png',None,"UTF-8",
               {'rankdir':'TB'},
               {'color':'black','fontcolor':'black','fontname':'Sans','fontsize':'12','style':'rounded','shape':'box'},
               {'color':'#999999','fontcolor':'#888888','fontsize':'10','fontname':'WeiRuanYaHei'},None,False)

    with open(filename,encoding='utf-8') as f:
        zidian=eval(f.read())

    start_name=list(zidian.values())[0][0]
    full_angle="".join(map(B2Q,start_name)) # 把start_name中的半角转为全角防止解析错误
    g.node(full_angle,start_name,shape='ellipse',fontsize='30',color='blue')

    for k,v in zidian.items():
        full_angle="".join(map(B2Q,v[0])) # 把v[0]中的半角转为全角防止解析错误
        g.node(full_angle,v[0],shape='ellipse') # 新建剧情
        for i in v[1:]:
            if i=='结束': # 剧情指向结束
                g.node(str(k)+'结束','结束',shape='circle',color='red')
                g.edge(full_angle,str(k)+'结束')
            else: # 剧情指向选项
                g.node(str(i[0]),i[1],shape='box') # 新建选项
                g.edge(full_angle,str(i[0]))

    start_id=list(zidian.keys())[0]
    for k,v in zidian.items(): # 选项指向剧情
        if k!=start_id:
            full_angle="".join(map(B2Q,v[0])) # 把v[0]中的半角转为全角防止解析错误
            g.edge(str(k),full_angle)
    g.view()

if __name__=='__main__':
    main()

结合分析部分和展示部分

之后只需要封装和调用就行了。

import 互动视频分析
import 互动视频展示
import sys

if len(sys.argv) > 1:
    bvid=sys.argv[1]
else:
    bvid=input("输入视频的BV号(带BV):")
互动视频分析.main(bvid)
print("***已分析完,即将生成图片。")
互动视频展示.main()

完整输入和输出展示

输入视频的BV号(带BV):BV1BK4y1e7cR
11914189 A 决定前往奇猫国!
11914196 A 让沸羊羊也当一回主角
11914140 A 成功溜走~赶紧赶路吧!
11914191 A 美羊羊忍不住了!
11914163 A 上车
11914182 A 听一下喜羊羊的悄悄话:哔 ——
11914194 A 进入皇宫
11914185 A 灰太狼
11914164 A 88,先走了
11914154 A 失败了~
11914189 A 决定前往奇猫国!
重复/循环
11914190 B 请皓月吃碗面再说~
11914146 A 给公主再来两碗!
结束
11914147 B 带皓月公主去旅行散散心
11914148 A 打开大门
11914159 A 让沸羊羊当一回主角
11914140 A 成功溜走~赶紧赶路吧!
重复/循环
11914160 B 这次看懒羊羊的吧
11914204 A 先尝一口什么味道嘛
11914206 A 被大老鼠送回去青青草原啦!
11914189 A 决定前往奇猫国!
重复/循环
11914190 B 请皓月吃碗面再说~
重复/循环
11914205 B 莫非是拍在自己头上?
11914145 A 成功打败“大老鼠”,赶紧赶路吧!
11914191 A 美羊羊忍不住了!
重复/循环
11914192 B 算了,还是先上车吧!
11914141 A 听一下喜羊羊的悄悄话..
11914194 A 进入皇宫
重复/循环
11914142 B 喜羊羊突然狂叫!
11914167 A 向左跑
11914202 A 向左跑
11914185 A 灰太狼
重复/循环
11914186 B 喜羊羊
11914199 A 觉得喜羊羊是卧底?
11914201 A 回去增加情感
11914189 A 决定前往奇猫国!
重复/循环
11914190 B 请皓月吃碗面再说~
重复/循环
11914200 B 相信喜羊羊不是卧底?
11914156 A 明日伸出双手,准备...
11914174 A 啊啊,好强!!
结束
11914157 B 这是什么招?迷惑!
结束
11914187 C 明日
11914149 A 明日赶紧伸出双手
11914195 A 莫得你不听~
结束
11914150 B 这是什么招?一脸问号?
结束
11914188 D 沸羊羊
11914175 A 捉住福来!
11914166 A 被遗忘的福来还在等车呢...那卧底是谁?~
11914185 A 灰太狼
重复/循环
11914186 B 喜羊羊
重复/循环
11914187 C 明日
重复/循环
11914188 D 沸羊羊
重复/循环
11914168 B 向右跑
11914151 A 向右跑
11914185 A 灰太狼
重复/循环
11914186 B 喜羊羊
重复/循环
11914187 C 明日
重复/循环
11914188 D 沸羊羊
重复/循环
11914143 C 别问我, 问灰太狼嘛!
11914144 A 无情拖走...
11914185 A 灰太狼
重复/循环
11914186 B 喜羊羊
重复/循环
11914187 C 明日
重复/循环
11914188 D 沸羊羊
重复/循环
11914193 C 万不得已,别这样嘛,美羊羊~
11914203 A 和美羊羊一起飞的去~
11914176 A 沸羊羊
11914175 A 捉住福来!
重复/循环
11914177 B 我不信!
11914149 A 明日赶紧伸出双手
重复/循环
11914150 B 这是什么招?一脸问号?
结束
11914178 C 喜羊羊
11914199 A 觉得喜羊羊是卧底?
重复/循环
11914200 B 相信喜羊羊不是卧底?
重复/循环
11914179 D 灰太狼
11914164 A 88,先走了
重复/循环
11914165 B 我留下来!
11914155 A 让我们一起对抗黑暗吧!
11914149 A 明日赶紧伸出双手
重复/循环
11914150 B 这是什么招?一脸问号?
结束
11914161 C 还是喜羊羊靠谱点
11914169 A 掏出那支神奇的...
11914152 A 围观一下
11914180 A 快回去青青草原,准备好再来吧!
11914158 A 回去青青草原
11914189 A 决定前往奇猫国!
重复/循环
11914190 B 请皓月吃碗面再说~
重复/循环
11914181 B 我不信,我要再看一次!
11914152 A 围观一下
重复/循环
11914153 B 受不了了
11914172 A 哇!村长太厉害了吧!我们赶紧溜喽!
11914191 A 美羊羊忍不住了!
重复/循环
11914192 B 算了,还是先上车吧!
重复/循环
11914193 C 万不得已,别这样嘛,美羊羊~
重复/循环
11914153 B 受不了了
重复/循环
11914170 B 既然躲不过了,那就...
11914173 A 成功打败“大老鼠”,抓紧时间赶路吧!
11914191 A 美羊羊忍不住了!
重复/循环
11914192 B 算了,还是先上车吧!
重复/循环
11914193 C 万不得已,别这样嘛,美羊羊~
重复/循环
11914171 C 请求外援!
11914162 A 哇!村长太厉害了吧!我们赶紧溜喽!
11914191 A 美羊羊忍不住了!
重复/循环
11914192 B 算了,还是先上车吧!
重复/循环
11914193 C 万不得已,别这样嘛,美羊羊~
重复/循环
11914165 B 我留下来!
重复/循环
11914186 B 喜羊羊
重复/循环
11914187 C 明日
重复/循环
11914188 D 沸羊羊
重复/循环
11914183 B 别问我,问一下另一位男主角嘛~
11914144 A 无情拖走...
重复/循环
11914184 C 喜羊羊突然狂叫!
11914167 A 向左跑
重复/循环
11914168 B 向右跑
重复/循环
11914192 B 算了,还是先上车吧!
重复/循环
11914193 C 万不得已,别这样嘛,美羊羊~
重复/循环
11914197 B 这次看懒羊羊的吧!
11914204 A 先尝一口什么味道嘛
重复/循环
11914205 B 莫非是拍在自己头上?
重复/循环
11914198 C 还是喜羊羊来靠谱点
11914169 A 掏出那支神奇的...
重复/循环
11914170 B 既然躲不过了,那就...
重复/循环
11914171 C 请求外援!
重复/循环
11914190 B 请皓月吃碗面再说~
重复/循环
***已分析完,即将生成图片。
>>> 

互动视频展示.gv.png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值