CDA數據分析師 出品
驚雷/通天修為/天塌地陷紫金錘
紫電/玄真火焰/九天玄劍驚天變
這看起來不著邊際的歌詞,配上簡單粗暴的蹦迪音樂。
最近,一首《驚雷》的喊麥歌曲在短視頻平臺火了,震驚了整個音樂圈。
但4月10日歌手楊坤卻在直播中批評《驚雷》“要歌沒歌,要旋律沒旋律,要節奏沒節奏,要律動沒律動”,評價其“難聽”、“俗氣”。
4月11日,MC六道以原唱者的身份對楊坤的指責做出回應表示,音樂沒有高低之分,稱《驚雷》現在比楊坤的任何一首歌都火。一時間更是把《驚雷》推上了風口浪尖。
那麼《驚雷》這首歌到底怎麼樣?都是哪些人在聽?今天我們就用Python來給你解讀。
01
拿下60億流量
喊麥歌曲《驚雷》火了!
說道喊麥,作為近年來一種新興的表演形式,其內容和表達方式則比較簡單和浮誇,主要形式是在網上下載一些伴奏(以電音伴奏為主),跟著伴奏以簡單的節奏和朗朗上口的押韻手法進行的語言表演。
更簡單的說法就是,演唱時不講究什麼技法,帶著伴奏對著麥喊就完事。比如之前爆火的《一人我飲酒醉》就是很具代表性的喊麥歌曲。
而喊麥歌曲也因為一味堆積看似沒有關聯的詞,鬧騰的電音,簡單粗暴的唱法等,讓大家各種吐槽。而在“全民抵制”喊麥的背景下,《驚雷》卻火了。
從今年3月起,以《驚雷》為BGM的短視頻在各大平臺上迅速走紅。截止到4月24日,在抖音的#驚雷#的標籤頁下顯示共有23w個視頻作品使用,產生64.1億次播放。
一些網友更是跟風錄製了各種翻唱版本。溫柔版、方言版、戲腔版、小黃人版、種類之多,只有你想不到,沒有網友做不到。瞬間《驚雷》就成了今年度的網絡爆款神曲之一。在B站上搜索《驚雷》更是可以看到大量的相關視頻。
我們對B站上《驚雷》的各類視頻進行整理分析發現:
在3月底,《驚雷》就在B站小火了一把,總播放量突破50萬。接著到了4月12日,隨著楊坤和MC六道的“隔空互掐”,大量《驚雷》相關視頻如雨後春筍一般爆發出來,無論是音樂、遊戲、生活、影視和鬼畜各視頻分區產生的相關視頻突破300個,播放量更是水漲船高。
02
“精神小夥”專屬歌曲
都是哪些人在聽《驚雷》?
我們使用Python獲取並分析了網易雲音樂上,MC六道的這首《驚雷》相關的評論數據。
經過去重得到1534條樣本,從而來分析一下《驚雷》這首歌的用戶和評價信息。
先看到結論:
評論時間趨勢圖
首先看到評論的時間,可以發現評論的高峰時間主要集中在:
- 中午12-13點左右;
- 下午5點之後的下班下課時間;
- 以及傍晚睡前9-10點
看來主要的聽歌時間是在忙完工作的午休時,下班後的路上,以及睡前,刷著手機聽聽歌寫寫評論,也比較符合用戶的聽歌習慣。
評論用戶性別分佈
聽歌的人群性別分佈是如何的呢?經過分析發現,男性佔比達到壓倒性的67.08%,女性佔比較少為16.43%,另外16.49%的用戶沒有標註性別。可見聽《驚雷》的更多是男性群體。
評論用戶年齡分佈
分析發現,用戶大多集中在14-30歲之間,以20歲左右居多,除去異常和虛假年齡之外,這個年齡分佈也符合網易雲用戶的年齡段。
評論用戶地區分佈
從城市分佈圖中可以看出,評論用戶涵蓋了全國各大省份,其中廣東的評論用戶排名第一,其次是山東、河北、河南等省份。
根據網易雲曾發佈的音樂數據,北上廣深等發達地帶的用戶對小眾音樂情有獨鍾,這些城市聚集了大量的小眾音樂用戶,其中廣東也是聚集了眾多熱愛電音的用戶,堪稱“最電音省份”。
同時我們查詢了2019年全國各省份的人口排名,排名前三的省份是:廣東、山東、河南,這個結果也與分佈圖較為吻合,果然還是人多力量大。
評論情感正負分佈
那麼評論中大家對《驚雷》更多是稱讚還是吐槽呢?接著我們對評論區的留言進行了情感分析,使用的是百度的API。
我們定義了一個函數獲取情感評分正向和負向的概率值,值介於[0,1]之前,越接近1,情感越偏向於積極,反之則越消極。
通過評論情感得分分佈圖,可以發現:
在1534條數據中,有780條數據評分分值在[0,0.05]之間,佔比50.08%,有一半以上的用戶對這首歌表達了非常厭惡的情緒。我們還看到,有227個樣本的評分在0.95以上,屬於非常正向,這些正向評論真的正向嗎?
我們不妨看幾條這些評論:
比如這一條:
謝謝,這首‘歌’我笑吐了
明顯是屬於負向的情緒,但是因為正向的關鍵詞比較多,百度的情感分析程序給了0.97分,所以可以看出這裡的正向評分也是有誤差。
還有這一條:
突然感覺楊坤老師有點偉大
這首歌雖然是讚揚楊坤老師,但是放到這裡是表達貶義,但是程序並沒有判斷出來,間接說明程序還是沒有人聰明啊(擬合能力不足+漢語語境情況複雜)。
所以實際上大部分評論帶著反諷的口吻,我們可以大膽推斷,這首歌的負向情緒佔比至少上升10~15%個百分點。
評論詞雲分佈:
通過文本分析,可以看出大家對這首歌的評論集中對楊坤和MC六道的討論上,吐槽點主要集中在關於歌曲的“難聽”、“俗氣”、"抄襲"等。同時也表達了對於“喊麥”和"音樂"的討論上。
03
教你用Python分析
網易雲音樂《驚雷》的評論
我們使用Python獲取並分析了網易雲音樂上《驚雷2020》相關的評論數據並進行了以下部分處理和分析,整個分析過程分為以下幾個步驟:
- 數據獲取
- 數據讀入與數據預處理
- 數據分析和可視化
01 數據獲取
此次爬蟲部分主要是調用官方API,本次用到的API主要是:
http://music.163.com/api/v1/resource/comments/R_SO_4_{歌曲ID}?limit={每頁限制數量}&offset={評論數總偏移}
參數說明如下:
{歌曲ID}:歌曲ID
limit:限制每頁獲取的數據條數
offset:翻頁參數偏移量,offset需要是limit的倍數
返回的數據格式為json,通過此接口目前每天獲取的數據量限制是1000條,代碼思路如下:
- 先獲取一頁的數據,並封裝成解析函數parse_one_page
- 變化offset參數,循環構建URL,並調用解析函數
具體代碼如下:
<code># 導入庫 import requests import json import time import pandas as pd def parse_one_page(comment_url): """ 功能:給定一頁的評論接口,獲取一頁的數據。 """ # 添加headers headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 Safari/537.36' } # 發起請求 r = requests.get(comment_url, headers=headers) # 解析數據 comment_data = r.json()['comments'] # 獲取用戶ID user_id = [i['user']['userId'] for i in comment_data] # 獲取用戶暱稱 nick_name = [i['user']['nickname'] for i in comment_data] # 獲取評論ID comment_id = [i['commentId'] for i in comment_data] # 獲取評論內容 content = [i['content'] for i in comment_data] # 獲取評論時間 content_time = [i['time'] for i in comment_data] # 獲取點贊 liked_Count = [i['likedCount'] for i in comment_data] df_one = pd.DataFrame({ 'user_id': user_id, 'nick_name': nick_name, 'comment_id': comment_id, 'content': content, 'content_time': content_time, 'liked_Count': liked_Count }) return df_one def get_all_page(song_id): """ 功能:獲取100頁短評:目前接口一天最多獲取數據量 """ df_all = pd.DataFrame() for i in range(101): # 最多100頁 url = 'http://music.163.com/api/v1/resource/comments/R_SO_4_{}?limit=10&offset={}'.format(song_id, i*10) # 調用函數 df = parse_one_page(comment_url=url) # 循環追加 df_all = df_all.append(df, ignore_index=True) # 打印進度 print('我正在獲取第{}頁的信息'.format(i + 1)) # 休眠一秒 time.sleep(1) return df_all if __name__ == '__main__': # 驚雷 song_id = '1431580747' # 運行函數 df_jl = get_all_page(song_id) 獲取到的數據如下所示,此次我們一共獲取了兩天的數據,經過去重得到1534條樣本,來分析一下《驚雷》這首歌的用戶和評價信息。 獲取的數據集主要包含了以下的信息:評論ID、用戶ID、用戶暱稱、用戶評論、評論時間、評論點贊。根據用戶ID可以獲取評論用戶相關信息,此處暫不做贅述。/<code>
<code># 提取正負概率 positive_prob = [i[0]['positive_prob'] for i in score_list] negative_prob = [i[0]['negative_prob'] for i in score_list] # 增加列 df_comment['positive_prob'] = positive_prob df_comment['negative_prob'] = negative_prob # 添加正向1 負向-1標籤 df_comment['score_label'] = df_comment['positive_prob'].apply(lambda x:1 if x>0.5 else -1) df_comment.head() /<code>
02 數據讀入與數據預處理
此處,我們將對獲取的評論數據集進行以下的處理以方便後續的分析:
- 讀入數據和數據合併,去除重複值
- 評論時間:將評論時間轉換為標準時間
- 用戶評論:使用jieba分詞對評論數據進行分詞處理。
代碼實現如下:
讀入數據、合併、去重
<code># 輸入API Key和Secret Key ak = '你的API Key' sk = '你的Secret Key' host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={}&client_secret={}'.format(ak, sk) # 發起請求 r = requests.post(host) # 獲取token token = r.json()['access_token'] def get_sentiment_score(text): """ 輸入文本,返回情感傾向得分 """ url = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify?charset=UTF-8&access_token={}'.format(token) data = { 'text': text } data = json.dumps(data) try: res = requests.post(url, data=data, timeout=3) items_score = res.json()['items'] except Exception as e: time.sleep(1) res = requests.post(url, data=data, timeout=3) items_score = res.json()['items'] return items_score/<code>
<code># 獲取情感傾向分值並存入列表 score_list = [] step = 0 for i in df_comment['content']: score = get_sentiment_score(i) # 打印進度 step += 1 print('我正在獲取第{}個評分'.format(step), end='\r') score_list.append(score) /<code>
評論時間處理
<code># 提取正負概率 positive_prob = [i[0]['positive_prob'] for i in score_list] negative_prob = [i[0]['negative_prob'] for i in score_list] # 增加列 df_comment['positive_prob'] = positive_prob df_comment['negative_prob'] = negative_prob # 添加正向1 負向-1標籤 df_comment['score_label'] = df_comment['positive_prob'].apply(lambda x:1 if x>0.5 else -1) df_comment.head() /<code>
03 使用百度API進行情感分析
情感分析是NLP的重要部分。這裡我們使用百度的API,來進行情感分析,經測試這個API接口結果相對比較準確。我們定義了一個函數獲取情感評分正向和負向的概率值。返回結果解釋:以正向概率positive_prob為例,值介於[0,1]之前,越接近1,情感越偏向於積極。
代碼和結果如下:
<code># 異常值處理 df_comment['content'] = df_comment['content'].replace('⚰️', '黑人抬棺') /<code>
<code># 輸入API Key和Secret Key ak = '你的API Key' sk = '你的Secret Key' host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={}&client_secret={}'.format(ak, sk) # 發起請求 r = requests.post(host) # 獲取token token = r.json()['access_token'] def get_sentiment_score(text): """ 輸入文本,返回情感傾向得分 """ url = 'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify?charset=UTF-8&access_token={}'.format(token) data = { 'text': text } data = json.dumps(data) try: res = requests.post(url, data=data, timeout=3) items_score = res.json()['items'] except Exception as e: time.sleep(1) res = requests.post(url, data=data, timeout=3) items_score = res.json()['items'] return items_score/<code>
<code># 獲取情感傾向分值並存入列表 score_list = [] step = 0 for i in df_comment['content']: score = get_sentiment_score(i) # 打印進度 step += 1 print('我正在獲取第{}個評分'.format(step), end='\r') score_list.append(score) /<code>
最後提取正負向的概率,並添加標籤。將positive_prob>0.5定義為正向。
<code># 提取正負概率 positive_prob = [i[0]['positive_prob'] for i in score_list] negative_prob = [i[0]['negative_prob'] for i in score_list] # 增加列 df_comment['positive_prob'] = positive_prob df_comment['negative_prob'] = negative_prob # 添加正向1 負向-1標籤 df_comment['score_label'] = df_comment['positive_prob'].apply(lambda x:1 if x>0.5 else -1) df_comment.head() /<code>
04 數據可視化
我們將進行以下的數據可視化
- 評論數時間(按小時)分佈
- 評論用戶性別佔比
- 評論用戶年齡分佈
- 評論用戶地區分佈
- 評論情感得分正負向標籤佔比分析-基於百度自然語言處理API
- 評論情感得分分佈
- 評論詞雲分析
評論數時間(按小時)分佈
<code>age_num = pd.Series(df_user.age.value_counts()) # 刪除異常值 age_num = age_num.drop(['未知',-5, -9, 0, 1, 6, 7]) age_num = pd.DataFrame(age_num).reset_index().rename({'index':'age', 'age':'num'}, axis=1) # 分箱 age_num['age_cut'] = pd.cut(age_num.age, bins=[10,15,20,25,30,35]) # 分組彙總 age_cut_num = age_num.groupby('age_cut')['num'].sum() from pyecharts.charts import Bar # 繪製柱形圖 bar1 = Bar(init_opts=opts.InitOpts(width='1350px', height='750px')) bar1.add_xaxis(age_cut_num.index.astype('str').tolist()) bar1.add_yaxis("數量", age_cut_num.values.tolist(), category_gap='20%') bar1.set_global_opts(title_opts=opts.TitleOpts(title="評論用戶年齡分佈"), visualmap_opts=opts.VisualMapOpts(max_=180), toolbox_opts=opts.ToolboxOpts()) bar1.render()/<code>
經過統計,此次數據採樣日期來自4.22~4.24日。
通過評論時間按小時分佈圖可以看出,評論數在一天當中從5點開始一路攀升,一天有三個小高峰:13點-17點-21點。
評論用戶性別佔比
<code># 計算佔比 gender_perc = df_user['gender'].value_counts() / df_user['gender'].value_counts() .sum() gender_perc = np.round(gender_perc*100,2) from pyecharts.charts import Pie # 繪製餅圖 pie1 = Pie(init_opts=opts.InitOpts(width='1350px', height='750px')) pie1.add("", [*zip(gender_perc.index, gender_perc.values)], radius=["40%","65%"]) pie1.set_global_opts(title_opts=opts.TitleOpts(title='評論用戶性別分佈'), legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"), toolbox_opts=opts.ToolboxOpts()) pie1.set_series_opts(label_opts=opts.LabelOpts(formatter="{c}%")) pie1.set_colors(['#D7655A', '#FFAF34', '#3B7BA9', '#EF9050', '#6FB27C']) pie1.render() /<code>
通過評論用戶性別分佈圖可以看出,在評論用戶中男性用戶佔到了67.08%。
評論用戶年齡分佈
<code>age_num = pd.Series(df_user.age.value_counts()) # 刪除異常值 age_num = age_num.drop(['未知',-5, -9, 0, 1, 6, 7]) age_num = pd.DataFrame(age_num).reset_index().rename({'index':'age', 'age':'num'}, axis=1) # 分箱 age_num['age_cut'] = pd.cut(age_num.age, bins=[10,15,20,25,30,35]) # 分組彙總 age_cut_num = age_num.groupby('age_cut')['num'].sum() from pyecharts.charts import Bar # 繪製柱形圖 bar1 = Bar(init_opts=opts.InitOpts(width='1350px', height='750px')) bar1.add_xaxis(age_cut_num.index.astype('str').tolist()) bar1.add_yaxis("數量", age_cut_num.values.tolist(), category_gap='20%') bar1.set_global_opts(title_opts=opts.TitleOpts(title="評論用戶年齡分佈"), visualmap_opts=opts.VisualMapOpts(max_=180), toolbox_opts=opts.ToolboxOpts()) bar1.render()/<code>
用戶年齡分佈圖可以看出,用戶大多集中在14-30歲之間,以20歲左右居多,除去異常和虛假年齡之外,這個年齡分佈也符合網易雲用戶的年齡段。
評論用戶城市分佈Top10
<code>province_num = df_user.province_name.value_counts() province_num.index = province_num.index.str[:2] province_top10 = province_num[:10] # 柱形圖 bar2 = Bar(init_opts=opts.InitOpts(width='1350px', height='750px')) bar2.add_xaxis(province_top10.index.tolist()) bar2.add_yaxis("城市", province_top10.values.tolist()) bar2.set_global_opts(title_opts=opts.TitleOpts(title="評論者Top10城市分佈"), visualmap_opts=opts.VisualMapOpts(max_=120), toolbox_opts=opts.ToolboxOpts()) bar2.render() /<code>
<code>from pyecharts.charts import Geo from pyecharts.globals import ChartType # 地圖 geo1 = Geo(init_opts=opts.InitOpts(width='1350px', height='750px')) geo1.add_schema(maptype='china') geo1.add("", [list(z) for z in zip(province_num.index.tolist(), province_num.values.tolist())], type_=ChartType.EFFECT_SCATTER, blur_size=15) geo1.set_global_opts(title_opts=opts.TitleOpts(title='評論者國內城市分佈'), visualmap_opts=opts.VisualMapOpts(max_=120)) geo1.set_series_opts(label_opts=opts.LabelOpts(is_show=False)) geo1.render() /<code>
<code># 地圖 map1 = Map(init_opts=opts.InitOpts(width='1350px', height='750px')) map1.add("", [list(z) for z in zip(province_num.index.tolist(), province_num.values.tolist())], maptype='china') map1.set_global_opts(title_opts=opts.TitleOpts(title='評論者國內城市分佈'), visualmap_opts=opts.VisualMapOpts(max_=120), toolbox_opts=opts.ToolboxOpts()) map1.render() /<code>
城市分佈圖中可以看出,評論用戶涵蓋了全國各大省份,其中廣東的評論用戶排名第一。
評論情感得分正負向標籤佔比分析
<code>label_num = df_comment.score_label.value_counts() / df_comment.score_label.value_counts().sum() label_perc = np.round(label_num,3) label_perc.index = ['負向', '正向'] label_perc/<code>
<code>負向 0.701 正向 0.299 Name: score_label, dtype: float64/<code>
<code># 繪製餅圖 pie2 = Pie(init_opts=opts.InitOpts(width='1350px', height='750px')) pie2.add("", [*zip(label_perc.index, label_perc.values)], radius=["40%","65%"]) pie2.set_global_opts(title_opts=opts.TitleOpts(title='評論情感標籤正負向分佈'), legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"), toolbox_opts=opts.ToolboxOpts()) pie2.set_series_opts(label_opts=opts.LabelOpts(formatter="{c}%")) pie2.set_colors(['#3B7BA9', '#EF9050']) pie2.render() /<code>
通過分佈圖可以看出,評論內容中70%左右的內容表達了負向的情緒,說明對於《驚雷》這首喊麥的歌曲,大眾主要持批判的觀點。
情感評論得分分佈
<code># 定義分隔區間 bins = [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0] positive_num = pd.cut(df_comment.positive_prob, bins).value_counts() positive_num = positive_num.sort_index() # 柱形圖 bar3 = Bar(init_opts=opts.InitOpts(width='1350px', height='750px')) bar3.add_xaxis(positive_num.index.astype('str').tolist()) bar3.add_yaxis("", positive_num.values.tolist(), category_gap='5%') bar3.set_global_opts(title_opts=opts.TitleOpts(title="評論情感得分"), visualmap_opts=opts.VisualMapOpts(max_=500), toolbox_opts=opts.ToolboxOpts() ) bar3.render() /<code>
通過評論情感得分分佈圖,可以發現,在1534條數據中,有780條數據評分分值在[0,0.05]之間,佔比50.08%。
評論詞雲分析
此處數據處理主要使用jieba分詞,步驟暫略。
<code>from pyecharts.charts import WordCloud from pyecharts.globals import SymbolType word1 = WordCloud(init_opts=opts.InitOpts(width='1350px', height='750px')) word1.add("", [*zip(key_words.words, key_words.num)], word_size_range=[20, 200], shape=SymbolType.DIAMOND) word1.set_global_opts(title_opts=opts.TitleOpts('網易雲音樂關於驚雷評論詞雲'), toolbox_opts=opts.ToolboxOpts(), ) word1.render() /<code>