天天看點

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

之前已經用python擷取了網易雲音樂的評論資料,下一步的工作就是資料分析了。一般資料分析無非是采用(統計)數字、圖或者表的形式來展現資料之中隐含的資訊。其中圖和表顯然是最直覺的了。是以這裡我使用可視化的方法即用圖形來展示從評論中挖掘到的各種資訊。

可視化的工具有很多,比如常見的有excel還有一些專門的繪圖軟體,各個程式設計語言當然也有很多可視化的包或者庫,比如統計上使用很多的R語言就有很多可視化的庫,我最喜歡的就是ggplot2了,我使用R語言主要用于資料的清洗以及可視化,其豐富的包(package)大大簡化了資料分析的工作量,而且可以繪制非常複雜、精美的圖表,以後有機會可以給大家專門介紹一下。python中可視化的庫也很多,最著名的的莫過于matplotlib了,這是一個面向對象的繪圖庫,很多方面的用法和matlab類似(從matlab的繪圖風格借鑒而來),由于我以前使用過一段時間的matlab,是以上手還是比較快,其他的還有seaborn(據說是對matplotlib的改進和封裝,使用起來更加友善,沒用過,有時間再研究下)、pygraph等。但是使用最廣泛的還是matplotlib。javascript有Echarts(百度的)等,這個我還沒接觸過,是一個可以網頁上進行可視化的函數庫,據說很棒。其他的當然還有特别多,這裡我就不一一列舉了。有興趣的小夥伴可以自行去查閱資料。這裡我決定使用matplotlib,主要是因為最近主要接觸的就是python,但是資料可視化方面的庫用的不多,剛好可以拿這次的資料來練練手,其次我接觸過matlab,相信對matplotlib入手會更快一點。

這次主要分析的有以下幾個方面:1、一首歌曲評論數目随時間變化的趨勢,比如每天的評論數變化,每月的評論數變化等等。2、一首歌曲點贊數目分布的情況,比如0-10贊有多少個,占多少比例,1000贊以上占多大比例等等。3、熱門評論詞雲的制作,主要想通過詞雲,将文本挖掘的結果可視化,可以看出哪些是高頻詞彙等。4、一首歌曲評論者的基本資訊的情況展示,比如評論者的地區分布,年齡分布、累計聽歌數目分布、動态分布、粉絲數分布等等。通過這些資訊,可以直覺看出一首歌曲被哪些地區、哪些年齡段的人所喜愛,以及聽歌的人具有什麼的特點等等。

好了,廢話不多說了,直接上圖吧。

首先來看一些歌曲評論數随時間的變化。

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 1

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 2

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 3

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 4

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 5

上面的5張圖我分别選取了5首不同的歌曲,有華語歌曲也有英文歌曲,有的起止時間很長(從13年就開始),也有的起止時間很短(從最近幾個月才開始)。總的來說可以分為兩種模式,一種是開始一段時間評論數很少,後來逐漸呈現爆發式地增長,前面三首歌《同桌的你》、《七裡香》、《All Too Well》都是這種模式,而後面兩首歌《不要再孤單》、《stay》則是恰好相反,歌曲剛剛出來的那幾天評論數猛增,後面評論數逐漸下降,之後趨于平穩。通過分析,其實也很好了解,第一種模式的歌曲,往往都是早期曲庫中就存在的歌曲(也可以稱之為“老歌”),那個時候網易雲音樂才剛剛出來,使用者數目還很少,是以這些歌曲每天的評論數很少(沒記錯的話網易雲應該是12、13年左右才出來的吧),後來網易雲一路走紅,直至現在号稱有2億使用者,由于使用者基數大,是以這些經典的老歌自然評論數猛增了,可以想見,這種評論爆發式增長和網易雲音樂使用者的增長趨勢應該是基本一緻的。而至于第二種模式,出現這種模式的歌曲往往都是比較新的歌曲,而且往往伴随着影視劇的火熱突然火起來,比如《不要再孤單》就是電影的主題曲,電影剛上映的那段時間,歌随影熱,評論數自然爆發式增長,後來這段熱潮過去了,評論數自然就降下來了(當然這種歌曲應該以網絡歌曲居多,隻是某一段時間特别火,不黑,我覺得真正的經典評論數應該不會大起大落,比如《晴天》、《see you again》等)。當然我隻是分析了兩種典型的評論随時間變化的模式,實際肯定不止這兩種模式,大家可以自行去探索。

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 6

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 7

前面5張圖都是使用折線圖來展示的,圖6使用的是柱狀圖。我們來看下圖7,圖7展示的最近一段時間比較火的李玉剛的歌曲《剛好遇見你》的評論數随時間的分布,讓人感到奇怪的是,中間從大約1月23日到3月24日的每天的評論量竟然是0!這怎麼可能呢?難道真的是這樣麼?當然不是。我解釋一下原因,這是程式本身的bug,我在抓取評論數過10W的歌曲的過程中發現,我最終看似抓取了全部的評論,但是實際上在去除重複之後,我隻得到了部分的資料,每次大概隻能得到2W到3W左右的資料,其他的資料就缺失了。至今我也沒能解決這個問題,個人覺得是伺服器做了什麼限制,如果有朋友知道該怎麼解決這個問題,望能不吝賜教!

除了可以從宏觀上看一首歌曲每天或者每月的評論數分布之外,我們還可以将不同的歌曲評論随時間變化放到一起對比,或者将一首歌曲每月的評論數放在一起進行對比。

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 8

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 9

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 10

圖 8 就展示了四首不同的歌曲在某一個時間段評論數目随時間變化,圖9展示了《同桌的你》從16年8月到17年3月這8個月的時間裡每月評論數的分布情況,圖10則是《越長大越孤單》從16年4月到17年3月這12個月的每月評論數分布。其實,這種圖形很容易做出,因為我已經将繪圖函數做了封裝,可以設定自定義參數字典,來生成自己想要的不同的圖形,也可以選擇繪制圖形的種類、顔色以及繪制的時間段、時間間隔等,在文末我會說明這一點。

接下來,看一下評論點贊數目的分布情況。

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 11

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 12

圖10和圖11展示的點贊數目分布我去除了10贊以下的,原因是我發現一首歌曲絕大部分的點贊數目(超過99%)都是10贊以下的,這也與我們的常識相一緻,是以為了友善我就直接去除了。通過上面的兩張圖我們可以看出,紅色區域面積最大,即100贊到1000占據了全部10贊以上評論的絕大部分,其次是10到100贊,然後是1000贊到10000贊,最少的是1W贊以上,我發現大部分歌曲基本都是呈現這個規律,是以隻在這裡簡單提一下,就不做詳細分析了。

接下來分析歌曲熱門評論的詞雲展示,其實python的詞雲,我之前的一篇随筆也有提到過,使用wordcloud(繪制詞雲)和jieba(中文分詞)即可。這裡就不細說了。直接看圖吧。

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 13 《不要再孤單》詞雲

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 14   周傑倫熱門50首歌曲熱門評論詞雲

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 15 Taylor Swift 熱門歌曲熱門評論詞雲

從以上的詞雲中還是可以看出一首歌曲或者一位歌手,評論區中出現頻率最高的是哪些詞的。比如傑倫 的熱門評論中反複出現的詞就有周傑倫、青春、喜歡、女朋友、故事等等,一股青春懷舊風撲面而來啊。哈哈,其他有意思的大家自己去分析吧。

最後,我想重點來分析一下,一首歌曲的評論者個人資訊具有什麼樣的特點。我将這些特點放在一張圖中,通過多張餅圖來展示了,見下。

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 16

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 17

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 18

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 19

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 20

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 21

python可視化分析網易雲音樂評論_網易雲音樂評論 可視化分析

圖 22

圖 16 到 圖 22 展示了不同的歌手(有中有外,有老有少)以及不同的歌曲(老歌和新歌)評論者多方面的資訊分布。通過對比不難發現如下的規律。周傑倫粉絲主要還是以90後和95後為主,這二者之和超過80%。周傑倫、Taylor Swift、Bruno Mars 這三位歌手評論者累計聽歌在1000到10000之間的人數(1000-10000算是累計聽歌較多)占比要顯著高于其他幾個歌手,粉絲人數在10-100以及100-1000的比例也是如此,這幾位都可以稱得上時下的歌壇巨星,評論者的聽歌數目以及粉絲人數可以在一定程度上反映出對音樂的喜愛程度以及對音樂的鑒賞力吧(不黑)。TFboys評論者中00後的比例高達25%,為列舉的所有歌手中最高,其他歌手00後的比例均不超過10%,不過考慮到tf是美少男組合,這也就可以說的通了。劉德華歌曲的評論者中80後以及80前的比例之和近20%,而其他歌手這一數字基本在7%左右,這在一定程度上可以說明劉天王的粉絲最多的還是而立之後的中年人啊。再來看地區,一眼望去,無論是歌手還是歌曲,地區分布的前五中都出現了一個共同的身影,那就是北京市東城區,看來網易雲上有相當一部分使用者都是來自北京市東城區啊,不過考慮到北京市是我國的文化中心,許多明星、歌手均在北京定居,還有網易雲上推薦的一些音樂人很多都在北京(東城區),這點就不難了解了。多次出現的地區還有廣州市、成都市等等,這些都是經濟較發達的地區,也是文化産業特别是音樂産業發達的地區(廣州主要是粵語歌,而且離香港也很近,成都民謠應該很豐富(猜測))。這麼一考慮,這些結果就不難了解了。當然,可以挖掘的其他資訊還有很多,比如還有動态的分布等等,還可以按照音樂的類别進行對比等等,如果有興趣,大家可以自己去完成這個工作。

到這裡,其實評論資料的可視化就差不多結束了。其實我做得很粗糙,很多分析也純屬我個人的臆測,大家随便看看就好。最後我還有幾點要補充的。

在這次寫代碼的過程中,我一開始覺得應該寫不了幾行應該就把資料可視化搞定了,沒想到最終還花了我挺長的時間,加到一起代碼有七八百行,當然,很多東西都是可以精簡的,我懶得去弄了。我将繪圖的幾個函數抽象了出來,可以通過簡單地配置參數字典(settings)傳入函數來配置自己想要的圖形,比如可以控制要繪制散點圖還是柱狀圖,控制顔色、時間間隔等等,隻需要更改相應的參數字典就可以了。主要有兩個類,一個是NetCloudCrawl類,主要用于歌曲評論數的抓取,還有一個是NetCloudProcessor,主要用于生成相關檔案以及繪制可視化圖形。幾個主要的函數如下:1 create_all_necessary_files(song_id,song_name) 這個函數可以自動完成評論資料的抓取(包括熱門評論)、儲存到檔案,生成必要的統計txt檔案,生成詞雲等。隻需要傳入歌曲id(song_id)以及歌曲名字(song_name)即可。2 plot_comments(song_name,settings)函數,用于繪制基本的評論或者點贊圖形,settings為參數字典,用于控制繪圖方式以及呈現結果。3 sub_plot_comments(song_names_list,settings,row,col)用于在一張圖中繪制多個歌曲的評論分布,song_names_list 為歌曲名字清單,settings 為繪圖參數字典,row為子圖行數,col為子圖列數。  4 draw_wordcloud 用于繪制詞雲  5 sub_plot_months 用于在一張圖中繪制某一首歌曲在某幾個月(按月繪制)中的評論分布。6 sub_plot_commenters_info 用于繪制歌曲評論者的各項資訊分布 。 還有三個 測試函數,分别是 sub_plot_months_test、subplot_test、plot_comments_test 直接調用相應的繪圖函數,可以友善地在其中配置參數字典,然後直接調用測試函數即可繪制圖形。是以繪制圖形其實隻有簡單的兩步:第一步,确定歌曲名稱以及id(直接去網易雲音樂上找相應歌曲連結即可,?id= 後面的數字就是歌曲id),然後調用create_all_necessary_files 生成所需要的檔案;第二步:調用相應的繪圖函數,一般隻需要傳入歌曲名字以及參數字典即可。

最後還是附上全部的代碼如下:

NetCloud_spider3.py

1 #!/usr/bin/env python2.7

2 #-*- coding: utf-8 -*-

3 #@Time : 2017/3/28 8:46

4 #@Author : Lyrichu

5 #@Email : [email protected]

6 #@File : NetCloud_spider3.py

7 '''

8 @Description:9 網易雲音樂評論爬蟲,可以完整爬取整個評論10 部分參考了@平胸小仙女的文章(位址:https://www.zhihu.com/question/36081767)11 post加密部分也給出了,可以參考原帖:12 作者:平胸小仙女13 連結:https://www.zhihu.com/question/36081767/answer/14028779514 來源:知乎15 '''

16 from Crypto.Cipher importAES17 importbase6418 importrequests19 importjson20 importcodecs21 importtime22 importos23

24 #定義抓取評論類NetCloudCrawl

25 classNetCloudCrawl(object):26 def __init__(self):27 #頭部資訊

28 self.headers ={29 'Host':"music.163.com",30 'User-Agent':"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0",31 'Accept-Language':"zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",32 'Accept-Encoding':"gzip, deflate",33 'Content-Type':"application/x-www-form-urlencoded",34 'Cookie':"_ntes_nnid=754361b04b121e078dee797cdb30e0fd,1486026808627; _ntes_nuid=754361b04b121e078dee797cdb30e0fd; JSESSIONID-WYYY=yfqt9ofhY%5CIYNkXW71TqY5OtSZyjE%2FoswGgtl4dMv3Oa7%5CQ50T%2FVaee%2FMSsCifHE0TGtRMYhSPpr20i%5CRO%2BO%2B9pbbJnrUvGzkibhNqw3Tlgn%5Coil%2FrW7zFZZWSA3K9gD77MPSVH6fnv5hIT8ms70MNB3CxK5r3ecj3tFMlWFbFOZmGw%5C%3A1490677541180; _iuqxldmzr_=32; vjuids=c8ca7976.15a029d006a.0.51373751e63af8; vjlast=1486102528.1490172479.21; __gads=ID=a9eed5e3cae4d252:T=1486102537:S=ALNI_Mb5XX2vlkjsiU5cIy91-ToUDoFxIw; vinfo_n_f_l_n3=411a2def7f75a62e.1.1.1486349441669.1486349607905.1490173828142; [email protected]|1489375076|1|study|00&99|null&null&null#hub&420100#10#0#0|155439&1|study_client|[email protected]; NTES_CMT_USER_INFO=84794134%7Cm155****4439%7Chttps%3A%2F%2Fsimg.ws.126.net%2Fe%2Fimg5.cache.netease.com%2Ftie%2Fimages%2Fyun%2Fphoto_default_62.png.39x39.100.jpg%7Cfalse%7CbTE1NTI3NTk0NDM5QDE2My5jb20%3D; usertrack=c+5+hljHgU0T1FDmA66MAg==; Province=027; City=027; _ga=GA1.2.1549851014.1489469781; __utma=94650624.1549851014.1489469781.1490664577.1490672820.8; __utmc=94650624; __utmz=94650624.1490661822.6.2.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; playerid=81568911; __utmb=94650624.23.10.1490672820",35 'Connection':"keep-alive",36 'Referer':'http://music.163.com/',37 'Upgrade-Insecure-Requests':"1"

38 }39 #設定代理伺服器

40 self.proxies={41 'http:':'http://180.123.225.51',42 'https:':'https://111.72.126.116'

43 }44 #offset的取值為:(評論頁數-1)*20,total第一頁為true,其餘頁為false

45 #first_param = '{rid:"", offset:"0", total:"true", limit:"20", csrf_token:""}' # 第一個參數

46 self.second_param = "010001" #第二個參數

47 #第三個參數

48 self.third_param = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"

49 #第四個參數

50 self.forth_param = "0CoJUm6Qyw8W8jud"

51 self.encSecKey = "257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c"

52

53 #擷取參數

54 def get_params(self,page): #page為傳入頁數

55 first_key =self.forth_param56 second_key = 16 * 'F'

57 iv = "0102030405060708"

58 if(page == 1): #如果為第一頁

59 first_param = '{rid:"", offset:"0", total:"true", limit:"20", csrf_token:""}'

60 h_encText =self.AES_encrypt(first_param, first_key,iv)61 else:62 offset = str((page-1)*20)63 first_param = '{rid:"", offset:"%s", total:"%s", limit:"20", csrf_token:""}' %(offset,'false')64 h_encText =self.AES_encrypt(first_param, first_key, iv)65 h_encText =self.AES_encrypt(h_encText, second_key, iv)66 returnh_encText67

68 #解密過程

69 defAES_encrypt(self,text, key, iv):70 pad = 16 - len(text) % 16

71 text = text + pad *chr(pad)72 encryptor =AES.new(key, AES.MODE_CBC, iv)73 encrypt_text =encryptor.encrypt(text)74 encrypt_text =base64.b64encode(encrypt_text)75 returnencrypt_text76

77 #獲得評論json資料

78 defget_json(self,url, params, encSecKey):79 data ={80 "params": params,81 "encSecKey": encSecKey82 }83 response = requests.post(url, headers=self.headers, data=data,proxies =self.proxies)84 returnresponse.content85

86 #抓取熱門評論,傳回熱評清單

87 defget_hot_comments(self,url):88 hot_comments_list =[]89 hot_comments_list.append(u"使用者ID 使用者昵稱 使用者頭像位址 評論時間 點贊總數 評論内容\n")90 params = self.get_params(1) #第一頁

91 json_text =self.get_json(url,params,self.encSecKey)92 json_dict =json.loads(json_text)93 hot_comments = json_dict['hotComments'] #熱門評論

94 print("共有%d條熱門評論!" %len(hot_comments))95 for item inhot_comments:96 comment = item['content'] #評論内容

97 likedCount = item['likedCount'] #點贊總數

98 comment_time = item['time'] #評論時間(時間戳)

99 userID = item['user']['userId'] #評論者id

100 nickname = item['user']['nickname'] #昵稱

101 avatarUrl = item['user']['avatarUrl'] #頭像位址

102 comment_info = unicode(userID) + u" " + nickname + u" " + avatarUrl + u" " + unicode(comment_time) + u" " + unicode(likedCount) + u" " + comment + u"\n"

103 hot_comments_list.append(comment_info)104 returnhot_comments_list105

106 #抓取某一首歌的全部評論

107 defget_all_comments(self,url):108 all_comments_list = [] #存放所有評論

109 all_comments_list.append(u"使用者ID 使用者昵稱 使用者頭像位址 評論時間 點贊總數 評論内容\n") #頭部資訊

110 params = self.get_params(1)111 json_text =self.get_json(url,params,self.encSecKey)112 json_dict =json.loads(json_text)113 comments_num = int(json_dict['total'])114 if(comments_num % 20 ==0):115 page = comments_num / 20

116 else:117 page = int(comments_num / 20) + 1

118 print("共有%d頁評論!" %page)119 for i in range(page): #逐頁抓取

120 params = self.get_params(i+1)121 json_text =self.get_json(url,params,self.encSecKey)122 json_dict =json.loads(json_text)123 if i ==0:124 print("共有%d條評論!" % comments_num) #全部評論總數

125 for item in json_dict['comments']:126 comment = item['content'] #評論内容

127 likedCount = item['likedCount'] #點贊總數

128 comment_time = item['time'] #評論時間(時間戳)

129 userID = item['user']['userId'] #評論者id

130 nickname = item['user']['nickname'] #昵稱

131 avatarUrl = item['user']['avatarUrl'] #頭像位址

132 comment_info = unicode(userID) + u" " + nickname + u" " + avatarUrl + u" " + unicode(comment_time) + u" " + unicode(likedCount) + u" " + comment + u"\n"

133 all_comments_list.append(comment_info)134 print("第%d頁抓取完畢!" % (i+1))135 returnall_comments_list136

137

138 #将評論寫入文本檔案

139 defsave_to_file(self,list,filename):140 with codecs.open(filename,'a',encoding='utf-8') as f:141 f.writelines(list)142 print("寫入檔案成功!")143

144 #抓取歌曲評論

145 defsave_all_comments_to_file(self,song_id,song_name):146 start_time = time.time() #開始時間

147 url = "http://music.163.com/weapi/v1/resource/comments/R_SO_4_%d/?csrf_token=" %song_id148 if(os.path.exists(song_name)):149 filename = u"%s/%s.txt" %(song_name,song_name)150 else:151 os.mkdir(song_name)152 filename = u"%s/%s.txt" %(song_name,song_name)153 all_comments_list =self.get_all_comments(url)154 self.save_to_file(all_comments_list,filename)155 end_time = time.time() #結束時間

156 print(u"抓取歌曲《%s》耗時%f秒." % (song_name,end_time - start_time))

NetCloud_comments_plot.py

1 #!/usr/bin/env python

2 #-*- coding: utf-8 -*-

3 #@Time : 2017/3/29 9:07

4 #@Author : Lyrichu

5 #@Email : [email protected]

6 #@File : NetCloud_comments_plot.py

7 '''

8 @Description:9 對抓取來的網易雲評論資料進行簡單的可視化分析10 '''

11 from NetCloud_spider3 importNetCloudCrawl12 importrequests13 importmatplotlib.dates as mdates14 from pylab import *

15 mpl.rcParams['font.sans-serif'] = ['SimHei'] #防止無法顯示中文

16 importmatplotlib.pyplot as plt17 from datetime importdatetime18 importre19 importtime20 importpandas as pd21 importcodecs22 importjieba23 from wordcloud importWordCloud24 from scipy.misc importimread25 from os importpath26 importos27

28

29 classNetCloudProcessor(NetCloudCrawl):30 #讀取評論文本資料,傳回一個清單,清單的每個元素為一個字典,字典中包含使用者id,評論内容等

31 defread_comments_file(self,filename):32 list_comments = [] #評論資料清單

33 with open(filename,'r') as f:34 comments_list = f.readlines() #讀取文本,按行讀取,傳回清單

35 del comments_list[0] #删除首個元素

36 comments_list = list(set(comments_list)) #去除重複資料

37 count_ = -1 #記錄評論數

38 for comment incomments_list:39 comment = comment.replace("\n","") #去除末尾的換行符

40 try:41 if (re.search(re.compile(r'^\d+?'),comment)): #如果以數字開頭

42 comment_split = comment.split(' ',5) #以空格分割(預設)

43 comment_dict ={}44 comment_dict['userID'] = comment_split[0] #使用者ID

45 comment_dict['nickname'] = comment_split[1] #使用者昵稱

46 comment_dict['avatarUrl'] = comment_split[2] #使用者頭像位址

47 comment_dict['comment_time'] = int(comment_split[3])#評論時間

48 comment_dict['likedCount'] = int(comment_split[4])#點贊總數

49 comment_dict['comment_content'] = comment_split[5] #評論内容

50 list_comments.append(comment_dict)51 count_ += 1

52 else:53 list_comments[count_]['comment_content'] += comment #将評論追加到上一個字典

54 exceptException,e:55 print(e)56 list_comments.sort(key= lambda x:x['comment_time'])57 print(u"去除重複之後有%d條評論!" % (count_+1))58 return (count_+1,list_comments) #傳回評論總數以及處理完的評論内容

59

60 #将網易雲的時間戳轉換為年-月-日的日期函數

61 #時間戳需要先除以1000才能得到真實的時間戳

62 #format 為要轉換的日期格式

63 deffrom_timestamp_to_date(self,time_stamp,format):64 time_stamp = time_stamp*0.001

65 real_date =time.strftime(format,time.localtime(time_stamp))66 returnreal_date67

68

69 #統計相關資料寫入文本檔案

70 defcount_comments_info(self,comments_list,count_,song_name):71 x_date_Ym = [] #評論數按年月進行統計

72 x_date_Ymd = [] #評論數按年月日進行統計

73 x_likedCount = [] #點贊總數分布

74 for i inrange(count_):75 time_stamp = comments_list[i]['comment_time'] #時間戳

76 real_date_Ym = self.from_timestamp_to_date(time_stamp,'%Y-%m') #按年月進行統計

77 real_date_Ymd = self.from_timestamp_to_date(time_stamp,'%Y-%m-%d') #按年月日統計

78 likedCount = comments_list[i]['likedCount'] #點贊總數

79 x_date_Ym.append(real_date_Ym)80 x_date_Ymd.append(real_date_Ymd)81 x_likedCount.append(likedCount)82 x_date_Ym_no_repeat =[]83 y_date_Ym_count =[]84 x_date_Ymd_no_repeat =[]85 y_date_Ymd_count =[]86 x_likedCount_no_repeat =[]87 y_likedCount_count =[]88 #年月

89 for date_ inx_date_Ym:90 if date_ not inx_date_Ym_no_repeat:91 x_date_Ym_no_repeat.append(date_)92 y_date_Ym_count.append(x_date_Ym.count(date_))93 #年月日

94 for date_ inx_date_Ymd:95 if date_ not inx_date_Ymd_no_repeat:96 x_date_Ymd_no_repeat.append(date_)97 y_date_Ymd_count.append(x_date_Ymd.count(date_))98

99 for likedCount inx_likedCount:100 if likedCount not inx_likedCount_no_repeat:101 x_likedCount_no_repeat.append(likedCount)102 y_likedCount_count.append(x_likedCount.count(likedCount))103 #将統計的資料存入txt檔案

104 with open(u"%s/comments_num_by_Ym.txt" % song_name,"w") as f:105 f.write("date_Ym comments_num\n")106 for index,date_Ym inenumerate(x_date_Ym_no_repeat):107 f.write(x_date_Ym_no_repeat[index] + " " + str(y_date_Ym_count[index]) + "\n")108 print(u"成功寫入comments_num_by_Ym.txt!")109 with open(u"%s/comments_num_by_Ymd.txt" % song_name,"w") as f:110 f.write("date_Ymd comments_num\n")111 for index,date_Ymd inenumerate(x_date_Ymd_no_repeat):112 f.write(x_date_Ymd_no_repeat[index] + " " + str(y_date_Ymd_count[index]) + "\n")113 print(u"成功寫入comments_num_by_Ymd.txt!")114 with open(u"%s/likedCount.txt" % song_name,"w") as f:115 f.write("likedCount count_num\n")116 for index,likedCount inenumerate(x_likedCount_no_repeat):117 f.write(str(x_likedCount_no_repeat[index]) + " " + str(y_likedCount_count[index]) + "\n")118 print(u"成功寫入likedCount.txt!")119 #得到處理過的x_date 和 count 統計資訊

120 defget_xdate_ycount(self,count_file_name,date_type,min_date_Ym,max_date_Ym,min_date_Ymd,max_date_Ymd):121 with open(count_file_name,'r') as f:122 list_count =f.readlines()123 #comment_or_like = list_count[0].replace("\n","").split(" ")[1] # 判斷是評論數還是點贊數

124 #song_name = count_file_name.split("/")[0] # 歌曲名字

125 dellist_count[0]126 x_date =[]127 y_count =[]128 for content inlist_count:129 content.replace("\n","")130 res = content.split(' ')131 if(date_type == '%Y-%m-%d'):132 if(int("".join(res[0].split("-"))) >= int("".join(min_date_Ymd.split("-"))) and int("".join(res[0].split("-"))) <= int("".join(max_date_Ymd.split("-")))):133 x_date.append(res[0])134 y_count.append(int(res[1]))135 else:136 if(int("".join(res[0].split("-"))) >= int("".join(min_date_Ym.split("-"))) and int("".join(res[0].split("-"))) <= int("".join(max_date_Ym.split("-")))):137 x_date.append(res[0])138 y_count.append(int(res[1]))139 return(x_date,y_count)140

141

142 #繪制圖形展示歌曲評論以及點贊分布

143 #plot_type:為 'plot' 繪制散點圖 為 'bar' 繪制條形圖

144 #date_type 為日期類型

145 #time_distance 為時間間隔(必填)例如:5D 表示5天,1M 表示一個月

146 #min_liked_num 為繪圖時的最小點贊數

147 #max_liked_num 為繪圖時的最大點贊數

148 #min_date_Ym 為最小日期(年-月形式)

149 #max_date_Ym 為最大日期(年-月形式)

150 #min_date_Ymd 為最小日期(年-月-日形式)

151 #max_date_Ymd 為最大日期(年-月-日形式)

152 defplot_comments(self,song_name,settings):153 comment_type = settings['comment_type']154 date_type = settings['date_type']155 plot_type = settings['plot_type']156 bar_width = settings['bar_width']157 rotation = settings['rotation']158 time_distance = settings['time_distance']159 min_date_Ymd = settings['min_date_Ymd']160 max_date_Ymd = settings['max_date_Ymd']161 min_date_Ym = settings['min_date_Ym']162 max_date_Ym = settings['max_date_Ym']163 if(comment_type): #評論

164 if(date_type == '%Y-%m-%d'):165 count_file_name = u"%s/comments_num_by_Ymd.txt" %song_name166 else:167 count_file_name = u"%s/comments_num_by_Ym.txt" %song_name168 else:169 count_file_name = u"%s/likedCount.txt" %song_name170 with open(count_file_name,'r') as f:171 list_count =f.readlines()172 dellist_count[0]173 if(comment_type): #如果是評論

174 x_date =[]175 y_count =[]176 for content inlist_count:177 content.replace("\n","")178 res = content.split(' ')179 if(date_type == '%Y-%m-%d'):180 if(int("".join(res[0].split("-"))) >= int("".join(min_date_Ymd.split("-"))) and int("".join(res[0].split("-"))) <= int("".join(max_date_Ymd.split("-")))):181 x_date.append(res[0])182 y_count.append(int(res[1]))183 else:184 if(int("".join(res[0].split("-"))) >= int("".join(min_date_Ym.split("-"))) and int("".join(res[0].split("-"))) <= int("".join(max_date_Ym.split("-")))):185 x_date.append(res[0])186 y_count.append(int(res[1]))187 else: #如果是點贊

188 #分為10-100,100-1000,1000-10000,10000以上這5個區間,由于絕大多數歌曲評論點贊數都在10贊一下

189 #超過99%,是以10贊以下暫時忽略

190 x_labels = [u'10-100',u'100-1000',u'1000-10000',u'10000以上']191 y_count =[0,0,0,0]192 for content inlist_count:193 content.replace("\n","")194 res = content.split(' ')195 if(int(res[0]) <= 100 and int(res[0]) >= 10):196 y_count[0] += int(res[1])197 elif(int(res[0]) <= 1000):198 y_count[1] += int(res[1])199 elif(int(res[0]) <= 10000):200 y_count[2] += int(res[1])201 else:202 y_count[3] += int(res[1])203 #如果是評論

204 if(comment_type):205 type_text = u"評論"

206 x = [datetime.strptime(d, date_type).date() for d inx_date]207 #配置橫坐标為日期類型

208 plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%s' %date_type))209 if(date_type == '%Y-%m-%d'):210 plt.gca().xaxis.set_major_locator(mdates.DayLocator())211 else:212 plt.gca().xaxis.set_major_locator(mdates.MonthLocator())213 if(plot_type == 'plot'):214 plt.plot(x,y_count,color = settings['color'])215 elif(plot_type == 'bar'):216 plt.bar(x,y_count,width=bar_width,color = settings['color'])217 else:218 plt.scatter(x,y_count,color = settings['color'])219 plt.gcf().autofmt_xdate(rotation=rotation) #自動旋轉日期标記

220 plt.title(u"網易雲音樂歌曲《" + song_name + u"》" + type_text + u"數目分布")221 plt.xlabel(u"日期")222 plt.ylabel(u"數目")223 plt.xticks(pd.date_range(x[0],x[-1],freq="%s" % time_distance)) #設定日期間隔

224 plt.show()225 else: #如果是點贊

226 x =y_count227 type_text = u"點贊"

228 pie_colors = settings['pie_colors']229 auto_pct = settings['auto_pct'] #百分比保留幾位小數

230 expl = settings['expl'] #每塊距離圓心的距離

231 plt.pie(x,labels = x_labels,explode=expl,colors = pie_colors,autopct =auto_pct)232 plt.title(u"網易雲音樂歌曲《" + song_name + u"》" + type_text + u"數目分布")233 plt.legend(x_labels)234 plt.show()235 plt.close()236

237

238 #生成某個歌曲的統計資訊檔案

239 defgenerate_count_info_files(self,song_name):240 filename = "%s/%s.txt" %(song_name,song_name)241 count_,list_comments =self.read_comments_file(filename)242 print(u"%s有%d條評論!" %(song_name,count_))243 self.count_comments_info(list_comments,count_,song_name)244

245 #一步完成資料抓取,生成統計資訊檔案的工作

246 defcreate_all_necessary_files(self,song_id,song_name):247 start_time =time.time()248 #資料抓取并寫入檔案

249 self.save_all_comments_to_file(song_id,song_name)250 #生成熱門評論檔案

251 url = "http://music.163.com/weapi/v1/resource/comments/R_SO_4_%d/?csrf_token=" %song_id252 hot_comments_list =self.get_hot_comments(url)253 self.save_to_file(hot_comments_list,u"%s/hotcomments.txt" %song_name)254 #生成統計資訊檔案(3個)

255 self.generate_count_info_files(song_name)256 #生成所有評論者資訊檔案

257 self.save_commenters_info_to_file(song_name)258 #生成 評論詞雲(全部評論)

259 self.draw_wordcloud(song_name,singer_name=False)260 end_time =time.time()261 print(u"任務完成!程式耗時%f秒!" %(end_time -start_time))262 #得到某首歌曲下所有評論者(需要去除重複)的首頁資訊

263 defget_commenters_info(self,filename):264 commenters_info_list = [] #存放評論使用者資訊

265 with codecs.open(filename,"r",encoding= 'utf-8') as f:266 lists =f.readlines()267 del lists[0] #删除第一行

268 commenters_urls_list = [] #評論者清單

269 for info inlists:270 if(re.match(r'^\d.*?',info)):271 commenters_urls_list.append(u"http://music.163.com/user/home?id=" + info.split(" ")[0]) #評論者首頁位址

272 commenters_urls_list = list(set(commenters_urls_list)) #去除重複的人

273 print("共有%d個不同評論者!" %len(commenters_urls_list))274 for index,url inenumerate(commenters_urls_list):275 try:276 info_dict = {} #評論使用者個人資訊字典

277 user_id_compile = re.compile(r'.*id=(\d+)')278 user_id = re.search(user_id_compile,url).group(1)279 html = requests.get(url,headers =self.headers).text280 event_count_compile = re.compile(r'(\d+?)')281 event_count = re.search(event_count_compile,html).group(1) #個人動态數目

282 follow_count_compile = re.compile(r'(\d+?)')283 follow_count = re.search(follow_count_compile,html).group(1) #關注人數

284 fan_count_compile = re.compile(r'(\d+?)')285 fan_count = re.search(fan_count_compile,html).group(1)286 location_compile = re.compile(u'所在地區:(.+?)') #注意需要使用unicode編碼,正規表達式才能比對

287 location_res =re.search(location_compile,html)288 if(location_res):289 location = location_res.group(1)290 else:291 location = u"未知地區"

292 self_description_compile = re.compile(u'

個人介紹:(.*?) ')293 if(re.search(self_description_compile,html)): #如果可以比對到

294 self_description = re.search(self_description_compile,html).group(1)295 else:296 self_description = u"未知個人介紹"

297 age_compile = re.compile(r'')298 if(re.search(age_compile,html)):299 age_time = re.search(age_compile,html).group(1) #這個得到的是出生日期距離unix時間戳起點的距離

300 #需要将其轉換為年齡

301 age = (2017-1970) - (int(age_time)/(1000*365*24*3600)) #真實的年齡

302 else:303 age = u"未知年齡"

304 listening_songs_num_compile = re.compile(u'

累積聽歌(\d+?)首

')305 if(re.search(listening_songs_num_compile,html)):306 listening_songs_num = re.search(listening_songs_num_compile,html).group(1) #聽歌總數

307 else:308 listening_songs_num = u'未知聽歌總數'

309 info_dict['user_id'] =user_id310 info_dict['event_count'] = event_count #動态總數

311 info_dict['follow_count'] = follow_count #關注總數

312 info_dict['fan_count'] = fan_count #粉絲總數

313 info_dict['location'] = location #所在地區

314 info_dict['self_description'] = self_description #個人介紹

315 info_dict['age'] = age #年齡

316 info_dict['listening_songs_num'] = listening_songs_num #累計聽歌總數

317 commenters_info_list.append(info_dict)318 print("成功添加%d個使用者資訊!" % (index+1))319 exceptException,e:320 printe321 return commenters_info_list #傳回評論者使用者資訊清單

322

323 #儲存評論者的資訊

324 defsave_commenters_info_to_file(self,song_or_singer_name):325 if(os.path.exists(u"%s/%s.txt" %(song_or_singer_name,song_or_singer_name))):326 filename = u"%s/%s.txt" %(song_or_singer_name,song_or_singer_name)327 else:328 filename = u"%s/hotcomments.txt" %song_or_singer_name329 commenters_info_lists = self.get_commenters_info(filename) #得到使用者資訊清單

330 with codecs.open(u"%s/commenters_info.txt" % song_or_singer_name,"w",encoding='utf-8') as f:331 f.write(u"使用者ID 動态總數 關注總數 粉絲總數 所在地區 個人介紹 年齡 累計聽歌總數\n")332 for info incommenters_info_lists:333 user_id = info['user_id'] #使用者id

334 event_count = info['event_count'] #動态數目

335 follow_count = info['follow_count'] #關注的人數

336 fan_count = info['fan_count'] #粉絲數

337 location = info['location'] #所在地區

338 self_description = info['self_description'] #個人介紹

339 age = unicode(info['age']) #年齡

340 listening_songs_num = info['listening_songs_num'] #累計聽歌總數

341 full_info = unicode(user_id) + u" " + event_count + u" " + follow_count + u" " + fan_count + u" " + location + u" " + self_description + u" " + age + u" " + listening_songs_num + u"\n"

342 f.write(full_info)343 print(u"成功寫入檔案%s/commenters_info.txt" %song_or_singer_name)344

345 #得到某個歌手全部熱門歌曲id清單

346 defget_songs_ids(self,singer_url):347 ids_list =[]348 html = requests.get(singer_url,headers = self.headers,proxies =self.proxies).text349 re_pattern = re.compile(r'.*?')350 ids =re.findall(re_pattern,html)351 for id inids:352 ids_list.append(id)353 returnids_list354 #得到某個歌手所有歌曲的熱門評論

355 defget_singer_all_hot_comments(self,singer_name,singer_id):356 singer_url = 'http://music.163.com/artist?id=%d' %singer_id357 song_ids = self.get_songs_ids(singer_url) #得到歌手所有熱門歌曲id清單

358 for song_id insong_ids:359 url = "http://music.163.com/weapi/v1/resource/comments/R_SO_4_%d/?csrf_token=" %int(song_id)360 hot_comments_list =self.get_hot_comments(url)361 if(os.path.exists(singer_name)):362 self.save_to_file(hot_comments_list,u"%s/hotcomments.txt" %singer_name)363 else:364 os.mkdir(singer_name)365 self.save_to_file(hot_comments_list,u"%s/hotcomments.txt" %singer_name)366 print(u"成功寫入%s的%d首歌曲!" %(singer_name,len(song_ids)))367

368 #在一張圖中繪制多個歌曲的評論分布

369 #song_names_list 為多個歌曲名字的清單

370 #settings 為含有字典元素的清單,每個字典含有每個子圖的配置項

371 defsub_plot_comments(self,song_names_list,settings,row,col):372 n = len(song_names_list) #歌曲總數

373 row =row374 col =col375 for i inrange(n):376 plt.subplot(row,col,i+1)377 if(settings[i]['date_type'] == '%Y-%m-%d'):378 count_file_name = u"%s/comments_num_by_Ymd.txt" %song_names_list[i]379 else:380 count_file_name = u"%s/comments_num_by_Ym.txt" %song_names_list[i]381 date_type = settings[i]['date_type']382 min_date_Ym = settings[i]['min_date_Ym']383 max_date_Ym = settings[i]['max_date_Ym']384 min_date_Ymd = settings[i]['min_date_Ymd']385 max_date_Ymd = settings[i]['max_date_Ymd']386 x_date,y_count = self.get_xdate_ycount(count_file_name,min_date_Ym = min_date_Ym,max_date_Ym =max_date_Ym,387 min_date_Ymd = min_date_Ymd,max_date_Ymd = max_date_Ymd,date_type =date_type)388

389 x = [datetime.strptime(d, date_type).date() for d inx_date]390 #配置橫坐标為日期類型

391 plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%s' %date_type))392 if(date_type == '%Y-%m-%d'):393 plt.gca().xaxis.set_major_locator(mdates.DayLocator())394 else:395 plt.gca().xaxis.set_major_locator(mdates.MonthLocator())396 plot_type = settings[i]['plot_type']397 if(plot_type == 'plot'):398 plt.plot(x,y_count,color = settings[i]['color'])399 elif(plot_type == 'bar'):400 plt.bar(x,y_count,width=settings[i]['bar_width'],color = settings[i]['color'])401 else:402 plt.scatter(x,y_count,color = settings[i]['color'])403 plt.gcf().autofmt_xdate(rotation=settings[i]['rotation']) #自動旋轉日期标記

404 plt.title(u"網易雲音樂歌曲《" + song_names_list[i] + u"》" + u"評論數目分布(%s到%s)" %(x[0],x[-1]),fontsize = settings[i]['fontsize'])405 plt.xlabel(u"日期")406 plt.ylabel(u"數目")407 plt.xticks(pd.date_range(x[0],x[-1],freq="%s" % settings[i]['time_distance'])) #設定日期間隔

408 plt.subplots_adjust(left=0.2, bottom=0.2, right=0.8, top=0.8,hspace=1.2,wspace=0.3)409 plt.show()410 #得到評論清單

411 defget_comments_list(self,filename):412 with codecs.open(filename,"r",encoding='utf-8') as f:413 lists =f.readlines()414 comments_list =[]415 for comment inlists:416 if(re.match(r"^\d.*",comment)):417 try:418 comments_list.append(comment.split(" ",5)[5].replace("\n",""))419 exceptException,e:420 print(e)421 else:422 comments_list.append(comment)423 returncomments_list424

425 #繪制詞雲

426 #pic_path 為詞雲背景圖檔位址

427 #singer_name 為 False 時,則讀取歌曲評論檔案,否則讀取歌手熱評檔案

428 #isFullComments = True 時,讀取全部評論,否則隻讀取熱評

429 def draw_wordcloud(self,song_name,singer_name,pic_path = "JayChou.jpg",isFullComments =True):430 if singer_name ==False:431 if isFullComments ==True:432 filename = u"%s/%s.txt" % (song_name,song_name) #全部評論

433 else:434 filename = u"%s/hotcomments.txt" % song_name #一首歌的熱評

435 else:436 filename = u"%s/hotcomments.txt" %singer_name437 comments_list =self.get_comments_list(filename)438 comments_text = "".join(comments_list)439 cut_text = " ".join(jieba.cut(comments_text)) #将jieba分詞得到的關鍵詞用空格連接配接成為字元串

440 d = path.dirname(__file__) #目前檔案檔案夾所在目錄

441 color_mask = imread(pic_path) #讀取背景圖檔

442 cloud = WordCloud(font_path=path.join(d,'simsun.ttc'),background_color='white',mask=color_mask,max_words=2000,max_font_size=40)443 word_cloud = cloud.generate(cut_text) #産生詞雲

444 if singer_name ==False:445 name =song_name446 else:447 name =singer_name448 word_cloud.to_file(u"%s/%s.jpg" %(name,name))449 print(u"成功生成%s.jpg" %name)450

451 #對一首歌曲繪制其某一年某幾個月的評論分布

452 #date_lists 為要繪制的月份

453 defsub_plot_months(self,song_name,DateLists,settings,row,col):454 n =len(DateLists)455 row = row #行

456 col = col #列

457 filename = u"%s/comments_num_by_Ymd.txt" %song_name458 date_lists =[]459 y_count =[]460 with codecs.open(filename,"r",encoding = 'utf-8') as f:461 lists =f.readlines()462 del lists[0] #删除頭部資訊

463 for content inlists:464 date_lists.append(content.split(" ")[0]) #添加日期資訊

465 y_count.append(int(content.split(" ")[1])) #添加數量資訊

466 for i inrange(n):467 plt.subplot(row,col,i+1)468 x_date = [date for date in date_lists if re.match(r"%s" %DateLists[i],date)]469 y = [y_count[j] for j in range(len(y_count)) if re.match(r"%s" %DateLists[i],date_lists[j])]470 x = [datetime.strptime(d, "%Y-%m-%d").date() for d inx_date]471 plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))472 plot_type = settings[i]['plot_type']473 if(plot_type == 'plot'):474 plt.plot(x,y,color = settings[i]['color'])475 elif(plot_type == 'bar'):476 plt.bar(x,y,width=settings[i]['bar_width'],color = settings[i]['color'])477 else:478 plt.scatter(x,y,color = settings[i]['color'])479 plt.gcf().autofmt_xdate(rotation=settings[i]['rotation']) #自動旋轉日期标記

480 plt.title(u"《%s》%s到%s" % (song_name,x[0],x[-1]),fontsize = settings[i]['fontsize'])481 plt.xlabel(u"日期")482 plt.ylabel(u"評論數目")483 plt.xticks(pd.date_range(x[0],x[-1],freq="%s" % settings[i]['time_distance'])) #設定日期間隔

484 plt.subplots_adjust(left=0.09, bottom=0.27, right=0.89, top=0.83,hspace=0.35,wspace=0.35)485 plt.show()486

487 #繪制一首歌曲評論者相關資訊的分布

488 defsub_plot_commenters_info(self,song_or_singer_name):489 file_name = u"%s/commenters_info.txt" %song_or_singer_name490 with codecs.open(file_name,'r',encoding='utf-8') as f:491 info_lists =f.readlines()492 del info_lists[0] #删除頭部資訊

493 event_count_list = [] #動态總數

494 follow_count_list = [] #關注總數

495 fan_count_list = [] #粉絲總數

496 area_list = [] #所在地區

497 age_list = [] #年齡

498 listen_songs_num_list = [] #累計聽歌數目

499 for info ininfo_lists:500 info.replace("\n","")501 event_count_list.append(int(info.split(" ")[1]))502 follow_count_list.append(int(info.split(" ")[2]))503 fan_count_list.append(int(info.split(" ")[3]))504 area_res= re.search(re.compile(u'.*\d (.+?-.+?) .*?|.*(未知地區).*'),info)505 if(area_res):506 if(area_res.group(1)):507 area_list.append(area_res.group(1))508 age_list.append(info.split(" ")[-2])509 listen_songs_num_list.append(int(info.split(" ")[-1]))510 event_count =[0,0,0,0]511 follow_count =[0,0,0,0,0]512 fan_count =[0,0,0,0,0]513 listen_songs_num =[0,0,0,0]514 area_count =[0,0,0,0,0,0]515 age_count =[0,0,0,0,0]516 for content inevent_count_list:517 if(content <= 10):518 event_count[0] += 1

519 elif(content <= 50):520 event_count[1] += 1

521 elif(content <= 100):522 event_count[2] += 1

523 else:524 event_count[3] += 1

525 for content infollow_count_list:526 if(content < 10):527 follow_count[0] += 1

528 elif(content < 30):529 follow_count[1] += 1

530 elif(content < 50):531 follow_count[2] += 1

532 elif(content < 100):533 follow_count[3] += 1

534 else:535 follow_count[4] += 1

536 for content infan_count_list:537 if(content < 10):538 fan_count[0] += 1

539 elif(content < 100):540 fan_count[1] += 1

541 elif(content < 1000):542 fan_count[2] += 1

543 elif(content < 10000):544 fan_count[3] += 1

545 else:546 follow_count[4] += 1

547 area_no_repeat_list = list(set(area_list)) #去除重複

548 area_tuple = [(area,area_list.count(area)) for area inarea_no_repeat_list]549 area_tuple.sort(key= lambda x:x[1],reverse=True) #從高到低排列

550 for i in range(5): #取出排名前4的地區

551 area_count[i] = area_tuple[i][1]552 area_count[5] = sum([x[1] for x in area_tuple[5:]]) #前5名之外的全部地區數量

553 area_labels = [x[0] for x in area_tuple[0:5]] #前5個地區的名字

554 area_labels.append(u"其他地區")555 age_no_repeat_list = list(set(age_list)) #去除重複

556 age_info = [age_list.count(age) for age inage_no_repeat_list]557 for index,age_ inenumerate(age_no_repeat_list):558 if(age_ != u"未知年齡"): #排除未知年齡

559 if(int(age_) <= 17):560 age_count[0] += age_info[index] #00後

561 elif(int(age_)<=22): #95後

562 age_count[1] +=age_info[index]563 elif(int(age_)<=27): #90後

564 age_count[2] +=age_info[index]565 elif(int(age_)<=37): #80後

566 age_count[3] +=age_info[index]567 else:568 age_count[4] += age_info[index] #80前

569 age_labels = [u"00後",u"95後",u"90後",u"80後",u"80前"]570

571 for content inlisten_songs_num_list:572 if(content < 100):573 listen_songs_num[0] += 1

574 elif(content < 1000):575 listen_songs_num[1] += 1

576 elif(content < 10000):577 listen_songs_num[2] += 1

578 else:579 listen_songs_num[3] += 1

580 for i in range(6):581 if(i ==0):582 title = u"%s:評論者分布" %song_or_singer_name583 labels = [u"0-10",u"10-50",u"50-100",u"100以上"]584 colors = ["red","blue","yellow","green"]585 x =event_count586 plt.subplot(2,3,i+1)587 plt.pie(x,colors=colors,labels=labels,autopct="%1.1f%%")588 plt.title(title)589 #plt.legend(labels)

590 elif(i == 1):591 title = u"%s:評論者分布" %song_or_singer_name592 labels = [u"0-10",u"10-30",u"30-50",u"50-100",u"100以上"]593 colors = ["red","blue","yellow","green","white"]594 x =follow_count595 plt.subplot(2,3,i+1)596 plt.pie(x,colors=colors,labels=labels,autopct="%1.1f%%")597 plt.title(title)598 #plt.legend(labels)

599 elif(i == 2):600 title = u"%s:評論者分布" %song_or_singer_name601 labels = [u"0-10",u"10-100",u"100-1000",u"1000-10000",u"10000以上"]602 colors = ["red","blue","yellow","green","white"]603 x =fan_count604 plt.subplot(2,3,i+1)605 plt.pie(x,colors=colors,labels=labels,autopct="%1.1f%%")606 plt.title(title)607 #plt.legend(labels)

608 elif(i == 3):609 title = u"%s:評論者分布" %song_or_singer_name610 colors = ["red","blue","yellow","green","white","purple"]611 x =area_count612 plt.subplot(2,3,i+1)613 plt.pie(x,colors=colors,labels=area_labels,autopct="%1.1f%%")614 plt.title(title)615 #plt.legend(area_labels,loc='upper center', bbox_to_anchor=(0.1,0.9),ncol=1,fancybox=True,shadow=True)

616 elif(i == 4):617 title = u"%s:評論者分布" %song_or_singer_name618 colors = ["red","blue","yellow","green","white"]619 x =age_count620 plt.subplot(2,3,i+1)621 plt.pie(x,colors=colors,labels=age_labels,autopct="%1.1f%%")622 plt.title(title)623 #plt.legend(age_labels)

624 else:625 title = u"%s:評論者分布" %song_or_singer_name626 labels = [u"0-100",u"100-1000",u"1000-10000",u"10000以上"]627 colors = ["red","blue","yellow","green"]628 x =listen_songs_num629 plt.subplot(2,3,i+1)630 plt.pie(x,colors=colors,labels=labels,autopct="%1.1f%%")631 plt.title(title)632 #plt.legend(labels)

633 plt.tight_layout()634 plt.show()635

636 #sub_plot_months 測試

637 defsub_plot_months_test(self):638 song_name = u"越長大越孤單"

639 row = 3

640 col = 4

641 settings_dict ={642 "plot_type":"plot",643 "color":"g",644 "bar_width":0.8,645 "fontsize":10,646 "rotation":50,647 "time_distance":"5D"

648 }649 settings =[]650 DateLists = ['2016-04','2016-05','2016-06','2016-07','2016-08','2016-09','2016-10','2016-11','2016-12','2017-01','2017-02','2017-03']651 for i inrange(len(DateLists)):652 settings.append(settings_dict)653 self.sub_plot_months(song_name,DateLists,settings,row=row,col=col)654

655 #繪制subplot 測試

656 defsubplot_test(self):657 song_names_list = [u"七裡香",u"不要再孤單",u"All Too Well",u"剛好遇見你"]658 settings_dict = {"date_type":"%Y-%m-%d",659 "plot_type":"bar",660 "fontsize":12,661 "color":"r",662 "bar_width":0.4,663 "rotation":50,664 "time_distance":"3D",665 "min_date_Ymd":"2017-03-01",666 "max_date_Ymd":"2017-12-31",667 "min_date_Ym":"2013-01",668 "max_date_Ym":"2017-12"

669 }670 settings =[]671 for i inrange(len(song_names_list)):672 settings.append(settings_dict)673 row = 2

674 col = 2

675 self.sub_plot_comments(song_names_list,settings,row=row,col =col)676

677 #plot_comments 函數測試

678 defplot_comments_test(self):679 song_name = u"我從崖邊跌落"

680 settings ={681 "comment_type":False,682 "date_type":"%Y-%m-%d",683 "plot_type":"plot",684 "bar_width":0.8,685 "rotation":20,686 "color":"purple",687 "pie_colors":["blue","red","coral","green","yellow"],688 "auto_pct":'%1.1f%%',689 "expl" :[0,0,0.1,0.3], #離開圓心的距離

690 "time_distance":"3D",691 "min_date_Ymd":"2013-12-01",692 "max_date_Ymd":"2017-12-31",693 "min_date_Ym":"2013-01",694 "max_date_Ym":"2017-12"

695 }696 self.plot_comments(song_name,settings)697

698

699 if __name__ == '__main__':700 Processor =NetCloudProcessor()701 Processor.plot_comments_test()

注:上面的代碼無法直接運作,因為繪圖時缺少必要的檔案(即函數create_all_necessary_files産生的與歌曲名字或者歌手同名的檔案夾),大家可以去百度雲下載下傳我已經抓取過的資料(位址:http://pan.baidu.com/s/1slS55gx)或者自行抓取。有任何問題,歡迎大家的指教。