Python 爬取 B 站資料分析,宋智孝李光洙誰最受中國粉絲喜愛
作者 | 左伊雅
責編 | 胡巍巍
《Running Man》是韓國SBS電視臺在《星期天真好》單元推出的戶外競技真人秀節目。
節目致力於打造一個不同於Real variety的新型態娛樂節目。每期有不同的主題,由不同的嘉賓參演,分為不同的隊伍進行比賽,通過完成各種遊戲任務,最後獲勝一方將獲得稱號或獎品。
成員組成包括原六位成員劉在石、池石鎮、金鐘國、HAHA(河東勳)、宋智孝、李光洙 ,以及兩位新成員全昭旻、樑世燦 。
Line"/>
抓取資料
自從限韓令釋出後,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。
def male(sex): att=['男','女','保密'] val=[] for i in att: 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 更新前後,對比其他時間段評論數有一定增長。
def ana_week(week): weeks=['星期天','星期一','星期二','星期三','星期四','星期五','星期六'] output_file('week_bar.html') count=[] for i in sorted(set(week)): if not numpy.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=(0,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=[],[] for i in range(len(hour)): if isinstance(hour[i],str): h.append(hour[i][:2]) for i in sorted(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=[],[],[],[],[] for i in range(len(com)): if len(com[i])<10: q.append(zan[i]) if 10<=len(com[i])<50: w.append(zan[i]) if 50<=len(com[i])<100: e.append(zan[i]) if 100<=len(com[i]): 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=[] for i in com: s=SnowNLP(i) q.append(round(s.sentiments,1)) emotion=[] count=[] for i in sorted(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=(0, 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的資料是有點悽慘的。對比兩個新成員,全妹的話題度比世贊高的不是一點點。
def hot(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=[],[],[],[],[],[],[],[],[] for i in com: if '池石鎮'in i or'石鎮' in i or'鼻子'in i: csz.append(i) if '劉在石'in i or '在石' in i or '大神' in i or '螞蚱' in i: lzs.append(i) if '宋智孝' in i or '智孝'in i or '懵智'in i or '美懵'in i: szx.append(i) if '李光洙'in i or '光洙'in i or '一筐豬'in i: lgz.append(i) if '金鐘國'in i or '鍾國'in i or '能力者'in i: jzg.append(i) if 'gary'in i or'狗哥'in i: gary.append(i) if 'haha'in i or 'HAHA'in i or '哈哈'in i: haha.append(i) if '全昭敏'in i or '全妹'in i or'全昭body'in i: qsm.append(i) if '樑世贊'in i or'世贊'in i or'小不點'in i: 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=(0, 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那麼有四成的概率會出現宋智孝,週一情侶真的是深入人心。
其次是宋智孝和金鐘國,看來之前還一直有人說他倆會結婚也不是空穴來潮;而樑世贊與其餘成員的相關性都很高,這說明大家都不怎麼單獨提到他,希望世贊可以早日找到自己的定位;獲得觀眾的認可!
def network_edg_csv(com): df=pandas.DataFrame(columns=['池石鎮','劉在石','宋智孝','李光洙','金鐘國','gary','haha','全昭敏','樑世贊'],index=['池石鎮','劉在石','宋智孝','李光洙','金鐘國','gary','haha','全昭敏','樑世贊']) df.loc[:,:]=0.0 for i in com: if (i in '池石鎮'in i or'石鎮' in i or'鼻子'in i): df['池石鎮']['池石鎮'] = df['池石鎮']['池石鎮'] + 1 if('劉在石'in i or '在石' in i or '大神' in i or '螞蚱' in i): df['池石鎮']['劉在石'] = df['池石鎮']['劉在石'] + 1 df['劉在石']['池石鎮'] = df['劉在石']['池石鎮'] + 1 #成員關係矩陣df計算方式:在同一個評論中,如果同時出現劉在石和池石鎮,那麼他們的聯絡值+1;再用(劉在石和池石鎮的聯絡值/池石鎮出現在評論的次數)*100得到他們的相關性係數。 for i in df.index: s=df.loc[i][i] for j in ['池石鎮','劉在石','宋智孝','李光洙','金鐘國','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 = 0,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)) for i in data.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)in G.edges(data=True) if z['weight']>100] middle = [(x, y) for (x, y, z) in G.edges(data=True) if 50<z['weight'] <= 100] middlev = [(x, y) for (x, y, z) in G.edges(data=True) if 10 < z['weight'] <= 50] small=[(x,y)for (x,y,z)in G.edges(data=True) if z['weight']<=10] 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各種喜愛之情。
def comment(com): df=pandas.DataFrame() pl=[] stopword=['的','了','是','。',',',' ','?','!','就','\n',':','“','”','*','=','(',')','嗎','吧','(',')','・','[',']','、','°','?','!','.','-','`',';',',','《','》'] for i in range(len(com)): cut_list=jieba.cut(com[i],cut_all=False) w='/'.join(cut_list) w=w.split('/') for j in w: if not j in stopword: pl.append(j) for s in set(pl): if len(s)>1: if pl.count(s) > 50: x = {} x['word']=s.strip('\n') 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<-read.csv(header=FALSE,'C:/Users/伊雅/PycharmProjects/untitled/venv/share/doc/ jieba.csv') f=data.frame(data) f wordcloud2(f)
最後,希望Running man 給我們帶來越來越多歡樂,收視率越來越好噢。
相關程式碼上傳到Github(https://github.com/zuobangbang/running-man--Bilibili)。
作者簡介:左伊雅,目前在南京某211大學讀研二,喜歡資料探勘和爬蟲,如果你對該方向感興趣,可關注作者公眾號:zuobangbang
推薦閱讀:
-
ofollow,noindex" target="_blank"> Angular 垮臺、ES6 最受歡迎,20,000 名程式員告訴你誰是 JS 王者!
-
演算法工程師獨得恩寵 四面楚歌的Android工程師該何去何從?
-
算力尋租或將終結中本聰的POW機制?深度解析BCH“司機補貼戰”
-
OA==&mid=2247499310&idx=2&sn=8ccbb7c4ebc91b6ce277ab592299429b&chksm=e99ecdd7dee944c13c8441880ae472d0ff2c2501247859431044896a3f77333b1df437fdb84d&scene=21#wechat_redirect" rel="nofollow,noindex" target="_blank">公開課報名 | 詳解CNN-pFSMN模型以及在語音識別中的應用
-
Java 程式設計師必備的 15 個框架,前 3 個地位無可動搖!