Python 爬取 B 站數據分析,宋智孝李光洙誰最受中國粉絲喜愛
作者 | 左伊雅
責編 | 胡巍巍
《Running Man》是韓國SBS電視台在《星期天真好》單元推出的戶外競技真人秀節目。
節目致力於打造一個不同於Real variety的新型態娛樂節目。每期有不同的主題,由不同的嘉賓參演,分為不同的隊伍進行比賽,通過完成各種遊戲任務,最後獲勝一方將獲得稱號或獎品。
成員組成包括原六位成員劉在石、池石鎮、金鐘國、HAHA(河東勛)、宋智孝、李光洙 ,以及兩位新成員全昭旻、梁世燦。
抓取數據
自從限韓令發布後,Running man在除B站以外的各大視頻網站均下架,所以本文從B站出發,抓取相關視頻的所有評論。
由於相關視頻非常多,本文選擇了最具代表性,點擊量觀看次數最多的視頻。
進入這個頁面後開始抓包(https://www.bilibili.com/video/av18089528?from=search&seid=16848360519725142300)。
不斷點擊下一頁,可以發現reply?callback=這個文件一直在出現。
打開其中一個文件以後可以看到每一面的評論都在裡面;只需構建出類似的URL就可以把所有的評論都爬下來啦。
分析一下這個URL:
https://api.bilibili.com/x/v2/replycallback=jQuery17201477141935656543_1541165464647&jsonp=jsonp&pn=368&type=1&oid=18089528&sort=0&_=1541165714862
pn是頁面數,_對應距離1971年1月1日的秒數,直接用time.time就可以獲得,其餘參數保持不變。數據格式是Json,但是B站有點小狡猾啊~
它把所有的Json數據都存在jQuery17201477141935656543_1541165464647這個裡面。
所以提取的時候要處理一下(Talk is cheap,show me the code)。
html=requests.get(url,headers=headers).text
html=json.loads(html.split("(",1))[1][:-1])
最後我們把所有的評論都抓取下來存入Excel中,數據格式是這樣子的:
寫入CSV的時候一定要記得encoding="utf-8",就因為少了這個,數據總會亂碼,因為各種奇葩的原因(點了一下,拉寬了一下,原地保存一下)。
數據清洗
對於B站的各種缺失數據,就直接用0替換;對於詩歌類的評論,它存到CSV時是一句佔一行,而它的其餘信息都會存到最後一行。
所以在處理時,把前面的n-1行打包append到n行的評論中,再把n-1行刪除;對於B站返回的時間(類似於1540882722);用time.strftime("%Y-%m-%d %H:%M:%S,time.localtime())變換成2018/11/12 22:15:15。
數據分析
清理後一共得到7513*11條數據,接下來對數據進行一些分析,數據分析通過Python和R完成。
男女分布
從餅圖可以看出,近六成的人選擇保密個人信息,公開信息顯示女生僅比男生多3%。這個結論是出乎意料的。原來不論男女都很喜歡Running man。
defmale(sex):
att=["男","女","保密"]
val=[]
foriinatt:
val.append(sex.count(i))
pie = Pie("","性別餅圖", title_pos="right", width=1200, height=600)
pie.add("", att, val, label_text_color=None, is_label_show=True, legend_orient="vertical",
is_more_utils=True, legend_pos="left")
pie.render("sexPie.html")
評論周分布
Running man在韓國的更新時間是每周天下午,但是要到周一B站才會有所更新。
因此從評論周分布圖可以看到,星期一的評論數是遠遠大於其他時間的,其次是星期二和星期天,正好在Runnning man 更新前後,對比其他時間段評論數有一定增長。
defana_week(week):
weeks=["星期天","星期一","星期二","星期三","星期四","星期五","星期六"]
output_file("week_bar.html")
count=[]
foriinsorted(set(week)):
ifnotnumpy.isnan(i):
count.append(week.count(i))
source = ColumnDataSource(data=dict(weeks=weeks, counts=count,color=["orange","yellowgreen","pink","darksalmon","lightgreen","paleturquoise","lightsteelblue"]))
p=figure(x_range=weeks, y_range=(,4000), plot_height=250, title="Week Counts",
toolbar_location=None, tools="")
p.vbar(x="weeks", top="counts", color="color",width=0.9, legend="Week", source=source)
p.legend.orientation ="horizontal"
p.legend.location ="top_right"
show(p)
評論時間分布
除了每周評論數,對於評論數的日趨勢也十分好奇,大家一般會在什麼時間段內觀看評論呢?
根據上圖可以看到,在6點以後迎來一個爆炸性增漲,在11點-13點之間達到峰值,其次是在15點-17點之間迎來第二波小高潮。
在晚間,除了20點有一定下降外,評論數都接近500條。而午夜評論數最少,不過還是有不少夜貓子啊。
def ana_hour(hour):
h,k=[],[]
foriinrange(len(hour)):
ifisinstance(hour[i],str):
h.append(hour[i][:2])
foriinsorted(set(h)):
k.append(h.count(i))
print(k)
output_file("hour_line.html")
p = figure(plot_width=400,title="各小時評論數", plot_height=400)
p.line(sorted(set(h)), k, line_width=2)
p.circle(sorted(set(h)), k, fill_color="white", size=8)
show(p)
評論字數與點贊數
對比每條評論的字數與點贊次數,從上圖可以看到,評論的字數越多,獲得贊的概率就越大:100字以上的評論獲得贊的平均次數遠高於100字以下的評論,而那些10個字以內的評論基本沒有獲得贊,所以只要你是認真評論寫出大家的心聲,就能獲得大家的認同。
def com_zan(com,zan):
q,w,e,r,t=[],[],[],[],[]
fori inrange(len(com)):
iflen(com[i])
q.append(zan[i])
if10
w.append(zan[i])
if50
e.append(zan[i])
if100
r.append(zan[i])
a=go.Box(y=q,name="0-10個字")
b=go.Box(y=w,name="10-50個字")
c=go.Box(y=e,name="50-100個字")
d=go.Box(y=r,name="100以上個字")
e=go.Box(y=zan,name="所有評論")
data=[a,b,e,c,d]
layout =go.Layout(legend=dict(font=dict(size=16)),orientation=270)
fig =go.Figure(data=data, layout=layout)
plotly.offline.plot(data)
情感分析
將大家的評論分別進行情感分析,越接近1說明正面情感越強烈;相反越靠近0負面情緒越強。
從上圖可以看到,雖然有近600人的評論是非常負能量,但是絕大多數的人都是1分、0.9分。
在Running man給我們帶來歡樂與感動的同時,大家對Running man是滿滿的寵愛啊。
def snownlp(com):
q=[]
foriincom:
s=SnowNLP(i)
q.append(round(s.sentiments,1))
emotion=[]
count=[]
foriinsorted(set(q)):
emotion.append(str(i))
count.append(q.count(i))
#count=[596,481,559,566,490,617,528,601,581,809,1685]
#emotion=["0.0","0.1","0.2","0.3","0.4","0.5","0.6","0.7","0.8","0.9","1.0"]
output_file("評論情感分析.html")
source = ColumnDataSource(data=dict(emotion=emotion, counts=count))
p = figure(x_range=emotion, y_range=(,2000), plot_height=250, title="評論情感分析",
toolbar_location=None, tools="")
p.vbar(x="emotion", top="counts", width=0.9, source=source)
p.legend.orientation ="horizontal"
show(p)
話題度排行
一直都很好奇在觀眾心中哪個mc的話題度最高,所以做了一個話題度排行。從上圖可以看到haha是最具話題性的mc(這個結果有點出乎意料呢)其次是李光洙和宋智孝。
因為筆者統計的是2018年的Running man ,所以Gary的數據是有點凄慘的。對比兩個新成員,全妹的話題度比世贊高的不是一點點。
defhot(com):
#print(com)
output_file("各成員話題度.html")
jzg=["金鐘國","鍾國","能力者"]
gary=["gary","狗哥"]
haha=["haha","HAHA","哈哈"]
qsm=["全昭敏","全妹","全昭body"]
lsz=["梁世贊","世贊","小不點"]
name=["池石鎮","劉在石","宋智孝","李光洙","金鐘國","gary","haha","全昭敏","梁世贊"]
csz,lzs,szx,lgz,jzg,gary,haha,qsm,lsz=[],[],[],[],[],[],[],[],[]
foriincom:
if"池石鎮"ini or"石鎮"ini or"鼻子"ini:
csz.append(i)
if"劉在石"inior"在石"inior"大神"inior"螞蚱"ini:
lzs.append(i)
if"宋智孝"inior"智孝"inior"懵智"inior"美懵"ini:
szx.append(i)
if"李光洙"inior"光洙"inior"一筐豬"ini:
lgz.append(i)
if"金鐘國"inior"鍾國"inior"能力者"ini:
jzg.append(i)
if"gary"ini or"狗哥"ini:
gary.append(i)
if"haha"inior"HAHA"inior"哈哈"ini:
haha.append(i)
if"全昭敏"inior"全妹"ini or"全昭body"ini:
qsm.append(i)
if"梁世贊"ini or"世贊"ini or"小不點"ini:
lsz.append(i)
count=[len(csz),len(lzs),len(szx),len(lgz),len(jzg),len(gary),len(haha),len(qsm),len(lsz)]
source = ColumnDataSource(data=dict(name=name, counts=count,color=["orange",
"yellowgreen","pink","darksalmon","lightgreen","paleturquoise","lightsteelblue",
"hotpink","yellow"]))
p = figure(x_range=name, y_range=(,600), plot_height=250, title="話題度排行",
toolbar_location=None, tools="")
p.vbar(x="name", top="counts", color="color", width=0.9, source=source)
p.legend.orientation ="horizontal"
show(p)
Running man一直都不缺CP,前有周一情侶Gary和宋智孝,權力夫婦劉在石和金鐘國,老年line劉在石和池石鎮,我兄我弟金鐘國和haha,背叛者聯盟必觸cross。
現在又有國民兄妹劉在石和全昭敏,麻浦兄妹宋智孝和haha,烤肉line金鐘國haha等等。
他們的關係錯綜複雜,所以筆者打算好好扒一扒觀眾眼中的各種line。
成員關係矩陣
滿分為100分,可以看到池石鎮和劉在石;劉在石和李光洙;金鐘國和宋智孝;Gary和宋智孝;haha和李光洙;全昭敏和宋智孝的相關性均非常高,其中Gary和宋智孝的相關性居然達到40,也就是說評論中如果有Gary那麼有四成的概率會出現宋智孝,周一情侶真的是深入人心。
其次是宋智孝和金鐘國,看來之前還一直有人說他倆會結婚也不是空穴來潮;而梁世贊與其餘成員的相關性都很高,這說明大家都不怎麼單獨提到他,希望世贊可以早日找到自己的定位;獲得觀眾的認可!
defnetwork_edg_csv(com):
df=pandas.DataFrame(columns=["池石鎮","劉在石","宋智孝","李光洙","金鐘國","gary","haha","全昭敏","梁世贊"],index=["池石鎮","劉在石","宋智孝","李光洙","金鐘國","gary","haha","全昭敏","梁世贊"])
df.loc[:,:]=0.0
foriincom:
if(iin"池石鎮"ini or"石鎮"ini or"鼻子"ini):
df["池石鎮"]["池石鎮"] = df["池石鎮"]["池石鎮"] +1
if("劉在石"inior"在石"inior"大神"inior"螞蚱"ini):
df["池石鎮"]["劉在石"] = df["池石鎮"]["劉在石"] +1
df["劉在石"]["池石鎮"] = df["劉在石"]["池石鎮"] +1
#成員關係矩陣df計算方式:在同一個評論中,如果同時出現劉在石和池石鎮,那麼他們的聯繫值+1;再用(劉在石和池石鎮的聯繫值/池石鎮出現在評論的次數)*100得到他們的相關性係數。
foriindf.index:
s=df.loc[i][i]
forjin["池石鎮","劉在石","宋智孝","李光洙","金鐘國","gary","haha","全昭敏","梁世贊"]:
df.loc[i][j]=df.loc[i][j]/s*100
fig=pyl.figure() names=["chishizhen","liuzaishi","songzhixiao","liguangzhu","jinzgongguo","gary","haha","quanshaomin","liangshizan"]
ax=fig.add_subplot(figsize=(100,100))
ax=seaborn.heatmap(df, cmap="rainbow",linewidths =0.05, vmax =100,vmin =,annot =True, annot_kws = {"size":6,"weight":"bold"})
pyl.xticks(np.arange(9) +0.5, names,rotation=-90)
pyl.yticks(np.arange(9) +0.5, names,rotation=360)
ax.set_title("Characteristic correlation")# 標題設置
pyl.show()
社交網路關係網
在社交網路關係網中,按紅、黃、綠、藍將聯繫的緊密程度劃分為四個等級,其中紅色代表聯繫非常緊密,而藍色是不緊密。
可以看到,李光洙、haha、劉在石三人聯繫非常緊密,同時金鐘國和宋智孝的關係也非常密切。對於Gary,自從他退出Running man以後,各成員和他的聯繫都非常小。
def network():
data=pandas.read_csv("run_edge.csv",encoding="utf-8",engine="python")
G = nx.Graph()
pyl.figure(figsize=(20,20))
foriindata.index: G.add_weighted_edges_from([(data.loc[i]["one"],data.loc[i]["two"],data.loc[i]["count"])])
n=nx.draw(G)
pyl.show()
pos=nx.spring_layout(G)
large=[(x,y)for(x,y,z)inG.edges(data=True)ifz["weight"]>100]
middle = [(x, y)for(x, y, z)inG.edges(data=True)if50
middlev = [(x, y)for(x, y, z)inG.edges(data=True)if10
small=[(x,y)for(x,y,z)inG.edges(data=True)ifz["weight"]
nx.draw_networkx_nodes(G,pos,alpha=0.6)
nx.draw_networkx_edges(G,pos,edgelist=large,width=3,edge_color="red")
nx.draw_networkx_edges(G, pos, edgelist=middle, width=2, edge_color="yellow")
nx.draw_networkx_edges(G, pos, edgelist=middlev, width=1, edge_color="yellowgreen")
nx.draw_networkx_edges(G, pos, edgelist=small, width=0.5, edge_color="green")
nx.draw_networkx_labels(G,pos,font_size=10,font_family="simhei")
pyl.axis("off")
pyl.show()
詞雲圖
這個詞雲圖我是用R做的,但是R的詞雲圖背景是要全黑和全白,所以就放棄了給詞雲加個圖案的想法。
回到詞雲圖,可以看出,大家對於節目本身,各位成員的討論是很多的,同時在評論里也表達了自己對Running man各種喜愛之情。
defcomment(com):
df=pandas.DataFrame()
pl=[]
stopword=["的","了","是","。",","," ","?","!","就","
",":","「","」","*","=","(",")","嗎","吧","(",")","?","[","]","、","°","?","!",".","-","`",";",",","《","》"]
foriinrange(len(com)):
cut_list=jieba.cut(com[i],cut_all=False)
w="/".join(cut_list)
w=w.split("/")
forjinw:
ifnotjinstopword:
pl.append(j)
forsinset(pl):
iflen(s)>1:
ifpl.count(s) >50:
x = {}
x["word"]=s.strip("
")
x["count"]=pl.count(s)
df=df.append(x,ignore_index=True)
print(df)
df.to_csv("jieba.csv",encoding="utf-8",index=False,mode="a", header=False)
print(df)
#下面用R生成詞雲圖
library(wordcloud2)
data
jieba.csv")
f=data.frame(data)
f
wordcloud2(f)
最後,希望Running man 給我們帶來越來越多歡樂,收視率越來越好噢。
相關代碼上傳到Github(https://github.com/zuobangbang/running-man--Bilibili)。
![](https://pic.pimg.tw/zzuyanan/1488615166-1259157397.png)
![](https://pic.pimg.tw/zzuyanan/1482887990-2595557020.jpg)
※Go 語言:我那麼值錢,我驕傲了嗎?
※放棄培訓班自學編程,9 個月後我成為年薪 6 位數的軟體工程師
TAG:CSDN |