黃渤首次導演的電影《一出好戲》自8月10日在全國上映,至今已有10天,其主演陣容強大,相信許多觀衆也都是衝着明星們去的。
目前《一出好戲》在貓眼上已經得到近60萬個評價,評分爲8.2分,票房已破10億。html
做者本人(湯小洋 )今天也走進了電影院,對這部電影作了親身的觀看,看完後的感受是有些許失落的,本覺得是喜劇片,結果發現笑點通常,從搞笑的角度來看,不如《西虹市首富》,影片更多的是反映人類本性的一部電影,不該當作喜劇片來看,影片中展示的人與人之間的關係卻是值得咱們去深思。python
今天就跟着 湯老師 一塊兒來揭祕影片《一出好戲》,看看「這齣好戲」到底如何?json
咱們將使用Python抓取貓眼近10萬條評論數據,並對獲取到的數據進行分析,看看觀衆對這部電影的評價究竟如何?bash
整個數據分析的過程分爲四步:服務器
本次獲取的是貓眼APP的評論數據,如圖所示:app
經過分析發現貓眼APP的評論數據接口爲:echarts
http://m.maoyan.com/mmdb/comments/movie/1203084.json?_v_=yes&offset=0&startTime=2018-08-18%2022%3A25%3A03
dom
經過對評論數據進行分析,獲得以下信息:ide
返回的是json格式數據函數
1203084表示電影的專屬id;offset表示偏移量;startTime表示獲取評論的起始時間,從該時間向前取數據,即獲取最新的評論
cmts表示評論,每次獲取15條,offset偏移量是指每次獲取評論時的起始索引,向後取15條
hcmts表示熱門評論前10條
這裏先定義一個函數,用來根據指定url獲取數據,且只能獲取到指定的日期向前獲取到15條評論數據
# coding=utf-8 __author__ = '湯小洋' from urllib import request import json import time from datetime import datetime from datetime import timedelta # 獲取數據,根據url獲取 def get_data(url): headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36' } req = request.Request(url, headers=headers) response = request.urlopen(req) if response.getcode() == 200: return response.read() return None if __name__ == '__main__': html = get_data('http://m.maoyan.com/mmdb/comments/movie/1203084.json?_v_=yes&offset=0&startTime=2018-07-28%2022%3A25%3A03') print(html)
對獲取的數據進行處理,轉換爲json
# 處理數據 def parse_data(html): data = json.loads(html)['cmts'] # 將str轉換爲json comments = [] for item in data: comment = { 'id': item['id'], 'nickName': item['nickName'], 'cityName': item['cityName'] if 'cityName' in item else '', # 處理cityName不存在的狀況 'content': item['content'].replace('\n', ' ', 10), # 處理評論內容換行的狀況 'score': item['score'], 'startTime': item['startTime'] } comments.append(comment) return comments if __name__ == '__main__': html = get_data('http://m.maoyan.com/mmdb/comments/movie/1200486.json?_v_=yes&offset=0&startTime=2018-07-28%2022%3A25%3A03') comments = parse_data(html) print(comments)
爲了可以獲取到全部評論數據,方法是:從當前時間開始,向前獲取數據,根據url每次獲取15條,而後獲得末尾評論的時間,從該時間繼續向前獲取數據,直到影片上映日期(2018-08-10)爲止,獲取這之間的全部數據。
# 存儲數據,存儲到文本文件 def save_to_txt(): start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 獲取當前時間,從當前時間向前獲取 end_time = '2018-08-10 00:00:00' while start_time > end_time: url = 'http://m.maoyan.com/mmdb/comments/movie/1203084.json?_v_=yes&offset=0&startTime=' + start_time.replace(' ', '%20') html = None ''' 問題:當請求過於頻繁時,服務器會拒絕鏈接,其實是服務器的反爬蟲策略 解決:1.在每一個請求間增長延時0.1秒,儘可能減小請求被拒絕 2.若是被拒絕,則0.5秒後重試 ''' try: html = get_data(url) except Exception as e: time.sleep(0.5) html = get_data(url) else: time.sleep(0.1) comments = parse_data(html) print(comments) start_time = comments[14]['startTime'] # 得到末尾評論的時間 start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S') + timedelta(seconds=-1) # 轉換爲datetime類型,減1秒,避免獲取到重複數據 start_time = datetime.strftime(start_time, '%Y-%m-%d %H:%M:%S') # 轉換爲str for item in comments: with open('comments.txt', 'a', encoding='utf-8') as f: f.write(str(item['id'])+','+item['nickName'] + ',' + item['cityName'] + ',' + item['content'] + ',' + str(item['score'])+ ',' + item['startTime'] + '\n') if __name__ == '__main__': # html = get_data('http://m.maoyan.com/mmdb/comments/movie/1200486.json?_v_=yes&offset=0&startTime=2018-07-28%2022%3A25%3A03') # comments = parse_data(html) # print(comments) save_to_txt()
有兩點須要說明:
這裏使用的是pyecharts,pyecharts是一個用於生成Echarts圖表的類庫,便於在Python中根據數據生成可視化的圖表。
Echarts是百度開源的一個數據可視化JS庫,主要用於數據可視化。
# 安裝pyecharts pip install pyecharts
pyecharts v0.3.2之後,pyecharts 將再也不自帶地圖 js 文件。如用戶須要用到地圖圖表,可自行安裝對應的地圖文件包。
# 安裝地圖文件包 pip install echarts-china-provinces-pypkg # 中國省、市、縣、區地圖 pip install echarts-china-cities-pypkg pip install echarts-china-counties-pypkg pip install echarts-china-misc-pypkg pip install echarts-countries-pypkg # 全球國家地圖 pip install echarts-united-kingdom-pypkg
代碼實現
# coding=utf-8 __author__ = '湯小洋' # 導入Style類,用於定義樣式風格 from pyecharts import Style # 導入Geo組件,用於生成地理座標類圖 from pyecharts import Geo import json # 導入Geo組件,用於生成柱狀圖 from pyecharts import Bar # 導入Counter類,用於統計值出現的次數 from collections import Counter # 數據可視化 def render(): # 獲取評論中全部城市 cities = [] with open('comments.txt', mode='r', encoding='utf-8') as f: rows = f.readlines() for row in rows: city = row.split(',')[2] if city != '': # 去掉城市名爲空的值 cities.append(city) # 對城市數據和座標文件中的地名進行處理 handle(cities) # 統計每一個城市出現的次數 # data = [] # for city in set(cities): # data.append((city, cities.count(city))) data = Counter(cities).most_common() # 使用Counter類統計出現的次數,並轉換爲元組列表 # print(data) # 定義樣式 style = Style( title_color='#fff', title_pos='center', width=1200, height=600, background_color='#404a59' ) # 根據城市數據生成地理座標圖 geo = Geo('《一出好戲》粉絲位置分佈', '數據來源:貓眼-湯小洋採集', **style.init_style) attr, value = geo.cast(data) geo.add('', attr, value, visual_range=[0, 3500], visual_text_color='#fff', symbol_size=15, is_visualmap=True, is_piecewise=True, visual_split_number=10) geo.render('粉絲位置分佈-地理座標圖.html') # 根據城市數據生成柱狀圖 data_top20 = Counter(cities).most_common(20) # 返回出現次數最多的20條 bar = Bar('《一出好戲》粉絲來源排行TOP20', '數據來源:貓眼-湯小洋採集', title_pos='center', width=1200, height=600) attr, value = bar.cast(data_top20) bar.add('', attr, value, is_visualmap=True, visual_range=[0, 3500], visual_text_color='#fff', is_more_utils=True, is_label_show=True) bar.render('粉絲來源排行-柱狀圖.html')
出現的問題:
報錯:ValueError: No coordinate is specified for xxx(地名)
緣由:pyecharts的座標文件中沒有該地名,其實是名稱不一致致使的,如數據中地名爲'達州',而座標文件中爲'達州市'
座標文件所在路徑:項目/venv/lib/python3.6/site-packages/pyecharts/datasets/city_coordinates.json
{ "達州市": [ 107.5, 31.22 ], "達州": [ 107.5, 31.22 ], }
不過因爲要修改的地名太多,上面的方法實在是麻煩,因此我定義了一個函數,用來處理地名數據找不到的問題
# 處理地名數據,解決座標文件中找不到地名的問題 def handle(cities): # print(len(cities), len(set(cities))) # 獲取座標文件中全部地名 data = None with open( '/Users/wangbo/PycharmProjects/python-spider/venv/lib/python3.6/site-packages/pyecharts/datasets/city_coordinates.json', mode='r', encoding='utf-8') as f: data = json.loads(f.read()) # 將str轉換爲json # 循環判斷處理 data_new = data.copy() # 拷貝全部地名數據 for city in set(cities): # 使用set去重 # 處理地名爲空的數據 if city == '': while city in cities: cities.remove(city) count = 0 for k in data.keys(): count += 1 if k == city: break if k.startswith(city): # 處理簡寫的地名,如 達州市 簡寫爲 達州 # print(k, city) data_new[city] = data[k] break if k.startswith(city[0:-1]) and len(city) >= 3: # 處理行政變動的地名,如縣改區 或 縣改市等 data_new[city] = data[k] break # 處理不存在的地名 if count == len(data): while city in cities: cities.remove(city) # print(len(data), len(data_new)) # 寫入覆蓋座標文件 with open( '/Users/wangbo/PycharmProjects/python-spider/venv/lib/python3.6/site-packages/pyecharts/datasets/city_coordinates.json', mode='w', encoding='utf-8') as f: f.write(json.dumps(data_new, ensure_ascii=False)) # 將json轉換爲str
可視化結果:
粉絲人羣主要集中在沿海一帶
從上圖能夠看出,《一出好戲》的觀影人羣主要集中在沿海一帶,這些地方經濟相對發達,城市人口基數龐大,極多的熒幕數量和座位、極高密度的排片場次,讓觀衆便捷觀影,活躍的觀衆評論也多,天然也就成爲票房的主要貢獻者。
粉絲來源排名前20的城市依次爲:北京、深圳、上海、成都、武漢、廣州、西安、鄭州、重慶、南京、天津、瀋陽、長沙、東莞、哈爾濱、青島、杭州、合肥、大連、蘇州
電影消費是城市消費的一部分,從某種角度來看,能夠做爲考察一個城市購買力的指標。這些城市在近年的GDP排行中大都居上游,消費水平較高。
jieba是一個基於Python的分詞庫,完美支持中文分詞,功能強大
pip install jieba
Matplotlib是一個Python的2D繪圖庫,可以生成高質量的圖形,能夠快速生成繪圖、直方圖、功率譜、柱狀圖、偏差圖、散點圖等
pip install matplotlib
wordcloud是一個基於Python的詞雲生成類庫,能夠生成詞雲圖
pip install wordcloud
代碼實現:
# coding=utf-8 __author__ = '湯小洋' # 導入jieba模塊,用於中文分詞 import jieba # 導入matplotlib,用於生成2D圖形 import matplotlib.pyplot as plt # 導入wordcount,用於製做詞雲圖 from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator # 獲取全部評論 comments = [] with open('comments.txt', mode='r', encoding='utf-8') as f: rows = f.readlines() for row in rows: comment = row.split(',')[3] if comment != '': comments.append(comment) # 設置分詞 comment_after_split = jieba.cut(str(comments), cut_all=False) # 非全模式分詞,cut_all=false words = ' '.join(comment_after_split) # 以空格進行拼接 # print(words) # 設置屏蔽詞 stopwords = STOPWORDS.copy() stopwords.add('電影') stopwords.add('一部') stopwords.add('一個') stopwords.add('沒有') stopwords.add('什麼') stopwords.add('有點') stopwords.add('這部') stopwords.add('這個') stopwords.add('不是') stopwords.add('真的') stopwords.add('感受') stopwords.add('以爲') stopwords.add('仍是') stopwords.add('可是') stopwords.add('就是') stopwords.add('一出') stopwords.add('好戲') # 導入背景圖 bg_image = plt.imread('bg.jpg') # 設置詞雲參數,參數分別表示:畫布寬高、背景顏色、背景圖形狀、字體、屏蔽詞、最大詞的字體大小 wc = WordCloud(width=1024, height=768, background_color='white', mask=bg_image, font_path='STKAITI.TTF', stopwords=stopwords, max_font_size=400, random_state=50) # 將分詞後數據傳入雲圖 wc.generate_from_text(words) plt.imshow(wc) plt.axis('off') # 不顯示座標軸 plt.show() # 保存結果到本地 wc.to_file('詞雲圖.jpg')
可視化結果:
整體評價很不錯
對評論數據進行分詞後製做以下詞雲圖:
從詞雲圖中能夠看到:
代碼實現:
# coding=utf-8 __author__ = '湯小洋' # 導入Pie組件,用於生成餅圖 from pyecharts import Pie # 獲取評論中全部評分 rates = [] with open('comments.txt', mode='r', encoding='utf-8') as f: rows = f.readlines() for row in rows: rates.append(row.split(',')[4]) # print(rates) # 定義星級,並統計各星級評分數量 attr = ['五星', '四星', '三星', '二星', '一星'] value = [ rates.count('5') + rates.count('4.5'), rates.count('4') + rates.count('3.5'), rates.count('3') + rates.count('2.5'), rates.count('2') + rates.count('1.5'), rates.count('1') + rates.count('0.5') ] # print(value) pie = Pie('《一出好戲》評分星級比例', title_pos='center', width=900) pie.add('7-17', attr, value, center=[75, 50], is_random=True, radius=[30, 75], rosetype='area', is_legend_show=False, is_label_show=True) pie.render('評分.html')
可視化結果:
4、五星級影評合計高達83%
從圖中能夠看出,五星比例接近62%,四星比例爲21%,二者合計高達83%,可見口碑仍是至關不錯的,一星佔比不足6%
《一出好戲》做爲黃渤第一次執導的做品,在拍攝過程當中導演渤哥對本身的要求也是很嚴格的,因此有這樣的成績,也是理所固然。
附:今天看電影的票根 ^_^
爬蟲案例對應的視頻課程:http://edu.51cto.com/course/14870.html