《用python 玩转数据》项目——B站弹幕数据分析,,1. 背景在视频网站


1. 背景

在视频网站上,一边看视频一边发弹幕已经是网友的习惯。在B站上有很多种类的视频,也聚集了各种爱好的网友。本项目,就是对B站弹幕数据进行分析。选取分析的对象是B站上点播量过1.4亿的一部剧《Re:从零开始的异世界生活》。

2.算法

分两部分:

第一部分:

2.1在《Re:从零开始的异世界生活》的首页面,找到共25集的所有对应播放链接和剧名的格式,获取每一集的播放链接,并保存。

2.2从每一集的播放页面中,通过正则re获取它的cid号,获得cid号后,就可用于获取弹幕文件。由于是动态页面,所以使用了selenium库的PhantomJS()方法。

2.3因为b站需要通过格式为:https://comment.bilibili.com/dmroll,{time},{cid}的链接获取弹幕历史文件,所以要获取30天内的弹幕历史,就要计算出最近30天的时间戳。最后将组装好的url 保存到history_danmu_url.txt 文件。

2.4当弹幕文件的url准备完成后,就可以通过静态页面的获取方法requests.get(url) 获取弹幕页面。所有的弹幕历史格式:<dp="533.67199707031,1,25,41194,1498943949,0,7edeebe9,3511616609">刀还是没有枪快</d>

2.5p这个字段里面的内容:(资料来自百度搜索)

0,1,25,16777215,1312863760,0,eff85771,42759017中几个逗号分割的数据

第一个参数是弹幕出现的时间以秒数为单位。

第二个参数是弹幕的模式1..3 滚动弹幕 4底端弹幕 5顶端弹幕 6.逆向弹幕 7精准定位 8高级弹幕

第三个参数是字号, 12非常小,16特小,18小,25中,36大,45很大,64特别大

第四个参数是字体的颜色以HTML颜色的十进制为准

第五个参数是Unix格式的时间戳。基准时间为 1970-1-1 08:00:00

第六个参数是弹幕池 0普通池 1字幕池 2特殊池【目前特殊池为高级弹幕专用】

第七个参数是发送者的ID,用于“屏蔽此弹幕的发送者”功能

第八个参数是弹幕在弹幕数据库中rowID 用于“历史弹幕”功能。

2.6逐个弹幕历史文件爬取,将内容一集的30天的弹幕历史,以[‘dtTime‘, ‘danmu_model‘, ‘font‘, ‘rgb‘, ‘stamp‘,‘danmu_chi‘, ‘userID‘, ‘rowID‘, ‘message‘, ‘episode‘] 的格式保存到d1.csv,d2.csv,d3.csv,……的文件中

2.7除了将30天的所有历史弹幕保存在一起外,爬虫程序,还将每一集视频,在B站的最新弹幕历史文件的内容单独保存在文件名格式为now1.csv,now2.csv,……的文件中。获取最新弹幕历史文件的链接格式是:https://comment.bilibili.com/{cid}.xml,它们已经被保存在”comment.txt”文件。


第二部分:

2.8保存了所有的数据后,对数据进行处理:先读取.csv文件,然后进行可视化分析:

2.8.1 每集弹幕总量的变化图

逐个读入弹幕历史文件:d1.csv,d2.csv,……经过去重后,统计出每集的弹幕总量:

data = pd.read_csv(item.strip(),encoding=‘gbk‘)

统计每一集,近30天,弹幕总量,保存在字典:{1:323233, 2:212121, .......}

episode_comment_dic[data.loc[1,‘episode‘]] = every_episode_comment(data)

2.8.2 发弹幕总量top5用户

a.统计每一集,30天内的弹幕数量,依据弹幕数量,把用户排序,每一集排序后的结果是一个DataFrame:

结果的大致结构:user_sort_dic = {1: DataFrame1, 2:DataFrame2, ......,25: DataFrame25}。

user_sort_dic[data.loc[1, ‘episode‘]] =every_episode_usersort(data)

b.把经过排序统计处理后的所有DataFrame 进行concat。然后就可以统计所有用户在30天内,对25集视频发送弹幕的数量。最后对用户排序。最终结果:d4_alldanmu_sort 是一个DataFrame 变量,将用户按弹幕数,降序排列。

d3_all_user = (pd.concat([item for k, itemin user_sort_dic.items()]))

d3_all_user[‘userID‘] = d3_all_user.index

aSer =d3_all_user.groupby(‘userID‘).episode.sum()

d4_alldanmu_sort =pd.DataFrame(aSer).sort_values(by=‘episode‘, ascending=False)

2.8.3 用户发弹幕长度分布

统计发送弹幕的字符串长度。

danmu_length_dic[data.loc[1,‘episode‘]] = static_danmu_length(data)

2.8.4 用户发弹幕数量分布

统计一集,用户发送弹幕数量的百分比分布图。

d_tmp = user_sort_dic[i] #统计用户排名时已经有数据了

every_episode_danmu_pie(d_tmp, i)

2.8.5 每集弹幕密度变化图

弹幕都有一个时间参数,代表了在视频的什么时间发了弹幕。可以统计出在相同时间参数发出的弹幕量,然后再画出“时间--弹幕”折线图,就可以看到弹幕量的变化了。

2.8.6 每集热词画出词云

对每一集的弹幕文本进行分析。先用jieba词库进行分词,然后逐行对弹幕进行词频统计,最后用WordCloud 画出词云图。


3.安装

使用的是anaconda的环境。所以直接在anaconda里进行安装:

pip install -ihttps://pypi.tuna.tsinghua.edu.cn/simple jieba

conda –c https://conda.anaconda.org/conda-forge wordecloud#词云只支持到python3.4



4.统计结果

4. 1 每集弹幕总量变化

技术分享

4.2 发送弹幕总量top5 用户

技术分享

4.3 用户发送弹幕长度分布

技术分享

4.4 用户发送弹幕数量分布

技术分享


4.5 每集弹幕密度分布图

技术分享

4.6 每集的词云

技术分享



5.结果分析

通过以上的图表数据可以看出用户的弹幕的一些行为。

5.1 25集剧里,第一集和最后一集,弹幕数量是最多的,这和其他剧的开头和结尾两部的一样。中间的18集也获得了很多的弹幕数,原来是在那集里剧情出现了很大的变化,吸引了大家的讨论。

5.2 统计了25部剧,用户ID为356ed98的用户共发送了580多条弹幕,算是超级粉丝了。

5.3 通过弹幕数量和长度的分布,可以看出:

绝大部分用户只会发送2条以内的弹幕。

参与弹幕讨论的用户以10个字符以内的短语为主。

5.4 统计每一集的弹幕密度,可以通过用户观看时的讨论,大致确定视频的精彩点。

5.5 通过词云图,可以大致看出用户的分布。例如第一集里“重温”作为热词显示里。说明现在有很多用户是会多次观看这部剧的。


6. 参考代码

6.1 爬虫部分:

#-*-coding:utf-8-*-"""CreatedonMonJul1016:34:272017@author:ahchprfilename:re_zero_bili.py"""importrequests,csv,re,timefrombs4importBeautifulSoupasBSfromseleniumimportwebdriverimportdatetimefrommultiprocessingimportPoolimportsys#Re:从零开始的异世界生活的总剧情首页first_url=‘https://bangumi.bilibili.com/anime/3461‘headers={‘User-Agent‘:‘Mozilla/5.0(WindowsNT10.0;WOW64)AppleWebKit/537.36(KHTML,likeGecko)‘}one_url=‘https://bangumi.bilibili.com/anime/3461/play#86298‘#history_danmu_url=‘https://comment.bilibili.com/dmroll,time,cid‘#now_danmu_url=‘https://comment.bilibili.com/{}.xml‘.format(danmu_id)defget_danmu_id(url):MyDriver=webdriver.PhantomJS()MyDriver.get(url)time.sleep(3)danmu_id=re.findall(r‘cid=(\d+)&‘,MyDriver.page_source)[0]return(danmu_id)defsele_get_first(url):MyDriver=webdriver.PhantomJS()MyDriver.get(url)time.sleep(5)response=MyDriver.page_source.encode(‘utf-8‘)page=response.decode(‘utf-8‘)return(page)defsele_get_re_list(page):pattern=re.compile(‘<a.*?href="(.*?)"title="(.*?)"target.*?class="v1-complete-text"><divclass=‘)abstract=re.findall(pattern,page)return(abstract)defrequest_get_comment(url):headers={‘User-Agent‘:‘Mozilla/5.0(WindowsNT10.0;WOW64)AppleWebKit/537.36(KHTML,likeGecko)‘}episode=url.split("")[0]url=url.split("")[1].strip()response=requests.get(url=url,headers=headers)soup=BS(response.text,‘lxml‘)result=soup.find_all(‘d‘)iflen(result)==0:return(result)all_list=[]foriteminresult:#danmu_list.append(item.get(‘p‘).split(",").append(item.string))danmu_list=item.get(‘p‘).split(",")danmu_list.append(item.string)#danmu_list[0]=sec_to_str(danmu_list[0])#danmu_list[4]=time.ctime(eval(danmu_list[4]))danmu_list.append(episode)#print(danmu_list)all_list.append(danmu_list)return(all_list)"""将秒转换成固定格式"hh:mm:ss""""defsec_to_str(seconds):seconds=eval(seconds)m,s=divmod(seconds,60)h,m=divmod(m,60)dtEventTime="%02d:%02d:%02d"%(h,m,s)return(dtEventTime)"""计算最近30天的每天的时间戳,并返回,用于获取历史弹幕"""deftime_to_stamp():today=datetime.date.today()end_day=datetime.datetime(today.year,today.month,today.day)start_day=end_day-datetime.timedelta(30)gap_day_sum=30stamp_list=[]foriinrange(1,gap_day_sum):tmp=start_day+datetime.timedelta(i)stamp_list.append(int(time.mktime(tmp.timetuple())))return(stamp_list)defcsv_write(tablelist,num):tableheader=[‘dtTime‘,‘danmu_model‘,‘font‘,‘rgb‘,‘stamp‘,‘danmu_chi‘,‘userID‘,‘rowID‘,‘message‘,‘episode‘]file_name="now{}.csv".format(num)print(file_name)withopen(file_name,‘w‘,newline=‘‘,errors=‘ignore‘)asfd:writer=csv.writer(fd)writer.writerow(tableheader)forrowintablelist:writer.writerow(row)if__name__=="__main__":sys.setrecursionlimit(1000000)"""爬取首页,获取共25话《re:从零开始的异世界生活》的播放连接"""page=sele_get_first(first_url)re_list=sele_get_re_list(page)#print(len(re_list))"""以字典的形式保存例如:{‘1‘:[‘初始的终结与结束的开始‘,‘https://bangumi.bilibili.com/anime/3461/play#85754‘],...}"""re_dict={}foriteminre_list:re_dict[item[1].split("")[0]]=[item[1].split("")[1],item[0]]#print(re_dict)"""获取每一话的播放连接,保存成列表"""re_url_list=[]foriinrange(1,len(re_dict)+1):re_url_list.append(re_dict[str(i)][1])"""利用进程池,获取每一话的弹幕文件连接,"""re_danmu_id_list=[]pool=Pool(14)re_danmu_id_list=pool.map(get_danmu_id,re_url_list)pool.close()pool.join()re_danmu_id_dict={}forn,pinenumerate(re_danmu_id_list):re_danmu_id_dict[str(n+1)]=p"""将25话剧集的各自的最新的弹幕文件连接保存到一个文档comment.txt,按照剧集的顺序保存"""withopen(‘comment.txt‘,‘w‘)asfd:foriinrange(len(re_danmu_id_list)):fd.write(‘{}https://comment.bilibili.com/{}.xml\n‘.format(i+1,re_danmu_id_list[i]))history_danmu_url_list=[]stamp_list=time_to_stamp()foriinrange(1,len(re_danmu_id_list)+1):forstampinstamp_list:history_danmu_url=‘{}https://comment.bilibili.com/dmroll,{},{}‘.format(i,stamp,re_danmu_id_dict[str(i)])history_danmu_url_list.append(history_danmu_url)history_danmu_url_list.append(‘{}https://comment.bilibili.com/{}.xml‘.format(i,re_danmu_id_dict[str(i)]))withopen(‘history_danmu_url.txt‘,‘w‘)asfd:forlineinhistory_danmu_url_list:fd.write("{}\n".format(line))all_list=[]‘‘‘把每一集的弹幕文件链接,按照集数,整理到一个字典,‘‘‘url_dict={}withopen("history_danmu_url.txt",‘r‘)asfd:url_whole=fd.readlines()print(len(url_whole))foriinrange(1,len(re_danmu_id_list)+1):url_dict[str(i)]=[lineforlineinurl_wholeifint(line.split("")[0])==i]print(len(url_dict))‘‘‘按照集数,取出弹幕链接,进行爬虫,获取弹幕记录,并保存到.csv文件‘‘‘foriinrange(1,len(url_dict)+1):n=0tmp_to_get_url=url_dict[(str(i))]file_name="d{}.csv".format(i)tableheader=[‘dtTime‘,‘danmu_model‘,‘font‘,‘rgb‘,‘stamp‘,‘danmu_chi‘,‘userID‘,‘rowID‘,‘message‘,‘episode‘]withopen(file_name,‘a‘,newline=‘‘,errors=‘ignore‘)asfd:writer=csv.writer(fd)writer.writerow(tableheader)forurlintmp_to_get_url:all_list=request_get_comment(url)ifall_list:forrowinall_list:writer.writerow(row)print("\n\n\n\n\n")n=n+1print(n)del(all_list)deltmp_to_get_url"""获取保存最新历史弹幕文件"""now_danmu_all={}withopen(‘comment.txt‘,‘r‘)asfd:forurlinfd:now_danmu_all[int(url.strip().split("")[0])]=request_get_comment(url)fornum,datainnow_danmu_all.items():csv_write(data,num)


6.2 数据统计可视化分析部分:

#-*-coding:utf-8-*-"""CreatedonWedJul1208:43:042017@author:ahchprfilename:static_danmu_comment.py"""importpandasaspdimportmatplotlib.pyplotaspltimportosfromscipy.miscimportimreadfromwordcloudimportWordCloudimportjiebaimportjieba.possegaspsegimportmathdefdanmu_compress_plot(data,num):plt.cla()plt.xlabel(u"视频时间",fontproperties=‘SimHei‘)plt.ylabel(u"弹幕量",fontproperties=‘SimHei‘)plt.title(u‘第{}集_时间轴弹幕变化‘.format(num),fontproperties=‘SimHei‘)keys=[itemforitemindata.index]values=[itemforitemindata.dtTime]"""弹幕密度折线图"""plt.plot(keys,values)plt.show()defdanmu_compress(data):df=data.drop_duplicates()dd=df.copy()#round_dic={}#向下取“整秒数”#round_dic[‘dtTime_new‘]=[math.floor(item)foritemindd.dtTime]dd[‘dtTime_new‘]=[math.floor(item)foritemindd.dtTime]#dd.sort_values(by=‘dtTime_new‘,inplace=True)#print(dd.iloc[1:200,[6,0,10]])dc=dd.groupby(‘dtTime_new‘).count()result=dc.sort_index()return(result)‘‘‘result大概的结构:只写出了一列参考,其中dtTime就是在0秒开始时的弹幕数量dtTimedtTime_new075137234‘‘‘defextract_words(data,num):df=data.drop_duplicates()dd=df.copy()message_list=[str(item)foritemindd.message]stop_words=set(line.strip()forlineinopen(‘stopwords.txt‘,encoding=‘utf-8‘))newslist=[]forsubjectinmessage_list:ifsubject.isspace():continue#segmentwordslinebylineword_list=pseg.cut(subject)forword,flaginword_list:ifnotwordinstop_wordsandflag==‘n‘:newslist.append(word)d=os.path.dirname(__file__)mask_image=imread(os.path.join(d,"qiaodan.png"))content=‘‘.join(newslist)wordcloud=WordCloud(font_path=‘simhei.ttf‘,background_color="grey",mask=mask_image,max_words=40).generate(content)#Displaythegeneratedimage:file_name=u"第{}集_热词云.jpg".format(num)plt.imshow(wordcloud)plt.axis("off")plt.title(file_name,fontproperties=‘SimHei‘)wordcloud.to_file(file_name)plt.show()plt.cla()plt.close()defstatic_danmu_length(data):df=data.drop_duplicates()dd=df.copy()dd[‘message_len‘]=[len(str(item))foritemindf.message]#统计每条弹幕的长度d1=dd.loc[:,[‘userID‘,‘message‘,‘message_len‘,‘episode‘]]dr=d1.copy()return(dr)defevery_episode_usersort(data):df=data.drop_duplicates()dd=df.groupby("userID").count()user_sort=dd.sort_values(by=‘episode‘,ascending=False).loc[:,[‘episode‘]]return(user_sort)defevery_episode_user(data):‘‘‘30天内共有多少用户发弹幕‘‘‘df=data.drop_duplicates()dd=df.groupby("userID").count()user_sum=len(dd)return(user_sum)defevery_episode_comment(data):‘‘‘30天内有多少弹幕发出‘‘‘df=data.drop_duplicates()danmu_sum=len(df)return(danmu_sum)deftop_user_danmu(data):plt.cla()ing=range(5)x=data.head(5).episode.indexy=data.head(5).episode.valuesplt.xticks(ing,x,rotation=30)plt.xlabel(u"用户ID",fontproperties=‘SimHei‘)plt.ylabel(u"发弹幕数量",fontproperties=‘SimHei‘)plt.title(u"发弹幕数top5用户",fontproperties=‘SimHei‘)plt.bar(ing,y)plt.show()defevery_episode_comment_change(episode_comment_dic):plt.cla()plt.xlabel(u"剧集",fontproperties=‘SimHei‘)plt.ylabel(u"弹幕量",fontproperties=‘SimHei‘)plt.title(u‘每集弹幕总量变化‘,fontproperties=‘SimHei‘)keys=range(1,len(episode_comment_dic)+1)values=[]foriinkeys:values.append(episode_comment_dic[i])"""每一集弹幕总量的折线变化图"""plt.plot(keys,values)plt.show()defevery_episode_danmu_pie(d_tmp,num):a1=float(len(d_tmp[(d_tmp.episode>=1)&(d_tmp.episode<=2)]))a2=len(d_tmp[(d_tmp.episode>=3)&(d_tmp.episode<=8)])a3=len(d_tmp[(d_tmp.episode>=9)&(d_tmp.episode<=20)])a4=len(d_tmp[(d_tmp.episode>=21)])s=a1+a2+a3+a4s=float(s)li=[a1,a2,a3,a4]xp=[]foriinli:i=float(i)ifi<=0:t=0xp.append(t)else:t=(i/s*100)xp.append(t)plt.rcParams[‘font.sans-serif‘]=[‘SimHei‘]#用来正常显示中文标签plt.rcParams[‘axes.unicode_minus‘]=False#用来正常显示负号plt.figure(figsize=(6,9))labels=[u‘1-2条‘,u‘2-8条‘,u‘9-20条‘,u‘21条以上‘]sizes=xpcolors=[‘red‘,‘yellow‘,‘gray‘,‘lightskyblue‘]explodes=[0,0,0,0.6]plt.axis(‘equal‘)plt.title(u‘第{}集_用户发送弹幕数量百分比分布图‘.format(num))plt.pie(sizes,labels=labels,explode=explodes,colors=colors,labeldistance=0.5,autopct=‘%2.2f%%‘,startangle=90,pctdistance=0.8)plt.show()plt.close()defdanmu_length_pie(d_tmp,num):a1=float(len(d_tmp[(d_tmp.message_len>=1)&(d_tmp.message_len<=4)]))a2=float(len(d_tmp[(d_tmp.message_len>=5)&(d_tmp.message_len<=10)]))a3=float(len(d_tmp[(d_tmp.message_len>=11)&(d_tmp.message_len<=15)]))a4=float(len(d_tmp[(d_tmp.message_len>=16)]))s=a1+a2+a3+a4s=float(s)li=[a1,a2,a3,a4]xp=[]foriinli:i=float(i)ifi<=0:t=0xp.append(t)else:t=(i/s*100)xp.append(t)plt.rcParams[‘font.sans-serif‘]=[‘SimHei‘]#用来正常显示中文标签plt.rcParams[‘axes.unicode_minus‘]=False#用来正常显示负号plt.figure(figsize=(6,9))labels=[u‘1-4个‘,u‘5-10个‘,u‘11-15个‘,u‘16个以上‘]sizes=xpcolors=[‘red‘,‘yellow‘,‘gray‘,‘lightskyblue‘]explodes=[0,0,0,0.09]plt.axis(‘equal‘)plt.title(u‘第{}集_用户发送弹幕长度百分比分布图‘.format(num))plt.pie(sizes,labels=labels,colors=colors,explode=explodes,labeldistance=0.5,autopct=‘%2.2f%%‘,startangle=90,pctdistance=0.8)plt.show()plt.close()#秒转换成时间defsec_to_str(seconds):seconds=eval(seconds)m,s=divmod(seconds,60)h,m=divmod(m,60)length_time="%02d:%02d:%02d"%(h,m,s)return(length_time)if__name__=="__main__":path=os.getcwd()path_list=[]foriinrange(1,26):path_list.append(path+"\\d{}.csv".format(i))episode_comment_dic={}user_sum_dic={}user_sort_dic={}danmu_length_dic={}reyun_data_dic={}foriteminpath_list:‘‘‘读取csv数据源文件,每一集的近30天弹幕都保存在一个csv文件‘‘‘data=pd.read_csv(item.strip(),encoding=‘gbk‘)‘‘‘统计每一集,近30天,弹幕总量,保存在字典:{1:323233,2:212121,.......}‘‘‘episode_comment_dic[data.loc[1,‘episode‘]]=every_episode_comment(data)‘‘‘统计每一集,近30天,共有多少用户发了弹幕,保存在字典:{1:3737,2:34234,......}‘‘‘user_sum_dic[data.loc[1,‘episode‘]]=every_episode_user(data)‘‘‘统计每一集,30天内的弹幕数量,依据弹幕数量,把用户排序,每一集排序后的结果是一个DataFrame,user_sort_dic={1:DataFrame1,2:DataFrame2,......,25:DataFrame25}‘‘‘user_sort_dic[data.loc[1,‘episode‘]]=every_episode_usersort(data)‘‘‘统计发送弹幕的字符串长度‘‘‘danmu_length_dic[data.loc[1,‘episode‘]]=static_danmu_length(data)‘‘‘统计每一集的分词,热词,词云‘‘‘reyun_data_dic[data.loc[1,‘episode‘]]=data.copy()‘‘‘统计每一集,近30天的弹幕量,视频里的每一秒的弹幕数量,弹幕密度‘‘‘#danmu_compress_dic[data.loc[1,‘episode‘]]=danmu_compress(data)deldataprint(episode_comment_dic)print(user_sum_dic)‘‘‘把经过排序统计处理后的所有DataFrame进行concat。然后就可以统计所有用户在30天内,对25集视频发送弹幕的数量。最后对用户排序。最终结果:d4_alldanmu_sort是一个DataFrame变量,将用户按弹幕数,降序排列。‘‘‘d3_all_user=(pd.concat([itemfork,iteminuser_sort_dic.items()]))d3_all_user[‘userID‘]=d3_all_user.indexaSer=d3_all_user.groupby(‘userID‘).episode.sum()d4_alldanmu_sort=pd.DataFrame(aSer).sort_values(by=‘episode‘,ascending=False)‘‘‘绘制折线图:25集视频,近30天每集弹幕总数‘‘‘every_episode_comment_change(episode_comment_dic)‘‘‘柱状图:25集视频,近30天,所有用户中,发弹幕数量最多的5个用户‘‘‘top_user_danmu(d4_alldanmu_sort)‘‘‘统计一集,用户发送弹幕数量的百分比分布图‘‘‘foriinrange(1,len(user_sort_dic)+1):d_tmp=user_sort_dic[i]every_episode_danmu_pie(d_tmp,i)deld_tmp‘‘‘统计用户发送弹幕的长度分布百分比‘‘‘foriinrange(1,len(user_sort_dic)+1):d_tmp=danmu_length_dic[i]danmu_length_pie(d_tmp,i)deld_tmp‘‘‘分析弹幕密度,使用当前最新的历史弹幕文件分析‘‘‘now_danmu_list=[]foriinrange(1,26):now_danmu_list.append(path+"\\now{}.csv".format(i))danmu_compress_dic={}foriteminnow_danmu_list:data=pd.read_csv(item.strip(),encoding=‘gbk‘)danmu_compress_dic[data.loc[1,‘episode‘]]=danmu_compress(data)fornum,dataindanmu_compress_dic.items():danmu_compress_plot(data,num)"""绘制热词云图,热词的运行时间太久了,先关闭"""fornum,datainreyun_data_dic.items():extract_words(data,num)


本文出自 “dayAndNight” 博客,请务必保留此出处http://hellocjq.blog.51cto.com/11336969/1947252

《用python 玩转数据》项目——B站弹幕数据分析

评论关闭