《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

黃渤首次導演的電影《一出好戲》自8月10日在全國上映,至今已有10天,其主演陣容強大,相信許多觀衆也都是衝着明星們去的。
目前《一出好戲》在貓眼上已經獲得近60萬個評價,評分爲8.2分,票房已破10億。

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

作者本人(湯小洋 )今天也走進了電影院,對這部電影做了親身的觀看,看完後的感覺是有些許失落的,本以爲是喜劇片,結果發現笑點一般,從搞笑的角度來看,不如《西虹市首富》,影片更多的是反映人類本性的一部電影,不應當做喜劇片來看,影片中展現的人與人之間的關係倒是值得我們去深思。

今天就跟着 湯老師 一起來揭祕影片《一出好戲》,看看“這齣好戲”到底如何?

我們將使用Python抓取貓眼近10萬條評論數據,並對獲取到的數據進行分析,看看觀衆對這部電影的評價究竟如何?

整個數據分析的過程分爲四步:

  1. 獲取數據
  2. 處理數據
  3. 存儲數據
  4. 數據可視化

一、獲取數據

1. 簡介

​ 本次獲取的是貓眼APP的評論數據,如圖所示:

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

通過分析發現貓眼APP的評論數據接口爲:

http://m.maoyan.com/mmdb/comments/movie/1203084.json?_v_=yes&offset=0&startTime=2018-08-18%2022%3A25%3A03

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

​ 通過對評論數據進行分析,得到如下信息:

  • 返回的是json格式數據

  • 1203084表示電影的專屬id;offset表示偏移量;startTime表示獲取評論的起始時間,從該時間向前取數據,即獲取最新的評論

  • cmts表示評論,每次獲取15條,offset偏移量是指每次獲取評論時的起始索引,向後取15條

  • hcmts表示熱門評論前10條

  • total表示總評論數

2. 代碼實現

​ 這裏先定義一個函數,用來根據指定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()

​ 有兩點需要說明:

  1. 服務器一般都有反爬蟲策略,當請求過於頻繁時,服務器會拒絕部分連接,我這裏是通過增加每個請求間延時來解決,只是一種簡單的解決方案,還望各位看客理解包涵
  2. 根據數據量的多少,抓取數據所需時間會有所不同,我抓取的是2018-8-19到2018-8-10(上映當天)之間的數據,大概花了2個小時,共抓取約9.2萬條評論數據

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

四、數據可視化

​ 這裏使用的是pyecharts,pyecharts是一個用於生成Echarts圖表的類庫,便於在Python中根據數據生成可視化的圖表。

​ Echarts是百度開源的一個數據可視化JS庫,主要用於數據可視化。

​ 參考:http://pyecharts.org/

# 安裝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

1. 粉絲位置分佈

​ 代碼實現

# 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

可視化結果:

粉絲人羣主要集中在沿海一帶

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

從上圖可以看出,《一出好戲》的觀影人羣主要集中在沿海一帶,這些地方經濟相對發達,城市人口基數龐大,極多的熒幕數量和座位、極高密度的排片場次,讓觀衆便捷觀影,活躍的觀衆評論也多,自然也就成爲票房的主要貢獻者。

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

粉絲來源排名前20的城市依次爲:北京、深圳、上海、成都、武漢、廣州、西安、鄭州、重慶、南京、天津、瀋陽、長沙、東莞、哈爾濱、青島、杭州、合肥、大連、蘇州

電影消費是城市消費的一部分,從某種角度來看,可以作爲考察一個城市購買力的指標。這些城市在近年的GDP排行中大都居上游,消費水平較高。

2. 詞雲圖

​ 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')

可視化結果:

總體評價很不錯

​ 對評論數據進行分詞後製作如下詞雲圖:

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

​ 從詞雲圖中可以看到:

  • 評論中多次出現“可以”、“好看”、“不錯”等熱詞,說明觀衆對《一出好戲》的總體評價還是很不錯的
  • 同時對該影片中“張藝興”的“演技”也給予了很大的認可,我本人今天在觀看後也有同感,讓我們看到了不一樣的張藝興,實力演員
  • 對於初次“導演”電影的“黃渤”,能拍出這樣的影片,粉絲們也是比較肯定的,同時其本身就是票房的保障
  • 至於劇情方面,“現實”、“喜劇”、“搞笑”、“故事”等詞語,能看出這是一部反映現實的故事片,同時也兼具喜劇搞笑
  • 對於評論中出現的“一般”、“失望”等,這些粉絲或許是和我一樣,本以爲這是一部爆笑喜劇片,笑點應該會很多(畢竟在我們心中,黃渤、王寶強等就是笑星),沒想到笑點並不很多,至少與期待的有差距,導致心裏有落差的原因吧^_^

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

3. 評分星級

​ 代碼實現:

# 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')

可視化結果:

四、五星級影評合計高達83%

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

​ 從圖中可以看出,五星比例接近62%,四星比例爲21%,兩者合計高達83%,可見口碑還是相當不錯的,一星佔比不足6%

​ 《一出好戲》作爲黃渤第一次執導的作品,在拍攝過程中導演渤哥對自己的要求也是很嚴格的,所以有這樣的成績,也是理所當然。

《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

附:今天看電影的票根 ^_^
《一出好戲》講述人性,使用Python抓取貓眼近10萬條評論並分析,一起揭祕“這齣好戲”到底如何?

爬蟲案例對應的視頻課程:http://edu.51cto.com/course/14870.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章