实训项目:豆瓣电影数据分析实战
系统需求分析:
数据采集:使用lxml,BeautifulSoup4框架或Scrapy框架爬取豆瓣电影Top250的数据。
(分析系统的功能模块需求,详细描述各个功能模块的作用)
数据清洗:对爬取到的电影数据进行缺失值处理、异常值处理、数据类型转换和数据去重等数据清洗操作。
数据分析:多维度分析:导演、主演、类型、制片国家等,对比分析:电影评分变化趋势、电影数据的变化趋势,文本分析:热词统计、文本情感分析
数据可视化:通过折线图、柱状图、柱状图和词云等多种方式完成可视化
系统设计
(系统整体设计,各个功能模块的设计,可以通过系统架构图、流程图和活动图等多种方式进行描述)
系统实现
各个功能模块的实现过程以及实现的结果)
调试及结果分析
所有功能演示,根据输入的不同数据得到不同的测试结果)
# 一、导入常用库
import numpy as np
import pandas as pd
from collections import Counter
import matplotlib.pyplot as plt
from bs4 import BeautifulSoup
import seaborn as sns
import collections
import requests
import pyecharts as pe
import json
import statsmodels.formula.api as smf
from array import array
from statsmodels.formula.api import ols
# 导入数据
df=pd.read_excel("movice.xlsx")
df.head(5)
#转换日期格式
df['上映时间'] = pd.to_datetime(df['上映时间']).apply(lambda x: x.date())
df.columns
df_columns = ['名字','类型','导演','编剧','主演','时长','国家','语言','上映时间','简介','评分','评分人数','排名']
# 检查重复值、缺失值
df.info()
print(df.describe())
# 判断哪些列有空值
df.isnull().any()
# 筛选出主演为空值的数据
df[df['编剧'].isnull()]
df[df['主演'].isnull()]
# 填充相应的值
df['编剧'].fillna('郭柯',inplace=True)
df['主演'].fillna('韦绍兰',inplace=True)
df.isnull().any()
# 二、数据可视化
# 定义函数,将用"/" 分割的数据转化为列表
def conver_list(df, *columns):
for column in columns:
data[column] = data[column].str.split('/')
return data
data
# #获取上映时间中的年份
type(data['上映时间'] )
type(data['类型'] )
data['上映时间']=pd.to_datetime(data['上映时间'], errors='coerce')
data['上映年份'] = df['上映时间'].dt.strftime('%Y')
data
# 1、获取每一年上映的电影数量
list_date = data['上映年份'].tolist()
date_count = collections.Counter(list_date)
print(date_count)
len(date_count)
# 2、按照电影上映年份进行排序
sort_date_count = sorted(date_count.items(), key=lambda x: x[0])
# 将排序后的字典转化为新的列表,作为图表横坐标轴、纵坐标轴
x1 = [x[0] for x in sort_date_count]
y1 = [x[1] for x in sort_date_count]
#3、绘制电影年份数量情况分布图
plt.figure(dpi=300,figsize=(24,8))
yearshu=data.groupby('上映年份')['上映年份'].count().plot(kind='bar')
p1 = plt.bar(x1, y1, width= 0.8, label='value') # width表示柱子的宽度
# plt.xticks(rotation=360)
plt.xticks(rotation = 90,fontsize=20)#使x轴标签竖着显示
plt.title('电影上映年份分布情况',fontname='SimHei',fontsize=15)
plt.xlabel('上映年份',fontname='SimHei',fontsize=15)
plt.ylabel('不同年份上映的电影数量',fontname='SimHei',fontsize=15)
plt.bar_label(p1, label_type='edge')# label_type=‘edge’表示将数据值标签放在柱子顶端,label_type=‘center’表示将数据值标签放在柱子中间。
plt.show()
import pandas as pd
yearshu1=data.groupby('上映年份')['上映年份'].count()
# 4、豆瓣评分&年份的情况分布的散点图
yearshu2=data.groupby('上映年份')['评分'].sum()
# yearshu2
list=[yearshu1,yearshu2]
data3=pd.DataFrame(list).T
data3.columns = ['每年的电影数量', '总评分',]
junfen=data3['总评分'] /data3['每年的电影数量']
data3['评分的平均值'] = data3['总评分'] /data3['每年的电影数量']
aa = data3.sort_values(by=['评分的平均值'],ascending=False)
aa
data3
5、豆瓣评分&年份的情况分布的散点图
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import random
# 正确显示中文和负号
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
plt.xticks(rotation = 90,fontsize=10)#使x轴标签竖着显示
sns.set(font='SimHei')
sns.scatterplot(aa,x='上映年份', y='评分的平均值') #sns.scatterplot()画散点图
plt.title('年份 &豆瓣评分情况分布散点图',fontname='SimHei',fontsize=15)
plt.xlabel('年份',fontname='SimHei',fontsize=15)
plt.ylabel('豆瓣评分每年的均值',fontname='SimHei',fontsize=15)
# plt.bar_label(p1, label_type='edge')# label_type=‘edge’表示将数据值标签放在柱子顶端,label_type=‘center’表示将数据值标签放在柱子中间。
plt.show()
plt.xticks(rotation = 90,fontsize=10)#使x轴标签竖着显示
sns.set(font='SimHei')
sns.scatterplot(data3,x='上映年份', y='评分的平均值') #sns.scatterplot()画散点图
plt.title('年份 &豆瓣评分情况分布散点图',fontname='SimHei',fontsize=15)
plt.xlabel('年份',fontname='SimHei',fontsize=15)
plt.ylabel('豆瓣评分每年的均值',fontname='SimHei',fontsize=15)
# plt.bar_label(p1, label_type='edge')# label_type=‘edge’表示将数据值标签放在柱子顶端,label_type=‘center’表示将数据值标签放在柱子中间。
plt.show()
# 6、各个国家电影数量的分布情况
# 将排序后的字典转化为新的列表,作为横坐标轴、纵坐标轴
x2 = [x[0] for x in sort_area_count]
y2 = [x[1] for x in sort_area_count]
# yearshu=data.groupby('上映年份')['上映年份'].count().plot(kind='bar')
# plt.figure(dpi=300,figsize=(24,8))
p1 = plt.bar(x2, y2,width=1,label='value') # width表示柱子的宽度
# plt.xticks(rotation=360)
plt.xticks(rotation = 90,fontsize=9)#使x轴标签竖着显示
plt.title('各个国家电影数量的分布情况',fontname='SimHei',fontsize=15)
plt.xlabel('各个国家',fontname='SimHei',fontsize=15)
plt.ylabel('不同国家的电影数量',fontname='SimHei',fontsize=15)
plt.bar_label(p1, label_type='edge')# label_type=‘edge’表示将数据值标签放在柱子顶端,label_type=‘center’表示将数据值标签放在柱子中间。
plt.show()
# 7、电影类型分布情况
plt.figure(dpi=300,figsize=(24,64))
type_list = []
for i in data['类型']:
type_list.extend(i)
type_count = collections.Counter(type_list)
sort_type_count = sorted(type_count.items(), key=lambda x: x[1], reverse=True)
print(sort_type_count)
x3 = [x[0] for x in sort_type_count]
y3 = [x[1] for x in sort_type_count]
pie,l_text,p_text=plt.pie(x=y3, labels=x3, autopct='%3.1f%%',labeldistance=1.2)
for t in l_text:
t.set_size(22)
for t in p_text:
t.set_size(17)
plt.title('各类型数量情况比较',fontsize=40)
plt.legend(loc='upper right', bbox_to_anchor=(1.8,1),fontsize=25)
plt.show()
p1 = plt.bar(x3, y3, width= 0.8, label='value') # width表示柱子的宽度
plt.xticks(rotation = 90,fontsize=10)#使x轴标签竖着显示
plt.title('电影类型的分布情况',fontname='SimHei',fontsize=15)
plt.xlabel('电影的类型',fontname='SimHei',fontsize=15)
plt.ylabel('不同类型的电影数量',fontname='SimHei',fontsize=15)
plt.bar_label(p1, label_type='edge')# label_type=‘edge’表示将数据值标签放在柱子顶端,label_type=‘center’表示将数据值标签放在柱子中间。
plt.show()
# 8、导演数量分布前20
director_list = []
for x in df['导演']:
director_list.extend(x)
director_count = collections.Counter(director_list)
sort_director_count = sorted(
director_count.items(), key=lambda x: x[1], reverse=True)
x4 = [x[0] for x in sort_director_count]
y4 = [x[1] for x in sort_director_count]
# fig = plt.figure(figsize=(100, 200))
plt.figure(dpi=300,figsize=(24,8))
p1 = plt.bar(x4[:20], y4[:20], width= 0.8) # width表示柱子的宽度
plt.xticks(rotation = 90,fontsize=20)#使x轴标签竖着显示
plt.title('Top250电影导演数量分布情况(前20位导演)',fontname='SimHei',fontsize=15)
plt.xlabel('导演名称',fontname='SimHei',fontsize=15)
plt.ylabel('电影数量',fontname='SimHei',fontsize=15)
plt.bar_label(p1, label_type='edge',fontsize=20)# label_type=‘edge’表示将数据值标签放在柱子顶端,label_type=‘center’表示将数据值标签放在柱子中间。
plt.show()
# 9、主演前20分布柱形图
actor_list = []
for x in df['主演']:
actor_list.extend(x)
actor_count = collections.Counter(actor_list)
sort_actor_count = sorted(
actor_count.items(), key=lambda x: x[1], reverse=True)
x5 = [x[0] for x in sort_actor_count]
y5 = [x[1] for x in sort_actor_count]
plt.figure(dpi=300,figsize=(24,8))
p1 = plt.bar(x5[:20], y5[:20], width= 0.8, label='value') # width表示柱子的宽度
plt.xticks(rotation = 90,fontsize=20)#使x轴标签竖着显示
plt.title('Top250电影主演分布情况(前20位导演)',fontname='SimHei',fontsize=15)
plt.xlabel('主演名称',fontname='SimHei',fontsize=15)
plt.ylabel('电影数量',fontname='SimHei',fontsize=15)
plt.bar_label(p1, label_type='edge')
plt.show()
# 10、查看电影时长柱形图
import matplotlib.pyplot as plt
bins=[0,80,100,120,140,160,180,240]
pd.cut(data.时长,bins)
pd.cut(data.时长,bins).value_counts()
pd.cut(data.时长,bins).value_counts().plot.bar(rot=20)
# 11、绘制评分人数与排名的散点图
plt.figure(figsize=(20,5))
plt.subplot(1,2,1)
# 绘制散点图
plt.scatter(data['评分人数'],data['排名'])
plt.xlabel('评分人数')
plt.ylabel('排名')
plt.gca().invert_yaxis()
# 绘制直方图
plt.subplot(1,2,2)
plt.hist(data['评分人数'])
# 12、豆瓣电影Top250评分-排名的散点分布
plt.figure(figsize=(9, 6), dpi=100)
# 设置字体
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
# 绘制散点图 设置点的颜色
plt.scatter(rating_score, rating, c='r')
# 添加描述信息 设置字体大小
plt.xlabel("评分", fontsize=12)
plt.ylabel("排名", fontsize=12)
plt.title("豆瓣电影Top250评分-排名的散点分布", fontsize=15)
# 添加网格 网格的透明度 线条样式
plt.grid(alpha=0.5, linestyle=":")
plt.savefig('test2.PNG')
plt.show()
# 13、豆瓣top250电影排名&评分&评分人数情况
x5 = data['评分人数'].tolist()
y5 = data['评分'].tolist()
z = data['排名'].tolist()
plt.figure(figsize=(12, 8))
plt.scatter(x=x5,
y=z,
cmap='Blues',
marker='o',
c=y5, # 数字越大,颜色越深,评分越高
alpha=0.8,
linewidths=0.3,
edgecolors='Black')
plt.title('豆瓣top250电影排名&评分&评分人数情况', fontsize=18)
plt.xlabel('评分人数', fontsize=14)
plt.ylabel('豆瓣排名', fontsize=14)
plt.xlim(0, 2500000)
plt.colorbar()
14、评论热词次统计及词云可视化
# 导入扩展库
import re # 正则表达式库
import numpy as np # numpy数据处理库
import jieba # 结巴分词
import wordcloud # 词云展示库
from PIL import Image # 图像处理库
import matplotlib.pyplot as plt # 图像展示库
# 读取文件
fn = open('pinglun1.csv','r',encoding='utf-8') # 打开文件
string_data = fn.read() # 读出整个文件
fn.close() # 关闭文件
# 文本预处理
pattern = re.compile(u'\t|\n|\.|-|:|;|\)|\(|\?|"') # 定义正则表达式匹配模式
a = re.sub(pattern, '', string_data) # 将符合模式的字符去除
# print(type(string_data))
words =jieba.lcut(a)
counts = {}
for word in words:
if len(word) == 1:
continue
else:
counts[word] = counts.get(word,0)+1
print(len(counts))
# 词频展示
mask = np.array(Image.open('771.jpg')) # 定义词频背景
wc = wordcloud.WordCloud(
font_path='C:/Windows/Fonts/simhei.ttf', # 设置字体格式
mask=mask, # 设置背景图
# max_words=200, # 最多显示词数
max_font_size=100,
background_color="white" #字体最大值
)
wc.generate_from_frequencies(counts) # 从字典生成词云
plt.imshow(wc) # 显示词云
plt.axis('off') # 关闭坐标轴
plt.show() # 显示图像
15、简介情感分析词云图
df=pd.read_excel("movice.xlsx")
s = []
for i in df['简介']:
s.append(i)
# print(s)
a = str(s)
words =jieba.lcut(a)
counts = {}
for word in words:
if len(word) == 1:
continue
else:
counts[word] = counts.get(word,0)+1
# items = list(counts.items())
# items.sort(key=lambda x:x[1],reverse=True)
# items
Counts
import numpy as np # numpy数据处理库
import wordcloud # 词云展示库
from PIL import Image # 图像处理库
import matplotlib.pyplot as plt # 图像展示库
text={"喜欢":16,"神秘":13,"梦想":9,'兴趣':9,"怀疑":8,"爱情":8,"案件":7,"社会":7,"愤怒":6,"美好":6,"疯狂":5,"热爱":5,"犯罪":5,
"威胁":5,"失去":5,"真实":5,"事实":5,"保护":5,"杀害":19,"奇怪":4,"放弃":4,"失败":4,"理想":4,"幸福":4,"烦恼":4
,"肮脏":4,"追杀":4,"期待":4,"可爱":4,"温暖":4,"绝望":3,"救赎":3,"鼓励":3,"孤独":3,"不满":3,"垃圾":3,"哭泣":3,
"好奇":3,"无力":3,"残忍":3,"刺激":3,"活泼":3,"快乐":3,"厌倦":3}
# 词频展示
mask = np.array(Image.open('771.jpg')) # 定义词频背景
wc = wordcloud.WordCloud(
font_path='C:/Windows/Fonts/simhei.ttf', # 设置字体格式
mask=mask, # 设置背景图
max_font_size=100,
background_color="white" #字体最大值
)
wc.generate_from_frequencies(text) # 从字典生成词云
# image_colors = wordcloud.ImageColorGenerator(mask) # 从背景图建立颜色方案
# wc.recolor(color_func=image_colors) # 将词云颜色设置为背景图方案
plt.imshow(wc) # 显示词云
plt.axis('off') # 关闭坐标轴
plt.show() # 显示图像
二、Flask可视化
from flask import Flask,render_template
import pymysql
app = Flask(__name__)
#路由
@app.route('/')
def hello_world():
return render_template("index.html")
@app.route('/index')
def index():
return render_template("index.html")
@app.route('/movie')
def movie():
# 打开数据库连接
db = pymysql.connect(host="81.68.122.101", user="root", password="123456", database="test")
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()
# 使用 execute() 方法执行 SQL 查询
cursor.execute("select * from douban250")
# 获取所有记录列表
results = cursor.fetchall()
data_list = []
for item in results:
data_list.append(item)
# print(data_list)
return render_template("movie.html",movies = data_list)
@app.route('/score')
def score():
score = [] #评分
num = [] #每个评分所统计出的电影数量
# 打开数据库连接
db = pymysql.connect(host="81.68.122.101", user="root", password="123456", database="test")
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()
# 使用 execute() 方法执行 SQL 查询
cursor.execute("SELECT grade,COUNT(grade) FROM douban250 GROUP BY grade")
results = cursor.fetchall()
for item in results:
score.append(item[0])
num.append(item[1])
return render_template("score.html",score = score,num = num)
@app.route('/word')
def word():
return render_template("word.html")
@app.route('/team')
def temp():
return render_template("team.html"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)
结果:
导入数据:
处理完数据结果:
二、可视化结果
# '导演', '主演', '类型', '制片国家/地区',这几列下面的值转化为列表
# 1、获取每一年上映的电影数量
# 2、按照电影上映年份进行排序
#3、绘制电影年份数量情况分布图
# 4、豆瓣评分&年份的情况分布的散点图
# 6、各个国家电影数量的分布情况
# 7、电影类型分布情况
# 8、导演数量分布前20
# 9、主演前20分布柱形图
# 10、查看电影时长柱形图
# 11、绘制评分人数与排名的散点图
# 12、豆瓣电影Top250评分-排名的散点分布
# 13、豆瓣top250电影排名&评分&评分人数情况
14、评论热词次统计及词云可视化
15、简介情感分析词云图
16、Flask可视化代码图
17、Flask可视化结果图
学习心得
此次,我们采用request,lxml,等库对豆瓣 TOP250 电影数据进行爬取,并用numpy,pandas,matplotlib 以及 wordcloud 等库对豆瓣 TOP250 电影数据进行数据分析处理,数据清洗pd.read_excel导入数据,df.isnull().any()检查数据缺失值,df.duplicated().sum()检查数据是否有重复。编剧、主演这一列有空值,数据有249条,说明各存在1个空值,详细看一下是哪一部电影 评分列格式是浮点型,评分人数是整型,都没有空值,接着对导演、主演、类型进行分割取值以便于后面的数据可视化。
从数据可视化分析我们可以得出,豆瓣电影 TOP250 看到很多演员主演的电影质量是很高的,中国演员张国荣、梁朝伟主演的电影,分别有8部,7部入围豆瓣top250,周星驰有6部入围;张国荣、梁朝伟一起主演的电影有3部入围,分别是《春光乍泄》、《射雕英雄传之东成西就》、《东邪西毒》。上榜次数最多的导演是宫崎骏,史蒂文·斯皮尔伯格以及克里斯托弗·诺兰等。上榜最多的是美国电影。剧情、爱情的电影居多。
我们从中也遇到许多的问题:如在爬取数据时老是遇到被封号,因此导入from selenium import webdriver 来模仿浏览器访问,以及运用sleep(5)来防止被封号。还有库的安装失败,网络爬虫的数据出现空值或爬取的数据不对, scrapy框架文件路径有问题无法运行;在拿取需要的数据时,对数据进行分组统计,使用了不再是传统的groupby,而是创新的方法from collections import Counter计数器。对字符串\列表\元祖\字典进行计数,返回一个字典类型的数据,键是元素,值是元素出现的次数;在可视化时,pyecharts库导入不了所以我们采用了matplotlib.pyplot ;jieba库分成把外国导演的名字分割错误,去掉了中间的一个点等等。结巴库分成把外国导演的名字分割错误是通过把名字读取进一个列表,再转换成字符串,并对其根据逗号进行分割然后统计绘制出词云图,在此过程中就没有用到结巴库了。
我们遇到的这些问题,经过我们的讨论和上网百度请问老师等得到了解决。经过这个实验我们也积累类关于网络爬虫,数据分析处理等经验,方便我们以后更好的操作和学习。我们会不停的学习,争取以后实验可以做到最好,提升自己的实力。