写在前面
记录一下这学期《数据分析与应用》这门课的课程报告,下面是正文内容。



  • 1 背景分析
  • 2 数据介绍
  • 3 数据爬虫
  • 4 数据清洗
  • 5 数据分析
  • 5.1 最受欢迎的前10种电影类型
  • 5.2 不同类型电影数和上映时间之间的关系
  • 5.3 电影排名和上映时间的关系
  • 5.4 电影总数和上映时间的关系
  • 5.5 最受欢迎的10位导演
  • 5.6 电影平均票房排名前10的电影类型
  • 5.7 电影票房和电影排名之间的关系
  • 6 总结
  • 参考文献


摘要:电影事业在近几十年来得到了蓬勃发展,分析全球前100名的电影的相关数据对于有助于了解整个电影市场及行业,本文以Python作为编程语言,首先利用Scrapy框架对全球前100名的电影数据进行了获取,并进行了数据清洗,最后利用Pandas、Matplotlib等进行了数据分析,对全球前100名电影的整体情况进行了探究。

关键词:Scrapy、电影、Python、数据分析

1 背景分析

随着人们生活水平的不断提高,看电影等精神层面的消费越来越受到人们的青睐,而电影拍摄和制作水平的提升也让电影制作的周期大幅度地降低。那么截止目前,全球前100名电影的情况呈现哪些趋势呢?这是我们想要探究的问题。

2 数据介绍

本数据的数据源来自网站www.piaofang.biz(全球电影票房排行榜网),观察网站我们需要获取的数据包括排名、电影名、上映时间、类型、导演和全球票房。

电影数据集数据分析 电影数据分析报告_Python


图 1 全球电影票房排行榜


3 数据爬虫

Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。下图显示了Scrapy的架构。

电影数据集数据分析 电影数据分析报告_数据分析_02


图 2 Scrapy架构图


基于Scrapy框架,简单地编写爬虫代码,将全球电影票房排行榜网的源代码保存为boxoffice.html留在本地,下面是关键代码,保存在附件中的tutorial/spider/Tutorial.py。

import scrapy
from tutorial.items import TutorialItem

class TutorialSpider(scrapy.Spider):
    name = 'Tutorial'
    allowed_domains = ['piaofang.biz']
    start_urls = ['http://www.piaofang.biz']

    def parse(self, response):
        filename = "boxoffice.html"
        open(filename, 'wb+').write(response.body)

这样我们就获取到了全球电影票房排行榜网的关键数据,爬取到html源代码如下图所示。

电影数据集数据分析 电影数据分析报告_爬虫_03


图 3 boxoffice.html爬取到的网页源代码(左图是记事本源代码,右图是html打开网页


4 数据清洗

通过分析爬取的网页源代码,我们编写相关的正则表达式来匹配需要的信息,这里注意两个点,第一是爬取的是排名的数字、上映时间和全球票房是数值型的数据,爬取后的数据是字符串类型,需要进行字符串到数值类型的转换;二是电影名对应的html代码可能存在超链接,需要对存在超链接的匹配字符串进行二次正则表达式匹配,提取真正的电影名。下面是数据清洗的关键代码:

from pandas.core.frame import DataFrame
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import re
# 正则表达式匹配规则
numberinfo='<td class="num">.*?第 (.*?) 名</td>'
titleinfo='<td class="title">《(.*?)》<[/s]'
yearinfo='<td class="year">(.*?)</td>'
typeinfo='<td class="type">(.*?)</td>'
directorinfo='<td class="daoyan">(.*?)</td>'
boxofficeinfo='<td class="piaofang"><span>(.*?)</span>'


# 排名:字符串转换为数值型数据
number=re.findall(numberinfo,str(soup))
number=[int(y) for y in number]
# 电影名:由于匹配的字符串有的存在超链接,所以进行了二次正则表达式匹配去除超链接
title=re.findall(titleinfo,str(soup))
title_n=[]
for t in title:
    if '<' in t:
        t=re.findall('<a href=.*?>(.*?)</a>',t)
    title_n.append(t)
# 年份:字符串转换为数值型数据
year=re.findall(yearinfo,str(soup))
year=[int(y) for y in year]
# 电影类型:字符串转换为数值型数据
types=re.findall(typeinfo,str(soup))
# 电影导演
director=re.findall(directorinfo,str(soup))
# 电影票房:去除数字中的逗号并将字符串转换为数值型数据
boxoffice=re.findall(boxofficeinfo,str(soup))
boxoffice=[s.replace(",","") for s in boxoffice] 
boxoffice=[int(s) for s in boxoffice]

进行了提取以后我们将数据转化为Pandas支持的DataFrame格式,命名为dataF。

data={"number": number,
        "title" :   title_n,
      "year":     year,
      "type":     types,
      "director": director,
      "boxoffice": boxoffice} 
dataF=DataFrame(data)

经过处理后的dataF再把电影类型中的’/’替换为’,’。并且保存为csv格式

dataF['type']=dataF['type'].str.replace('/',',')
dataF.to_csv('电影票房100.csv', encoding='gbk',index=False)

我们可以查看一下我们导出的csv的部分数据:

电影数据集数据分析 电影数据分析报告_数据分析_04


图 4 导出的csv的部分数据


5 数据分析

5.1 最受欢迎的前10种电影类型

在dataF中的电影类型的电影类型可能有多种,我们进行拆分并去重得到全球前100名电影的电影类型。

#对type进行二次拆分
types_cnt=[]#[x,y,z,t]
types_rnt=[]#[[x,y],[z,t]]
for i in dataF['type']:
    i=i.split(',')
    types_cnt.extend(i)
types_rnt.append(i)

types_all=[]
for i in types_rnt:
    for j in i:
        if j not in types_all:
            types_all.append(j)
print(types_all[:])

[‘科幻’, ‘动作’, ‘灾难’, ‘爱情’, ‘冒险’, ‘动画’, ‘犯罪’, ‘奇幻’, ‘喜剧’, ‘枪战’, ‘惊悚’, ‘战争’, ‘音乐’, ‘传记’, ‘军事’, ‘亲情’, ‘家庭’]

我们按照电影类型进行分组并进行排序,然后进行绘图。

type_list=pd.Series(types_cnt).value_counts()[:10].sort_values(ascending=False)
type_df=pd.DataFrame(type_list)
type_df.rename(columns={0:"Total"}, inplace=True)
import matplotlib.font_manager as mfm    #

font_path=r"msyh.ttc"#字体 华文仿宋
prop=mfm.FontProperties(fname=font_path)
plt.style.use('ggplot')
plt.subplots(figsize=(10,8))
sns.barplot(y=type_df.index,x='Total', data=type_df)
plt.xticks(fontsize=12)
plt.yticks(fontproperties=prop,fontsize=12)
plt.xlabel('电影数量', fontproperties=prop,fontsize=12)
plt.ylabel('电影类型',fontproperties=prop, fontsize=12)
plt.title('最受欢迎的前10种电影类型', fontproperties=prop, fontsize=16)

电影数据集数据分析 电影数据分析报告_scrapy_05


图 5 最受欢迎的10种电影类型


可以看出全球前100名电影最受欢迎的还是动作、冒险、科幻、奇幻等更为震撼和吸引人的题材,相比之下犯罪、爱情、惊悚等类型要出彩难得多。

5.2 不同类型电影数和上映时间之间的关系

根据5.1节所得到的最受欢迎的电影类型,我们想研究一下这100部电影中,不同类型电影数和上映时间之间存在着什么关系,我们取前10种最受欢迎类型的电影进行绘图,如下图所示。从中可以看到,近几年动作、冒险、科幻等题材的电影发布数量有较大的增加,这可能是因为消费者偏好这些电影类型,电影市场很大程度地向其倾斜。

电影数据集数据分析 电影数据分析报告_Python_06


图 6 最受欢迎的10种电影类型上映数和上映时间之间的关系


下面是本节所使用的代码:

year_min=dataF['year'].min()
year_max=dataF['year'].max()
year_gen=pd.DataFrame(index=types_CNT, columns=range(int(year_min),int(year_max)+1))
year_gen.fillna(value=0, inplace=True)
year_gen.head()
z=0
for i in types_rnt:
    for j in list(i):
        year_gen.loc[j, int(yearArray[z])]=year_gen.loc[j, int(yearArray[z])]+1
    z += 1
#绘制子图的函数
def setplot_TypeTime(i,data,type):
  plt.rcParams['font.family']=['SimHei']
  plt.plot(data.loc[type].T)
  plt.title(str(i)+'.'+type+'类型电影和时间的关系', fontsize=16)
  plt.xlabel('电影上映时间')
  plt.ylabel('电影数')
  plt.xticks(range(1982,2023,5))
  plt.ylim([0,10])
  plt.legend([type])
#绘图
plt.rcParams['font.family']=['SimHei']
data=year_gen
plt.figure(figsize=(10, 10))
for i in range(1,11):
  plt.subplot(5,2,i)
  type=type_df.index[:][i-1]
  setplot_TypeTime(i,data,type)
  plt.tight_layout()

5.3 电影排名和上映时间的关系

我们还可以查看一下电影排名和时间之间的关系,如下图所示。图中的纵坐标是排名,横坐标是上映的时间,随着排名越低(对应着图更上方的位置),散点图的颜色越浅,可以看到,排名前20名的电影大都在近10年上映,这说明现代的电影拍摄手法以及剧情较之以前有了很大的提升,观众愿意为这些新电影买单。但其中有一个特例,上映时间是1997年,经过查看是排名第三的电影《泰坦尼克号》,可见真正的经典之作是不会被人们所遗忘的。

电影数据集数据分析 电影数据分析报告_scrapy_07


图 7 电影排名和上映时间的关系


#绘图
plt.subplots(figsize=(10,8))
sc1=plt.scatter(dataF['year'][:],dataF['number'][:],s=100,c=dataF['number'][:],cmap='hot')
plt.colorbar(sc1)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.xlabel('电影上映时间')
plt.ylabel('电影排名')
plt.title('电影排名和上映时间的关系',fontsize=16)
plt.show()

上面这个图可能不太直观,我们可以取每一年排名最高(数字最小)的电影作为代表数据,描绘电影最好排名和上映时间之间的关系,如下图所示。可以得出同样的结论:近几年的电影排名都跻身到了前20,说明随着时间的演进,有越来越多优质的影片出现,占据老影片排名的位置。

电影数据集数据分析 电影数据分析报告_电影数据集数据分析_08


图 8 电影最好排名和上映时间之间的关系


5.4 电影总数和上映时间的关系

根据5.3,我们直观的体会是,随着时间的推进,优质电影的数量在不断增长。和5.2不同,这次我们按照年份对电影进行分组并记数,绘制了下图。可以看到,总体趋势上,优质电影的数量确实是随着时间在增加的,这一方面得益于电影拍摄手法以及剧情较之以前有了很大的提升,使电影的制作周期缩短,另一方面则是因为观众的消费水平和收入水平随着时间演进有所提升,使得近些年来的这些电影票房增加,排名随之冲到了前面。

电影数据集数据分析 电影数据分析报告_电影数据集数据分析_09


图 9 电影数量和上映时间的关系


下面是本节的代码:

year_count=[]
for y in range(year_min,year_max+1):
    ye=dataF[dataF['year']==y]
    if ye.empty:
        year_count.append(0)
    else:
        df=ye.groupby(['year'])['title'].count()
        year_count.append(df[y])
#绘图
plt.subplots(figsize=(10,8))
plt.bar(range(year_min,year_max+1),year_count,color='#539caf')
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.xlabel('电影上映时间')
plt.ylabel('电影数量')
plt.title('电影数量和上映时间的关系',fontsize=16)
plt.show()

5.5 最受欢迎的10位导演

这100部电影中的导演谁最擅长拍摄呢,谁的电影最受欢迎呢?同样的,我们可以对这100部电影按照导演的名字进行分组并记数,绘制如下的条形图。可以看到最受欢迎的10位导演大都还是欧美的导演,中国的电影也在不断发展,但还需要继续努力。

电影数据集数据分析 电影数据分析报告_爬虫_10


图 10 最受欢迎的10位导演


下面是本节的关键代码:

#创建dirctor的series对象
se_dirctor_number=dataF.groupby(['director'])['title'].count().sort_values(ascending=False)
print(se_dirctor_number[:10])

#把series对象转化为dataFrame对象
dit_dirctor_number = {'director':se_dirctor_number.index,'numbers':se_dirctor_number.values}
df_dirctor_number = pd.DataFrame(dit_dirctor_number)
print(df_dirctor_number[:10])

#绘图
plt.subplots(figsize=(10,8))
sns.barplot(y='director',x='numbers', data=df_dirctor_number[:10])
plt.xticks(fontsize=12)
plt.yticks(fontproperties=prop,fontsize=12)
plt.xlabel('拍摄电影数量', fontproperties=prop,fontsize=12)
plt.ylabel('电影类型',fontproperties=prop, fontsize=12)
plt.title('最受欢迎的十位导演', fontproperties=prop, fontsize=16)

5.6 电影平均票房排名前10的电影类型

在5.1我们已经统计了前10种最受欢迎的电影类型,这是根据题材分类进行统计的,更进一步的,如果对这些同类型的电影票房取平均获取它们的平均票房,那么是否还是按照这个顺序呢?平均票房的计算按照下式计算:
电影数据集数据分析 电影数据分析报告_电影数据集数据分析_11
式中的 电影数据集数据分析 电影数据分析报告_数据分析_12表示同类型平均票房, 电影数据集数据分析 电影数据分析报告_数据分析_13表示同类型电影的总票房,而 电影数据集数据分析 电影数据分析报告_爬虫_14则代表同类型的电影数量。

绘制图像如下,可以看到爱情、灾难、犯罪片反而是平均票房最高的电影类型,这和5.1前10种最受欢迎的电影类型的结论似乎有点矛盾,但是由于本文只考虑了全球前100名的电影数据,且前两名爱情和灾难类型的电影中包含总票房第三的《泰坦尼克号》,总票房数较高,样本数量又少,所以平均票房就出现了下图的情况。

电影数据集数据分析 电影数据分析报告_数据分析_15


图 11 电影平均票房排名前10的电影类型


下面是本节的代码:

part_df=dataF[['title','type','year','boxoffice']].reset_index(drop=True)
part_df.head()
movie_types = types_CNT
for mt in movie_types:
    part_df[mt]=0
    z=0
    for g in types_rnt:
        if mt in list(g):
            part_df.loc[z,mt]=1
        else:
            part_df.loc[z,mt]=0
        z+=1
boxoffice_means = []
for mt in movie_types:
    i=part_df.groupby(mt, as_index=True)['boxoffice'].mean()
    boxoffice_means.append(i)
part_df.head()
mean_list = [m[1] for m in boxoffice_means]
mean_df=pd.DataFrame(movie_types)
mean_df.rename(columns={0:"type"}, inplace=True)
mean_df['average_boxoffice']=mean_list
mean_df.head()
s1=mean_df.sort_values(['average_boxoffice'],ascending=False)[:10]
#绘图
plt.subplots(figsize=(10,8))
sns.barplot(x='average_boxoffice', y='type',data=s1)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.xlabel('平均票房')
plt.ylabel('电影类型')
plt.title('电影平均票房排名前10的电影类型',fontsize=16)
plt.show()

5.7 电影票房和电影排名之间的关系

网站中的这个电影排名是根据什么规则排出来的呢?我们大胆猜测,电影票房越高,电影排名越好,我们根据网站中的数据绘制了电影票房和电影排名之间关系的图像,如下图所示。可以看出我们的猜测是正确的,网站的电影票房确实和电影排名存在严格的负相关关系。

电影数据集数据分析 电影数据分析报告_数据分析_16


图 12 电影票房和电影排名之间的关系


下面是本节的关键代码:

#绘图
plt.subplots(figsize=(10,8))
plt.plot(dataF['number'], dataF['boxoffice'])
plt.scatter(dataF['number'], dataF['boxoffice'],s=10)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.xlabel('电影排名')
plt.ylabel('电影票房')
plt.title('电影票房和电影排名之间的关系',fontsize=16)
plt.show()

6 总结

本文利用Scrapy爬虫技术对全球前100名的电影进行了数据获取和分析,从数据中我们可以看出电影的行业在不断地发展和进步,越来越多的好电影不断涌现,电影市场也在向科幻、动作等强视觉效果的类型不断拓展。下一个票房冠军是什么电影呢?我们拭目以待。

参考文献

[1] Scrapy入门教程.https://www.runoob.com/w3cnote/scrapy-detail.html