豆瓣top250电影数据分析

一. 数据抓取

# -*- coding:utf-8 -*-
import urllib.request as urlrequest
from bs4 import BeautifulSoup
import csv

"""
所需模块
request 模块
csv 模块
BeautifulSoup   模块
"""

top250_url = 'https://movie.douban.com/top250?start={}&filter='
movie_name = '名称'
movie_assess = '评价人数'
movie_score = '评分'
movie_url = '链接'
movie_intro = '介绍'
movie_num = 0

# 打开文件
with open('top250_movie.csv', 'w', encoding='utf8') as outputfile:
    #写文件
    writer = csv.writer(outputfile)
    # 写文件表头
    outputfile.write(
        "movie_num#movie_name#movie_year#movie_country#movie_type#movie_director#movie_assess#movie_score#movie_url#movie_intro\n")
    # 解析数据
    for list in range(10):
        # 获取网页数据
        movies_content = urlrequest.urlopen(top250_url.format(list * 25)).read()
        #设置编码格式
        movies_html = movies_content.decode('utf8')
        #解析HTML
        moviessoup = BeautifulSoup(movies_html, 'html.parser')
        # 获取数据列表
        all_list = moviessoup.find_all(class_='item')
        # 遍历列表,获取字段数据
        for item in all_list:
            # TODO 获取图片
            item_data = item.find(class_='pic')
            #获取图片链接
            movie_url = item_data.find('a')['href']
            # TODO 获取图片描述
            movie_name = item_data.find('img')['alt']
            # TODO 获取评分
            item_info = item.find(class_='star')
            info = item.find('div', attrs={'class': 'star'})
            # find_all 将star标签中的所有span 存入一个列表中
            movie_assess = info.find_all('span')[3].get_text()[:-3]
            movie_score = item_info.find('span', attrs={'class': 'rating_num'}).get_text()
            # 获取描述信息
            try:
                # TODO 获取描述
                movie_intro = item.find(class_='quote').find(class_='inq').get_text()
            except Exception as e:
                movie_intro = 'None'
            # 序号
            movie_num = movie_num + 1

            # TODO 抓取电影上映年份、 导演、主演等信息
            movie_actor_infos_html = item.find(class_='bd')
            # strip() 方法用于移除字符串头尾指定的字符(默认为空格)
            movie_actor_infos = movie_actor_infos_html.find('p').get_text().strip().split('\n')
            #  
            actor_infos1 = movie_actor_infos[0].split('\xa0\xa0\xa0')
            movie_director = actor_infos1[0][3:]
            # TODO 获取导演信息
            movie_role = movie_actor_infos[1]

            movie_year_area = movie_actor_infos[1].lstrip().split('\xa0/\xa0')
            # TODO 获取制作年份
            movie_year = movie_year_area[0]
            # TODO 获取地区
            movie_country = movie_year_area[1]
            # TODO 获取电影类型
            movie_type = movie_year_area[2]

            if movie_type == '':
                movie_type = 'NULL'
            # TODO 写出具体采集信息
            outputfile.write(
                '{}#{}#{}#{}#{}#{}#{}#{}#{}#{}\n'.format(movie_num, movie_name, movie_year, movie_country, movie_type,
                                                         movie_director, movie_assess, movie_score, movie_url,
                                                         movie_intro))

直接打开top350_movie.csv 文件可能会乱码,这是window下因为csv 文件编码格式为gbk

二、 数据清洗

  1. 预览数据
import pandas as pd

"""
 检查数据
"""
df = pd.read_csv('top250_movie.csv',sep='#',encoding='utf8')
df.head()
# 查看数据基本信息
df.info()

检查数据

共有250行 10个字段,没有缺失值。

  1. 重复值检查
import pandas as pd

"""
 检查数据
"""
df = pd.read_csv('top250_movie.csv',sep='#',encoding='utf8')
df.head()
# 查看数据基本信息
df.info()
# 重复值检查
count=df.duplicated().value_counts()
print(count)

重复值检查

没有重复项也没有重名的电影

  1. 查看国家或地区参与电影制作的排名情况
    对于 country 列,有些电影由多个国家或地区联合制作:
# 对于 country 列,有些电影由多个国家或地区联合制作
country =df['movie_country'].str.split(' ').apply(pd.Series)
print(country)
国家或地区参与

我们可以看到,有些国家甚至有5个国家或地区参与制作,对于这么多的空值,可以通过先按列计数,将空值 NaN 替换为“0”,再按行汇总。我们统计每个区域里相同国家的总数:

all_country = country.apply(pd.value_counts).fillna('0')
all_country.columns = ['area1','area2','area3','area4','area5','area6']
all_country['area1'] = all_country['area1'].astype(int)
all_country['area2'] = all_country['area2'].astype(int)
all_country['area3'] = all_country['area3'].astype(int)
all_country['area4'] = all_country['area4'].astype(int)
all_country['area5'] = all_country['area5'].astype(int)
all_country['area6'] = all_country['area6'].astype(int)

print(all_country)

得到如下结果:


国家的总数

三、数据分析

1. 得到一个国家或地区参与制作电影数的排名情况

接下来我们可以计算每个国家参与制作电影总数排名情况:

# 得到一个国家或地区参与制作电影数的排名情况
all_country['all_counts'] = all_country['area1']+all_country['area2']+all_country['area3']+all_country['area4']+all_country['area5']
#降序
all_country.sort_values(['all_counts'],ascending=False)
counts=all_country.head()

print(counts)
制作电影总数

2. 关于电影类型的字段分析

排名前10

# 关于电影类型的字段分析
all_type = df['movie_type'].str.split(' ').apply(pd.Series)
top10=all_type.head(10)
print(top10)
排名前10

修改NaN为0:

# 关于电影类型的字段分析
all_type = df['movie_type'].str.split(' ').apply(pd.Series)
all_type = all_type.apply(pd.value_counts).fillna('0')
all_type.columns = ['tpye1','type2','type3','type4','type5']
all_type['tpye1'] = all_type['tpye1'].astype(int)
all_type['type2'] = all_type['type2'].astype(int)
all_type['type3'] = all_type['type3'].astype(int)
all_type['type4'] = all_type['type4'].astype(int)
all_type['type5'] = all_type['type5'].astype(int)
# 计算总数
all_type['all_counts'] = all_type['tpye1']+all_type['type2']+all_type['type3']+all_type['type4']+all_type['type5']
all_type = all_type.sort_values(['all_counts'],ascending=False)
top10=all_type.head(10)
print(top10)
关于电影类型的字段分析

也可以通过 unstack 函数将行“旋转”为列,重排数据:

# 通过 unstack 函数将行“旋转”为列,重排数据,因为原来只有5列:
coltop=all_type.unstack().head()
print(coltop)
将行“旋转”为列
# 此时数据为 Series ,去掉空值,并通过 reset_index() 转化为 Dataframe :
all_type = all_type.unstack().dropna().reset_index()
dataframe=all_type.head(10)
print(dataframe)
电影类型的摘要

3. 获取电影类型数量前10的类型

all_type.columns =['level_0','level_1','counts']
all_type_m = all_type.drop(['level_0'],axis=1).groupby('level_1').sum()
all_type_m.sort_values(['counts'],ascending=False)
#获取电影类型数量前10的类型
#top10type=all_type_m.head(10)
print(all_type_m)
电影类型汇总情况
# 数据清洗
year_= df['movie_year'].str.split('(').apply(pd.Series)[0].str.strip()
year_split = pd.to_datetime(year_).dt.year
# 将年替换
df['movie_year'] = year_split
year=df.head(10)
print(year)
处理时间

4. 上榜次数最多的导演

# value_counts()返回一个Series 序列
director = df['movie_director'].value_counts()
#director.index  可以查看下标 director.values可以查看值
#series 转dataframe 可以使用字典的方式
myDirector = pd.DataFrame({'name':director.index,'counts':director.values})
#这样就生成了字段为‘name’ 和‘counts’的两列
print(myDirector)
上榜次数最多的导演

四、可视化

1. 评分和排名的关系

#排名和评分的关系
import matplotlib.pyplot as plt
import matplotlib
import pandas as pd

df = pd.read_csv('top250_movie.csv',sep='#',encoding='utf8')
#配置中文字体和修改字体大小
matplotlib.rcParams['font.family'] = 'SimHei'
matplotlib.rcParams['font.size'] = 20

# 显示大小 宽高
plt.figure(figsize=(10,6))
plt.scatter(df['movie_score'],df['movie_num'])
plt.xlabel('电影评分')
plt.ylabel('电影排名')
#修改y轴为倒序
plt.gca().invert_yaxis()

plt.show()
评分和排名的关系

2. 集中趋势的直方图

#排名和评分的关系
import matplotlib.pyplot as plt
import matplotlib
import pandas as pd

df = pd.read_csv('top250_movie.csv',sep='#',encoding='utf8')
#配置中文字体和修改字体大小
matplotlib.rcParams['font.family'] = 'SimHei'
matplotlib.rcParams['font.size'] = 20

# 显示大小 宽高
plt.figure(figsize=(10,6))

#集中趋势的直方图
plt.hist(df['movie_score'],bins=15)

plt.show()
评分的集中趋势

评分大多是集中在 8.3 - 9.2 之间,随评分的升高,豆瓣Top250排名名次也提前。

  1. 国家或者地区上榜数的排名情况
import matplotlib.pyplot as plt
import matplotlib
import pandas as pd


df = pd.read_csv('top250_movie.csv',sep='#',encoding='utf8')
#配置中文字体和修改字体大小
matplotlib.rcParams['font.family'] = 'SimHei'
matplotlib.rcParams['font.size'] = 20

# 对于 country 列,有些电影由多个国家或地区联合制作
country =df['movie_country'].str.split(' ').apply(pd.Series)

all_country = country.apply(pd.value_counts).fillna('0')
all_country.columns = ['area1','area2','area3','area4','area5','area6']
all_country['area1'] = all_country['area1'].astype(int)
all_country['area2'] = all_country['area2'].astype(int)
all_country['area3'] = all_country['area3'].astype(int)
all_country['area4'] = all_country['area4'].astype(int)
all_country['area5'] = all_country['area5'].astype(int)
all_country['area6'] = all_country['area6'].astype(int)
# 得到一个国家或地区参与制作电影数的排名情况
all_country['all_counts'] = all_country['area1']+all_country['area2']+all_country['area3']+all_country['area4']+all_country['area5']

country_rank = pd.DataFrame({'counts':all_country['all_counts']})
# 地区排序 并显示
show_conuntry=country_rank.sort_values(by='counts',ascending=False)
show_conuntry.plot(kind='bar',figsize=(14,6))
plt.show()
国家或者地区上榜数的排名情况

上榜数最多的国家是美国,中国大陆 排名第5

附:
通过豆瓣开放平台的接口,我们可以知道在top250的电影数据中,有给出了这部电影的一些关键词并统计了统计数量,例如肖生克的救赎:
豆瓣电影的API接口:
https://api.douban.com/v2/movie/1292052

1、获取正在热映的电影:
接口:https://api.douban.com/v2/movie/in_theaters
访问参数:
start : 数据的开始项
count:单页条数
city:城市
如:获取 广州热映电影 第一页 10条数据:
https://api.douban.com/v2/movie/in_theaters?city=广州&start=0&count=10
返回数据格式:(这里只列出app需要的字段)
key 类型 描述
count int 单页条数
start int 数据的开始项
total int 数据总条数
subjects json数组 电影列表
— id string 电影id
— title string 电影名中文名
— images json对象 存放各种大小的电影图
—— small/large/medium string 电影图url
— genres json数组 电影类型
— rating json对象 评分信息
—— average float 电影评分
— directors json数组 导演列表
—— name string 导演名
— casts json数组 主演列表
—— name string 主演名
— year int 年份
2、获取电影Top250:
接口:https://api.douban.com/v2/movie/top250
访问参数:
start : 数据的开始项
count:单页条数
如:获取电影Top250 第一页 10条数据:
https://api.douban.com/v2/movie/top250?start=0&count=10
返回数据格式:同上
3、电影搜索
接口:https://api.douban.com/v2/movie/search
访问参数:
start : 数据的开始项
count:单页条数
q:要搜索的电影关键字
tag:要搜索的电影的标签
如:搜索电影《神秘巨星》:
https://api.douban.com/v2/movie/search?q=神秘巨星&start=0&count=10
搜索喜剧类型的电影:
https://api.douban.com/v2/movie/search?tag=喜剧&start=0&count=10
返回数据格式:同上
4、电影详情
接口:https://api.douban.com/v2/movie/subject/:id
访问参数:电影id
如:电影《神秘巨星》的电影id为:26942674,搜索此电影的详细信息:
https://api.douban.com/v2/movie/subject/26942674
返回数据格式:
key 类型 描述
id string 电影id
title string 电影名中文名
original_title string 电影原名
images json对象 存放各种大小的电影图
— small/large/medium string 电影图url
genres json数组 电影类型
rating json对象 评分信息
— average float 电影评分
ratings_count int 评分人数
directors json数组 导演列表
— name string 导演名
—avatars json对象 各种大小的影人头像图
— small/large/medium string 头像图url
casts json数组 主演列表
— name string 主演名
—avatars json对象 各种大小的影人头像图
— small/large/medium string 头像图url
year int 年份
countries json数组 制片国家/地区
summary string 简介

  1. 数据采集
    开放平台的json数据:
    我们可以统计所有250部电影的tags 标签,看看上榜的电影中哪些标签的电影最多;
    我们使用json 来抓取豆瓣API的数据,为放置被豆瓣服务器封锁IP,我们使用动态代理服务器来爬取数据;
#通过豆瓣API接口来获取每个电影的标签信息
import urllib
import urllib.request as urlrequest
import json
import time
import random
import pandas as pd
df = pd.read_csv('F:/jupyter_workspace/top250_movie.csv',sep='#',encoding='utf8')
url_list = df['movie_url'].str.split('/').apply(pd.Series)

movieID_list =url_list[4]
print(movieID_list.size)
num = 0
#IP需定时更换,非长时有效
IP_list =['110.168.201.196:8888','216.245.222.106:8080','183.207.176.252:1080','67.149.77.18:21896']
IP = random.choice(IP_list)
with open ('top250_movie_json2.csv','w',encoding='utf8') as outputfile:
    outputfile.write("num#rank#alt_title#image#title#tags\n")

    proxy_support = urllib.request.ProxyHandler({'https':random.choice(IP_list)})
    opener = urllib.request.build_opener(proxy_support)
    #地址头伪装成火狐浏览器
    opener.addheaders = [('User-Agent','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30')]
    urllib.request.install_opener(opener)
    
    
    for id in movieID_list:
         url_visit = 'https://api.douban.com/v2/movie/{}'.format(id)
         print(url_visit)
         crawl_content = urlrequest.urlopen(url_visit).read()
         json_content =json.loads(crawl_content.decode('utf8'))
         rank = json_content['rating']['average']
         alt_title = json_content ['alt_title']
         image = json_content['image']
         title = json_content['title']
         tags = json_content['tags']
         num = num+1
         print(tags)
         outputfile.write('{}#{}#{}#{}#{}#{}\n'.format(num,rank,alt_title,image,title,tags))

         #time.sleep(1)

查看数据

json_df = pd.read_csv('F:/jupyter_workspace/top250_movie_json_data.csv',sep='#',encoding='utf8')
json_df['tags'].head()
out[130]:
0    [{'name': '经典', 'count': 180808}, {'name': '励志...
1    [{'name': '经典', 'count': 111157}, {'name': '中国...
2    [{'name': '经典', 'count': 138696}, {'name': '爱情...
3    [{'name': '励志', 'count': 167316}, {'name': '经典...
4    [{'name': '意大利', 'count': 67279}, {'name': '经典...
  1. 数据清洗
    加工数据
#去掉头和尾
json_df['tags']=json_df['tags'].str[3:-3]
json_tags=json_df['tags'][0]
json_tags
out[131]:
"name': '经典', 'count': 180808}, {'name': '励志', 'count': 152354}, {'name': '信念', 'count': 134338}, {'name': '自由', 'count': 119968}, 
{'name': '美国', 'count': 91446}, {'name': '人性', 'count': 84378}, {'name': '人生', 'count': 62969}, {'name': '剧情', 'count': 5434"
#再次整理数据,删除无效的字符,转换为数据集
tag_split = json_df['tags'].str.replace('name\': \'',' ').str.replace('\', \'count\': ',' ').str.replace('}, {\'','').str.split(' ').apply(pd.Series)
tag_split.head()

整理标签字段

#删除0列
del tag_split[0]
tag_split
#更改列名
tag_split.columns=['tag1','tag_count1','tag2','tag_count2','tag3','tag_count3','tag4','tag_count4','tag5','tag_count5','tag6','tag_count6','tag7','tag_count7','tag8','tag_count8']
tag_split.head()
  1. 数据分析
    为了便于直观的了解上榜的电影中那些标签非常常见,我们使用wordcloud制作词云
from wordcloud import WordCloud
text = tag_split[['tag1','tag2','tag3','tag4','tag5','tag6','tag7','tag8']].to_string(header=False,index=False)
#wordcloud = WordCloud(background_color='white').generate(text)
#from scipy.misc import imread
#读入背景图片
#bg_pic = imread('C://Users//Administrator//Desktop//1111.jpg')
#wordcloud = WordCloud(mask=bg_pic,background_color='white',scale=1.5).generate(text)
wordcloud = WordCloud(background_color='white',scale=1.5).generate(text)
plt.figure(figsize=(16,9))
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
乱码的词云

需要字体:
这是因为默认安装wordcloud 后使用的字体是DroidSansMono.ttf,我们需要把它改成msyh.ttf,在网上下载一个msyh.ttf ,放置到和DroidSansMono.ttf 同级目录下,再修改wordcloud.py文件中的FONT_PATH为msyh.ttf ;

FONT_PATH = os.environ.get("FONT_PATH", os.path.join(os.path.dirname(__file__),
                                                     "msyh.ttf"))

我的wordcloud.py 和DroidSansMono.ttf的路径是E:\program files\Miniconda\envs\python3.5\Lib\site-packages\wordcloud,每个人的不同,仅供参考,修改好了之后记得重启jupyter notebook ,不然不会及时生效,之后我们可以得到这样的词云结果

Top250中电影标签的词云概览

如果提示wordcloud 没有安装,需要使用pip install wordcloud,安装,如果还是无法安装成功,则需要下载wordcloud的whl 文件,下载地址为:http://www.lfd.uci.edu/~gohlke/pythonlibs/#wordcloud ,在终端进入whl 文件路径,使用 pip install xx.whl 安装即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容