以前已經用python獲取了網易雲音樂的評論數據,下一步的工做就是數據分析了。通常數據分析無非是採用(統計)數字、圖或者表的形式來展示數據之中隱含的信息。其中圖和表顯然是最直觀的了。因此這裏我使用可視化的方法即用圖形來展現從評論中挖掘到的各類信息。javascript
可視化的工具備不少,好比常見的有excel還有一些專門的繪圖軟件,各個編程語言固然也有不少可視化的包或者庫,好比統計上使用不少的R語言就有不少可視化的庫,我最喜歡的就是ggplot2了,我使用R語言主要用於數據的清洗以及可視化,其豐富的包(package)大大簡化了數據分析的工做量,並且能夠繪製很是複雜、精美的圖表,之後有機會能夠給你們專門介紹一下。python中可視化的庫也不少,最著名的的莫過於matplotlib了,這是一個面向對象的繪圖庫,不少方面的用法和matlab相似(從matlab的繪圖風格借鑑而來),因爲我之前使用過一段時間的matlab,因此上手仍是比較快,其餘的還有seaborn(聽說是對matplotlib的改進和封裝,使用起來更加方便,沒用過,有時間再研究下)、pygraph等。可是使用最普遍的仍是matplotlib。javascript有Echarts(百度的)等,這個我還沒接觸過,是一個能夠網頁上進行可視化的函數庫,聽說很棒。其餘的固然還有特別多,這裏我就不一一列舉了。有興趣的小夥伴能夠自行去查閱資料。這裏我決定使用matplotlib,主要是由於最近主要接觸的就是python,可是數據可視化方面的庫用的很少,恰好能夠拿此次的數據來練練手,其次我接觸過matlab,相信對matplotlib入手會更快一點。html
此次主要分析的有如下幾個方面:一、一首歌曲評論數目隨時間變化的趨勢,好比天天的評論數變化,每個月的評論數變化等等。二、一首歌曲點贊數目分佈的狀況,好比0-10贊有多少個,佔多少比例,1000贊以上佔多大比例等等。三、熱門評論詞雲的製做,主要想經過詞雲,將文本挖掘的結果可視化,能夠看出哪些是高頻詞彙等。四、一首歌曲評論者的基本信息的狀況展現,好比評論者的地區分佈,年齡分佈、累計聽歌數目分佈、動態分佈、粉絲數分佈等等。經過這些信息,能夠直觀看出一首歌曲被哪些地區、哪些年齡段的人所喜好,以及聽歌的人具備什麼的特色等等。java
好了,廢話很少說了,直接上圖吧。python
首先來看一些歌曲評論數隨時間的變化。正則表達式
圖 1編程
圖 2json
圖 3api
圖 4服務器
圖 5網絡
上面的5張圖我分別選取了5首不一樣的歌曲,有華語歌曲也有英文歌曲,有的起止時間很長(從13年就開始),也有的起止時間很短(從最近幾個月纔開始)。總的來講能夠分爲兩種模式,一種是開始一段時間評論數不多,後來逐漸呈現爆發式地增加,前面三首歌《同桌的你》、《七里香》、《All Too Well》都是這種模式,然後面兩首歌《不要再孤單》、《stay》則是剛好相反,歌曲剛剛出來的那幾天評論數猛增,後面評論數逐漸降低,以後趨於平穩。經過分析,其實也很好理解,第一種模式的歌曲,每每都是早期曲庫中就存在的歌曲(也能夠稱之爲「老歌」),那個時候網易雲音樂纔剛剛出來,用戶數目還不多,因此這些歌曲天天的評論數不多(沒記錯的話網易雲應該是十二、13年左右纔出來的吧),後來網易雲一路走紅,直至如今號稱有2億用戶,因爲用戶基數大,因此這些經典的老歌天然評論數猛增了,能夠想見,這種評論爆發式增加和網易雲音樂用戶的增加趨勢應該是基本一致的。而至於第二種模式,出現這種模式的歌曲每每都是比較新的歌曲,並且每每伴隨着影視劇的火熱忽然火起來,好比《不要再孤單》就是電影的主題曲,電影剛上映的那段時間,歌隨影熱,評論數天然爆發式增加,後來這段熱潮過去了,評論數天然就降下來了(固然這種歌曲應該以網絡歌曲居多,只是某一段時間特別火,不黑,我以爲真正的經典評論數應該不會大起大落,好比《晴天》、《see you again》等)。固然我只是分析了兩種典型的評論隨時間變化的模式,實際確定不止這兩種模式,你們能夠自行去探索。
圖 6
圖 7
前面5張圖都是使用折線圖來展現的,圖6使用的是柱狀圖。咱們來看下圖7,圖7展現的最近一段時間比較火的李玉剛的歌曲《恰好碰見你》的評論數隨時間的分佈,讓人感到奇怪的是,中間從大約1月23日到3月24日的天天的評論量居然是0!這怎麼可能呢?難道真的是這樣麼?固然不是。我解釋一下緣由,這是程序自己的bug,我在抓取評論數過10W的歌曲的過程當中發現,我最終看似抓取了所有的評論,可是實際上在去除重複以後,我只獲得了部分的數據,每次大概只能獲得2W到3W左右的數據,其餘的數據就缺失了。至今我也沒能解決這個問題,我的以爲是服務器作了什麼限制,若是有朋友知道該怎麼解決這個問題,望能不吝賜教!
除了能夠從宏觀上看一首歌曲天天或者每個月的評論數分佈以外,咱們還能夠將不一樣的歌曲評論隨時間變化放到一塊兒對比,或者將一首歌曲每個月的評論數放在一塊兒進行對比。
圖 8
圖 9
圖 10
圖 8 就展現了四首不一樣的歌曲在某一個時間段評論數目隨時間變化,圖9展現了《同桌的你》從16年8月到17年3月這8個月的時間裏每個月評論數的分佈狀況,圖10則是《越長大越孤單》從16年4月到17年3月這12個月的每個月評論數分佈。其實,這種圖形很容易作出,由於我已經將繪圖函數作了封裝,能夠設置自定義參數字典,來生成本身想要的不一樣的圖形,也能夠選擇繪製圖形的種類、顏色以及繪製的時間段、時間間隔等,在文末我會說明這一點。
接下來,看一下評論點贊數目的分佈狀況。
圖 11
圖 12
圖10和圖11展現的點贊數目分佈我去除了10贊如下的,緣由是我發現一首歌曲絕大部分的點贊數目(超過99%)都是10贊如下的,這也與咱們的常識相一致,因此爲了方便我就直接去除了。經過上面的兩張圖咱們能夠看出,紅色區域面積最大,即100贊到1000佔據了所有10贊以上評論的絕大部分,其次是10到100贊,而後是1000贊到10000贊,最少的是1W贊以上,我發現大部分歌曲基本都是呈現這個規律,因此只在這裏簡單提一下,就不作詳細分析了。
接下來分析歌曲熱門評論的詞雲展現,其實python的詞雲,我以前的一篇隨筆也有提到過,使用wordcloud(繪製詞雲)和jieba(中文分詞)便可。這裏就不細說了。直接看圖吧。
圖 13 《不要再孤單》詞雲
圖 14 周杰倫熱門50首歌曲熱門評論詞雲
圖 15 Taylor Swift 熱門歌曲熱門評論詞雲
從以上的詞雲中仍是能夠看出一首歌曲或者一位歌手,評論區中出現頻率最高的是哪些詞的。好比杰倫 的熱門評論中反覆出現的詞就有周杰倫、青春、喜歡、女友、故事等等,一股青春懷舊風撲面而來啊。哈哈,其餘有意思的你們本身去分析吧。
最後,我想重點來分析一下,一首歌曲的評論者我的信息具備什麼樣的特色。我將這些特色放在一張圖中,經過多張餅圖來展現了,見下。
圖 16
圖 17
圖 18
圖 19
圖 20
圖 21
圖 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 : 919987476@qq.com 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/140287795 14 來源:知乎 15 ''' 16 from Crypto.Cipher import AES 17 import base64 18 import requests 19 import json 20 import codecs 21 import time 22 import os 23 24 # 定義抓取評論類NetCloudCrawl 25 class NetCloudCrawl(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; P_INFO=m15527594439@163.com|1489375076|1|study|00&99|null&null&null#hub&420100#10#0#0|155439&1|study_client|15527594439@163.com; 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_param 56 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 return h_encText 67 68 # 解密過程 69 def AES_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 return encrypt_text 76 77 # 得到評論json數據 78 def get_json(self,url, params, encSecKey): 79 data = { 80 "params": params, 81 "encSecKey": encSecKey 82 } 83 response = requests.post(url, headers=self.headers, data=data,proxies = self.proxies) 84 return response.content 85 86 # 抓取熱門評論,返回熱評列表 87 def get_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 in hot_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 return hot_comments_list 105 106 # 抓取某一首歌的所有評論 107 def get_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 return all_comments_list 136 137 138 # 將評論寫入文本文件 139 def save_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 def save_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_id 148 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 : 919987476@qq.com 6 # @File : NetCloud_comments_plot.py 7 ''' 8 @Description: 9 對抓取來的網易雲評論數據進行簡單的可視化分析 10 ''' 11 from NetCloud_spider3 import NetCloudCrawl 12 import requests 13 import matplotlib.dates as mdates 14 from pylab import * 15 mpl.rcParams['font.sans-serif'] = ['SimHei'] # 防止沒法顯示中文 16 import matplotlib.pyplot as plt 17 from datetime import datetime 18 import re 19 import time 20 import pandas as pd 21 import codecs 22 import jieba 23 from wordcloud import WordCloud 24 from scipy.misc import imread 25 from os import path 26 import os 27 28 29 class NetCloudProcessor(NetCloudCrawl): 30 # 讀取評論文本數據,返回一個列表,列表的每一個元素爲一個字典,字典中包含用戶id,評論內容等 31 def read_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 in comments_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 except Exception,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 def from_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 return real_date 67 68 69 # 統計相關數據寫入文本文件 70 def count_comments_info(self,comments_list,count_,song_name): 71 x_date_Ym = [] # 評論數按年月進行統計 72 x_date_Ymd = [] # 評論數按年月日進行統計 73 x_likedCount = [] # 點贊總數分佈 74 for i in range(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_ in x_date_Ym: 90 if date_ not in x_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_ in x_date_Ymd: 95 if date_ not in x_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 in x_likedCount: 100 if likedCount not in x_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 in enumerate(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 in enumerate(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 in enumerate(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 def get_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 del list_count[0] 126 x_date = [] 127 y_count = [] 128 for content in list_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 def plot_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_name 166 else: 167 count_file_name = u"%s/comments_num_by_Ym.txt" % song_name 168 else: 169 count_file_name = u"%s/likedCount.txt" % song_name 170 with open(count_file_name,'r') as f: 171 list_count = f.readlines() 172 del list_count[0] 173 if(comment_type): # 若是是評論 174 x_date = [] 175 y_count = [] 176 for content in list_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 in list_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 in x_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_count 227 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 def generate_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 def create_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_id 252 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 def get_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 in lists: 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 in enumerate(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).text 280 event_count_compile = re.compile(r'<strong id="event_count">(\d+?)</strong>') 281 event_count = re.search(event_count_compile,html).group(1) # 我的動態數目 282 follow_count_compile = re.compile(r'<strong id="follow_count">(\d+?)</strong>') 283 follow_count = re.search(follow_count_compile,html).group(1) # 關注人數 284 fan_count_compile = re.compile(r'<strong id="fan_count">(\d+?)</strong>') 285 fan_count = re.search(fan_count_compile,html).group(1) 286 location_compile = re.compile(u'<span>所在地區:(.+?)</span>') # 注意須要使用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'<div class="inf s-fc3 f-brk">我的介紹:(.*?)</div>') 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'<span.*?data-age="(\d+)">') 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'<h4>累積聽歌(\d+?)首</h4>') 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_id 310 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 except Exception,e: 320 print e 321 return commenters_info_list # 返回評論者用戶信息列表 322 323 # 保存評論者的信息 324 def save_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_name 329 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 in commenters_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 def get_songs_ids(self,singer_url): 347 ids_list = [] 348 html = requests.get(singer_url,headers = self.headers,proxies = self.proxies).text 349 re_pattern = re.compile(r'<a href="/song\?id=(\d+?)">.*?</a>') 350 ids = re.findall(re_pattern,html) 351 for id in ids: 352 ids_list.append(id) 353 return ids_list 354 # 獲得某個歌手全部歌曲的熱門評論 355 def get_singer_all_hot_comments(self,singer_name,singer_id): 356 singer_url = 'http://music.163.com/artist?id=%d' % singer_id 357 song_ids = self.get_songs_ids(singer_url) # 獲得歌手全部熱門歌曲id列表 358 for song_id in song_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 def sub_plot_comments(self,song_names_list,settings,row,col): 372 n = len(song_names_list) # 歌曲總數 373 row = row 374 col = col 375 for i in range(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 in x_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 def get_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 in lists: 416 if(re.match(r"^\d.*",comment)): 417 try: 418 comments_list.append(comment.split(" ",5)[5].replace("\n","")) 419 except Exception,e: 420 print(e) 421 else: 422 comments_list.append(comment) 423 return comments_list 424 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_name 437 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_name 446 else: 447 name = singer_name 448 word_cloud.to_file(u"%s/%s.jpg" % (name,name)) 449 print(u"成功生成%s.jpg" % name) 450 451 # 對一首歌曲繪製其某一年某幾個月的評論分佈 452 # date_lists 爲要繪製的月份 453 def sub_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_name 458 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 in lists: 464 date_lists.append(content.split(" ")[0]) # 添加日期信息 465 y_count.append(int(content.split(" ")[1])) # 添加數量信息 466 for i in range(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 in x_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 def sub_plot_commenters_info(self,song_or_singer_name): 489 file_name = u"%s/commenters_info.txt" % song_or_singer_name 490 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 in info_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 in event_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 in follow_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 in fan_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 in area_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 in age_no_repeat_list] 557 for index,age_ in enumerate(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 in listen_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_name 583 labels = [u"0-10",u"10-50",u"50-100",u"100以上"] 584 colors = ["red","blue","yellow","green"] 585 x = event_count 586 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_name 592 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_count 595 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_name 601 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_count 604 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_name 610 colors = ["red","blue","yellow","green","white","purple"] 611 x = area_count 612 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_name 618 colors = ["red","blue","yellow","green","white"] 619 x = age_count 620 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_name 626 labels = [u"0-100",u"100-1000",u"1000-10000",u"10000以上"] 627 colors = ["red","blue","yellow","green"] 628 x = listen_songs_num 629 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 def sub_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 in range(len(DateLists)): 652 settings.append(settings_dict) 653 self.sub_plot_months(song_name,DateLists,settings,row=row,col=col) 654 655 # 繪製subplot 測試 656 def subplot_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 in range(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 def plot_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)或者自行抓取。有任何問題,歡迎你們的指教。