美文网首页数据分析
使用python抓取豆瓣top250电影数据进行分析

使用python抓取豆瓣top250电影数据进行分析

作者: Mr_dvbkhm | 来源:发表于2018-02-26 21:36 被阅读1057次

    抓取豆瓣Top250电影数据的链接和电影名称

    代码如下:

    import urllib.request as urlrequest
    from bs4 import BeautifulSoup
    import re
    import csv,codecs 
    
    top250_url ='https://movie.douban.com/top250?start={}&filter='
    movie_name='名称'
    movie_assess='评价人数'
    movie_score='评分'
    movie_url ='链接'
    movie_intro='介绍'
    movie_num =0
    
    #print('{} {} {} {} {}'.format(movie_name,movie_assess,movie_score,movie_url,movie_intro))
    with open('top250_movie.csv','w',encoding='utf8') as outputfile:
        #outputfile.write(codecs.BOM_UTF8)
        writer = csv.writer(outputfile)
        #writer.writerow(["movie_num","movie_name","movie_assess","movie_score","movie_url","movie_intro"])
        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')
                 moviessoup = BeautifulSoup(movies_html,'html.parser')
                 all_list = moviessoup.find_all(class_='item')
                 #print(all_list)
                 for item in all_list:
                     item_data=item.find(class_='pic')
                     movie_url = item_data.find('a')['href']
                     movie_name = item_data.find('img')['alt']
                     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:
                         movie_intro = item.find(class_='quote').find(class_='inq').get_text()
                     except Exception as e:
                         movie_intro='None'  
                            
                     movie_num =movie_num+1
                     #print(movie_assess)
                     #print(item_assissent)
                     # item_assisent = item_data.find(name='span',attrs={'property':'v:average'})
                    
                     #抓取电影上映年份、 导演、主演等信息
                     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:]
                     #print(movie_director)
                     movie_role = movie_actor_infos[1]
                    
                     movie_year_area = movie_actor_infos[1].lstrip().split('\xa0/\xa0')
                     movie_year = movie_year_area[0]
                     #print(movie_year)
                     movie_country = movie_year_area[1]
                     #print(movie_country)
                     
                     movie_type = movie_year_area[2]
                     #print(movie_type)
                        
                     #print('{} {} {} {} {} {} {} {} {} {}'.format(movie_num,movie_name,movie_year,movie_country,movie_type,movie_director,movie_assess,movie_score,movie_url,movie_intro))
                    
                     #writer.writerow([movie_num,movie_name,movie_assess,movie_score,movie_url,movie_intro])
    
                     if movie_type =='':
                        movie_type='NULL'
                         
                     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

    预览数据
    import numpy as np
    import pandas as pd
    
    df = pd.read_csv('top250_movie.csv',sep='#',encoding='utf8')
    df.head()
    
    预览数据
    查看数据基本信息
    df.info()
    
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 250 entries, 0 to 249
    Data columns (total 10 columns):
    movie_num         250 non-null int64
    movie_name        250 non-null object
    movie_year        250 non-null object
    movie_country     250 non-null object
    movie_type        250 non-null object
    movie_director    250 non-null object
    movie_assess      250 non-null int64
    movie_score       250 non-null float64
    movie_url         250 non-null object
    movie_intro       250 non-null object
    dtypes: float64(1), int64(2), object(7)
    memory usage: 19.6+ KB
    

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

    重复值检查
    df.duplicated().value_counts()
    False    250
    dtype: int64
    #检查是否有重名电影
    len(df.movie_name.unique())
    outline[113]:250
    

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

    查看国家或地区参与电影制作的排名情况

    对于 country 列,有些电影由多个国家或地区联合制作:

    country =df['movie_country'].str.split(' ').apply(pd.Series)
    country
    
    国家和地区联合制作详情

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

    all_country = country.apply(pd.value_counts).fillna('0')
    all_country.columns = ['area1','area2','area3','area4','area5']
    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['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)
    all_country.head()
    

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

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

    关于电影类型的字段分析

    all_type = df['movie_type'].str.split(' ').apply(pd.Series)
    all_type.head(10)
    

    得到如下结果


    电影类型的预览
    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','type6']
    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['type6'] = all_type['type6'].astype(int)
    all_type.head(10)
    
    all_type['all_counts'] = all_type['tpye1']+all_type['type2']+all_type['type3']+all_type['type4']+all_type['type5']+all_type['type6']
    
    all_type = all_type.sort_values(['all_counts'],ascending=False)
    all_type.head(10)
    

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

    all_type = all_type.apply(pd.value_counts)
    all_type.unstack().head()
    0  传记      2.0
       儿童      2.0
       冒险      7.0
       剧情    163.0
       动作     15.0
    dtype: float64
    

    此时数据为 Series ,去掉空值,并通过 reset_index() 转化为 Dataframe :

    all_type = all_type.unstack().dropna().reset_index()
    all_type.head(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的类型
    all_type_m.head(10)
    
    电影类型汇总情况
    处理时间
    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
    df.head(10)
    
    上榜次数最多的导演
    # 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’的两列
    

    得到结果如下


    上榜次数最多的导演
    评分和排名的关系
    #排名和评分的关系
    
    import matplotlib.pyplot as plt
    import matplotlib
    %matplotlib inline
    
    #配置中文字体和修改字体大小
    matplotlib.rcParams['font.family'] = 'SimHei'
    matplotlib.rcParams['font.size'] = 20
    
    plt.figure(figsize=(20,5))
    plt.subplot(1,2,1)
    plt.scatter(df['movie_score'],df['movie_num'])
    plt.xlabel('movie_score')
    plt.ylabel('movie rank')
    #修改y轴为倒序
    plt.gca().invert_yaxis()
    
    #集中趋势的直方图
    plt.subplot(1,2,2)
    plt.hist(df['movie_score'],bins=15)
    
    #电影排名和评分的相关性检测
    df['movie_score'].corr(df['movie_num'])
    #out[35]:-0.69237508495035771
    

    得到结果如下

    评分和排名的关系 评分的集中趋势

    评分大多是集中在 8.3 - 9.2 之间,随评分的升高,豆瓣Top250排名名次也提前,Pearson相关系数为
    -0.6923,为强相关性

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

    得到如下结果

    国家或者地区上榜数的排名情况

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

    挖掘其他可用数据
    通过豆瓣开放平台的接口,我们可以知道在top250的电影数据中,有给出了这部电影的一些关键词并统计了统计数量,例如肖生克的救赎——>"https://api.douban.com/v2/movie/1292052"
    开放平台的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': '经典...
    
    加工数据
    #去掉头和尾
    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()
    

    得到如下结果

    修改列名后的结果

    为了便于直观的了解上榜的电影中那些标签非常常见,我们使用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 安装即可。

    参考文章:
    http://mp.weixin.qq.com/s/lq8sMQfiXbOV9K74UEUXow

    相关文章

      网友评论

        本文标题:使用python抓取豆瓣top250电影数据进行分析

        本文链接:https://www.haomeiwen.com/subject/thxzdxtx.html