2020年受疫情影响影视行业受冲击较大,也没什么新的电影上映,要说20年我最期待的电影肯定是准备在20年上映的花木兰了,对花木兰的影评来一波分析先上图

python猫眼影评情感分析 猫眼如何写影评_python猫眼影评情感分析


python猫眼影评情感分析 猫眼如何写影评_猫眼爬虫_02


python猫眼影评情感分析 猫眼如何写影评_python猫眼影评情感分析_03


python猫眼影评情感分析 猫眼如何写影评_猫眼电影爬虫_04


python猫眼影评情感分析 猫眼如何写影评_猫眼电影爬虫_05


python猫眼影评情感分析 猫眼如何写影评_猫眼爬虫_06


接下来就是代码块了

主函数 movie_comment_analysis_main.py

from film.data_analysis import data_analysis
from film.gen_analy_result import gen_analy_result
from film.pre_process_data import pre_process_data
from film.prepare_datat import prepare_data


movie_id = 1210778
movie_name = "花木兰"
df = None

# 下载数据
def down_data():
    prepare_data(movie_id, movie_name)

# 数据清洗
def pre_data():
    global df
    df = pre_process_data(movie_name)


# 数据分析
def movie_analysis():
    # 各项数据分析
    df_result = data_analysis(df)
    # 可视化与生成分析结果
    gen_analy_result(df_result, df, movie_name)


if __name__ == '__main__':
    down_data()
    pre_data()
    movie_analysis()

采集模块 prepare_datat.py

import time
import requests
import json
import pandas as pd
import numpy as np

def prepare_data(movie_id, movie_name):
    print("===================获取影评数据======================")
    # 模拟浏览器,需要提供header头部信息
    headers = {
        'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'
    }
    comment_pd = pd.DataFrame()
    startTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
    for i in range(0, 10000, 15):
        print("爬取第{0}页".format(int((i+15)/15)))
        # i%1000是因为爬猫眼时偏移量最大只能1000
        url = "http://m.maoyan.com/mmdb/comments/movie/"+str(movie_id)+".json?_v_=yes&offset="+str(i%1000)+"&startTime="+startTime
        html = requests.get(url, headers=headers)
        time.sleep(3)
        res_data = html.content  # 提取content内容
        res_data = res_data.decode("utf-8")  # 对内容解码

        # json.loads():反序列化成标准的Json格式
        json_data = json.loads(res_data)
        # 提取Json数据
        # 先判断是否有数据
        if "cmts" in json_data:
            # 提取cmts评论
            cmts = json_data["cmts"]
            # 选取数据
            for cmt in cmts:
                # 昵称,性别,评论星级,点赞数,回复数,城市,日期,评论内容
                cmt_dict = {}
                # 昵称
                if "nick" in cmt.keys():
                    cmt_dict["nick"] = cmt["nick"]
                else:
                    cmt_dict["nick"] = np.nan
                # 性别
                if "gender" in cmt.keys():
                    cmt_dict["gender"] = cmt["gender"]
                else:
                    cmt_dict["gender"] = np.nan
                # 评分
                if "score" in cmt.keys():
                    cmt_dict["score"] = cmt["score"]
                else:
                    cmt_dict["score"] = np.nan
                # 点赞数
                if "approve" in cmt.keys():
                    cmt_dict["approve"] = cmt["approve"]
                else:
                    cmt_dict["approve"] = np.nan
                # 评论的回复数量
                if "replyCount" in cmt.keys():
                    cmt_dict["replyCount"] = cmt["replyCount"]
                else:
                    cmt_dict["replyCount"] = np.nan
                # 城市
                if "cityName" in cmt.keys():
                    cmt_dict["cityName"] = cmt["cityName"]
                else:
                    cmt_dict["cityName"] = np.nan
                # 日期
                if "time" in cmt.keys():
                    cmt_dict["time"] = cmt["time"]
                else:
                    cmt_dict["time"] = np.nan
                # 评论内容
                if "content" in cmt.keys():
                    cmt_dict["content"] = cmt["content"]
                else:
                    cmt_dict["content"] = np.nan

                comment_pd = comment_pd.append(cmt_dict, ignore_index=True)
            print(comment_pd)
        else:
            print("No data")

    comment_pd.to_excel(str(movie_name)+".xlsx")
    print("===================数据获取完毕======================")

清洗模块 pre_process_data.py

import pandas as pd


def pre_process_data(movie_name):
    print("===================开始数据清洗======================")
    df = pd.read_excel(movie_name+".xlsx",parse_dates=["time"])  # 读取源数据,将数据解析为时间格式
    df["小时"] = df["time"].map(lambda x: int(x.strftime("%H")))  # 提取小时
    df = df.drop_duplicates()  # 去重
    print("数据去重完毕")
    df = df.dropna(subset=["cityName"])  # 删除城市空值行
    df = df.dropna(subset=["gender"])  # 删除性别空值行
    print("去除空值完毕")
    df.to_excel(movie_name+".xlsx")  # 写入处理后的数据
    print("===================数据清洗完毕======================")
    return df

数据处理模块 data_analysis.py

def data_analysis(df_comment):
    print("===================开始数据分析======================")

    df_comment_list = []
    df_score = df_comment.groupby(["cityName"])['score']  # 按城市分组
    df_gen_score = df_comment.groupby(["gender"])['score']  # 按性别分组
    df_time_score = df_comment.groupby(["time"])['time']  # 按时间分组
    time_count = df_comment.groupby("小时")["nick"].agg(['count'])  # 提取时间段

    comment_gender_sum = df_gen_score.agg(['sum'])  # 评分中男女总数
    df_comment_list.append(comment_gender_sum)
    comment_time_count = df_time_score.agg(['count'])  # 评分中日期计数
    comment_score_mean_count = df_score.agg(['mean', 'count'])  # 评分中各个城市的平均分、数量

    # 重新设置索引  inplace改变原来的
    comment_score_mean_count.reset_index(inplace=True)
    comment_score_mean_count['mean'] = round(comment_score_mean_count['mean'],2)
    df_comment_list.append(comment_score_mean_count)
    df_comment_list.append (comment_time_count)

    df_score = df_comment.groupby(["score"])
    df_score_nick = df_score["nick"]
    score_count = df_score_nick.agg(['count'])
    df_comment_list.append(score_count)
    df_comment_list.append(time_count)
    print("===================数据分析完毕======================")
    return df_comment_list

分析模块 gen_analy_result.py

import collections
import os
import numpy as np
import imageio
import jieba
from wordcloud import WordCloud, ImageColorGenerator
import matplotlib.pyplot as plt

# 解决中文乱码问题
plt.rcParams["font.sans-serif"] = "SimHei"
fig = plt.figure(figsize=(8, 6))

url_city = None
url_gen = None
url_score = None
url_scatter = None
url_stack = None


def gen_analy_result(df_result, df_source, movie_name):
    print("===================开始生成结果======================")
    # 生成词云
    gen_wordcloud(df_source, movie_name)
    # 生成折线图、柱状图、网页的函数
    draw_plot_bar(df_result, movie_name)
    draw_pie(df_result, movie_name)
    drwa_bar(df_result, movie_name)
    draw_scatter(df_result, movie_name)
    draw_stackplot(df_result, movie_name)
    print("===================结果生成完毕======================")


# 气泡图
def draw_scatter(df_result, movie_name):
    global url_scatter
    city_result = df_result[1]
    city_main = city_result.sort_values("count", ascending=False)[0:10]
    # 建立坐标系
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    # 指明x和y的值
    x = np.array(city_main['count'].tolist())  # 城市评论数做x轴
    y = np.array(city_main['mean'].tolist())  # 城市平均分做y轴
    # 绘图
    colors = y * 10  # 根据y值的大小生成不同的颜色
    area = y * 100  # 据y值的大小生成大小不同的形状
    ax1.scatter(x, y, marker="o", s=area, c=colors)
    # 设置标题
    ax1.set_title("城市评论数量与评分关系图", loc="center")

    # 添加数据标签 ha水平方向  va垂直方向
    for a, b in zip(x, y):
        plt.text(a, b, b, ha="center", va="center", fontsize=12, color="white")
    # 设置x轴和y轴
    ax1.set_xlabel('评论数量')
    ax1.set_ylabel('平均分')
    # 设置网格线
    plt.grid(False)
    url_scatter = movie_name + "scatter.jpg"
    fig.savefig(url_scatter)


# 词云函数
def gen_wordcloud(df_source, movie_name):
    # 把所有评论串在一起,用空格分隔
    full_comment = " ".join(df_source["content"])
    # 分词
    word_list = []
    # cut_for_search搜索引擎模式:在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词
    words_gen = jieba.cut_for_search(full_comment)  # 返回的是生成器
    for w in words_gen:
        word_list.append(w)
    word_list = [k for k in word_list if len(k) > 1]  # 过滤只有一个词的评论
    print(word_list)

    # imageio导入照片功能比较好用,可以导入很多格式类型的图片
    bg_color = imageio.imread("地球图片.jpg")

    # WordCloud可以将文本中词语出现的频率作为一个参数绘制词云,而词云的大小、颜色、形状等都是可以设定的
    # 设置对象
    wc = WordCloud(background_color='white',  # 背景颜色
                   max_words=200,  # 最大词数
                   mask=bg_color,  # 设置词云形状  以该参数值作图绘制词云,这个参数不为空时,width和height会被忽略
                   max_font_size=300,  # 显示字体的最大值
                   font_path="simfang",  # 指定字体路径,系统字体路径:C:\Windows\Fonts
                   random_state=42,  # 为每个词返回一个PIL颜色
                   )

    # 统计列表元素出现次数,返回的数据中键对应词,值对应出现次数,例如:Counter({'blue': 3, 'red': 2, 'green': 1})
    counter = collections.Counter(word_list)
    # wc对象的生成器的词频
    wc.generate_from_frequencies(counter)  # frequencies频率
    print("================词云已生成================")
    # 从图片中取色
    image_colors = ImageColorGenerator(bg_color)  # 从图片中取色
    plt.figure()  # 创建画布
    plt.imshow(wc.recolor(color_func=image_colors))  # 绘制图像
    plt.axis("off")  # 关闭坐标轴
    wc.to_file(os.path.join(movie_name + "词云.jpg"))  # 生成词云图片
    print("================词云图片已生成================")

# 评分统计
def drwa_bar(df_result, movie_name):
    global url_score
    # 建立坐标系
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    # 指明x和y的值
    score_result = df_result[3]
    x = np.array(score_result.index)
    y = np.array(score_result['count'].tolist())  # 评论平均分作为折线图的Y轴
    # 绘制柱状图
    ax1.bar(x, y, color="r", label="评分数量")
    # 设置标题
    ax1.set_title(movie_name + "评分统计", loc="center")
    # 添加数据标签
    for a, b in zip(x, y):  # zip()需是np.array()
        ax1.text(a, b, b, ha="center", va="bottom", fontsize=11)
    # 设置x轴和y轴的名称
    ax1.set_xlabel('星级')
    ax1.set_ylabel('数量')
    # 设置x轴和y轴的刻度
    ax1.set_xticks(np.arange(0, 6, 1))
    ax1.set_yticks(np.arange(100, 600, 100))
    # 显示图例
    ax1.legend()
    # 保存图表到本地
    url_score = movie_name + "bar.jpg"
    fig.savefig(url_score)


# 城市分析
def draw_plot_bar(df_result, movie_name):
    print("=====================开始绘制城市分析图====================")
    global url_city
    # 建立坐标系
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    # 指明x和y的值
    city_result = df_result[1]
    city_main = city_result.sort_values("count", ascending=False)[0:10]  # 取前10
    x = city_main['cityName'].tolist()
    y1 = city_main['mean'].tolist()  # 评论平均分作为折线图的Y轴
    y2 = city_main['count'].tolist()  # 评论数量作为柱状图的Y轴
    # 直接绘制折线图和柱形图
    ax1.plot(x, y1, color="r", linestyle="solid", linewidth=1, marker="o", markersize=3, label="平均评分")
    ax1.bar(x, y2, color="b", label="评论数量")
    # 设置标题
    ax1.set_title(movie_name+"-TOP10城市评论数量与平均评分", loc="center")
    # 添加数据标签
    for a, b in zip(x, y1):
        ax1.text(a, b, b, ha="center", va="bottom", fontsize=11)
    for a, b in zip(x, y2):
        ax1.text(a, b, b, ha="center", va="bottom", fontsize=11)
    # 设置x轴和y轴的名称
    ax1.set_xlabel('城市')
    ax1.set_ylabel('评论数量')
    # 设置x轴和y轴的刻度
    ax1.set_xticks(np.arange(0,10,1))
    ax1.set_yticks(np.arange(10, 60, 10))
    # 显示图例
    ax1.legend()
    # 保存图表到本地
    url_city = movie_name + "bar_plot.jpg"
    fig.savefig(url_city)
    print("=====================绘制城市分析图完毕====================")


# 性别分析
def draw_pie(df_result, movie_name):
    global url_gen
    genter_result = df_result[0]
    # 建立坐标系
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    # 指明x值
    x = np.array(genter_result['sum'])
    lab = genter_result.index
    if lab[0] == 1:
        gen1 = "男"
    else:
        gen1 = "女"
    if lab[1] == 2:
        gen2 = "女"
    else:
        gen2 = "男"
    labels = [gen1, gen2]
    explode = [0, 0]
    labeldistance = 1.1  # 标签距离
    # autopct百分比格式  shadow是否有阴影  radius半径
    ax1.pie(x, labels=labels, autopct="%.0f%%", explode=explode, radius=1.0, labeldistance=labeldistance)
    # 设置标题
    ax1.set_title(movie_name+"评分男女占比", loc="center")
    # 保存图表到本地
    url_gen = movie_name + "pie.jpg"
    fig.savefig(url_gen)


# 面积图
def draw_stackplot(df_result, movie_name):
    global url_stack
    time_result = df_result[4]
    print(time_result)
    print(type(time_result))
    # 建立坐标系
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    # 指明x和y的值
    print("00")
    print(time_result.index)
    x = np.array(time_result.index)
    y = np.array(time_result['count'].tolist())
    # 绘图
    print("11")
    ax1.stackplot(x, y, labels="评论数量")
    # 设置标题
    ax1.set_title("评论数量与时间的关系图", loc="center")
    # 设置x轴和y轴名称
    ax1.set_xlabel('时间/小时')
    ax1.set_ylabel('评论数量')
    # 设置网格线
    plt.grid(False)
    # 显示图例
    ax1.legend()
    url_stack = movie_name + "stack.jpg"
    fig.savefig(url_stack)