Pandas分析某眼TOP100电影都来自于谁?

本文详细介绍了从猫眼网站抓取TOP100电影数据的过程,包括数据抓取、清洗、处理及分析。通过Python的requests、numpy、pandas和matplotlib库,实现了数据的高效抓取和可视化分析,揭示了最受欢迎的明星、电影诞生年份、题材分布、国家地区和评分分布。

某眼TOP100电影数据分析

2018年有一段时间稍微看了一段时间数据分析,稀里糊涂地做过一些demo,后来忙于创业项目的开发,风风火火地搞了10个月,就把数据分析及挖掘的事情给搁置了。

鼠年春节发生了新冠病毒的疫情,被逼在家,除了看娃,还是要找些事情做,来充实一下自己(其实,来自内心的恐惧,强制自己好好梳理一下),于是,又开始重新学习数据挖掘及分析,准备通过一系列的案例,来系统地学完实操,不能再半途而废了。

下面就是做某眼TOP100电源分析的过程,以作记录:

准备数据

对于Python开发者来说,获取数据最简单的方式,就是盘它~,通过requests进行数据抓取,因为这只是一个简单的数据抓取,所以,没必要动用scrapy框架来做。

import re
import time
import requests
from bs4 import BeautifulSoup
from fake_useragent import UserAgent

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 设置显示中文所需字体
plt.rcParams['font.family'] = 'Songti SC'
# 设置负号的正常显示
plt.rcParams['axes.unicode_minus'] = False

%matplotlib inline

# 设置清晰度
%config InlineBackend.figure_format = 'retina'

# 更改设计风格,使用自带的形式进行美化,这是一个r语言的风格
plt.style.use('ggplot')

导入爬取数据及分析数据常用的第三方库,requests、numpy、pandas相信所有数据工程师都不陌生,最后,引入简单的数据可视化库matplotlib,通过图标显示更加直观。

网页抓取

数据抓取前,需要根据需求进行网页分析,都哪里获取所需的字段。

  • 分析排行榜网页构造
  • 根据规则抓取TOP榜10页
  • 根据电源ID构造URL抓取详情页
# 猫眼榜单Url
base_url = 'https://maoyan.com/board/4?offset=%d'
# 存储抓取数据
# film = [fid, rank, title, cover, roles, realease_time, score,
# genres, country, duration]
movies = []
# 反爬取设置
header = { 
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36',
    'Host': 'maoyan.com',
    'Referer': 'https://maoyan.com/board'
}

# 解析电影数据
def parse_movie(html):
    soup =  BeautifulSoup(html, 'lxml')
    dd_list = soup.find_all('dd')
    for item in dd_list:
        # 通过链接地址,正则匹配出ID
        fids = re.findall('\d+$', item.select_one('.image-link')['href'])
        movie = {
            'rank': item.select_one('.board-index').text, # 影片排名
            'fid': fids[0] if len(fids) > 0 else 0, # 影片ID, 通过正则匹配
            'title': item.select_one('.image-link')['title'],  # 影片名称
            'cover': item.select_one('.poster-default')['src'],  # 封面图
            'roles': item.select_one('.star').text.strip(),  # 演员信息
            'realease_time': item.select_one('.releasetime').text.strip(),  # 上映时间
            'score': item.select_one('.integer').text + item.select_one('.fraction').text  # 影片得分
        }
        movies.append(movie)

def crawl_film(fid):
    """抓取电源详情信息"""
    film_url = 'https://maoyan.com/films/%d' % int(fid)  # 构造电源详情页url
    headers = {
        'User-Agent': UserAgent().random,
        'Host': 'maoyan.com',
        'Referer': 'https://maoyan.com/board/4'
    }
    req = requests.get(film_url, headers=headers)
    req.encoding = 'utf-8'
    if req.status_code == 200:
        # 解析电影信息
        soup = BeautifulSoup(req.text, 'lxml')
        lis = soup.find_all('li', class_='ellipsis')
        print(lis)
        if len(lis) > 0:
            # 题材
            genres = []
            for a in lis[0].select('a'):
                genres.append(a.text)

            # 国家地区 时长
            country, duration = lis[1].text.strip().split('\n')
            duration = duration.replace('/', '').lstrip()

            # 附加信息
            movies_extra.append((fid, genres, country, duration))

准备工作完毕,开始执行~

# 构造10个页面url
for page in range(10):
    page_url = base_url % int(page*10)
    if page > 0:
        header['Referer'] = 'https://maoyan.com/board/4?offset=%d' % int((page-1)*10)
    
    # 发起请求
    req = requests.get(page_url, headers=header)
    
    print('request url[%s], page[%d], status code[%d]' % (page_url, page, req.status_code))
    
    if req.status_code == 200:
        # print(req.text)
        parse_movie(req.text)
    
    # 强制等待5秒
    time.sleep(5)

自定义构造榜单10页,抓取电影的基本信息,暂时存入movies列表里,然后,遍历movies获取电影的详情页内容,国家地区、电影题材、时长等。

# 详情页获取附加信息
movies_extra = []

for movie in movies:
    print('Request film[%d]...' % int(movie['fid']))
    
    crawl_film(movie['fid'])
    
    # 强制等待5秒
    time.sleep(5)

OK,至此数据准备完毕~

不得不说,猫眼电影在反爬方面,做的还是相当全面的,以下是遇到的问题:

  • 反爬1:页面跳转安全中心,解决方法:添加headers的Referer
  • 爬取中文乱码问题, 解决方法:req.encoding为utf-8
  • 反爬2:页面不存在问题, 解决方法:添加cookie信息
  • 反爬3:页面字体加密问题,解决方法:woff2otf

数据分析

数据导入pandas的DataFrame,并进行基本信息及扩展信息的合并。

df_movie = pd.DataFrame(movies)
df_movie_extra = pd.DataFrame(movies_extra, columns=['fid', 'genres', 'country', 'duration'])
# 合并DataFrame
df_movie_all = pd.merge(df_movie, df_movie_extra, on='fid')

再次,数据合并完整,对于原始数据我们最好保存下来,供以后再做学习与实践~

# 保存文件
df_movie_all.to_excel('./datas/maoyan_films_top100.xlsx', index=False)

数据清洗和处理

# 对字符串进行替换处理
df_movie_all.loc[:, 'duration'] = df_movie_all['duration'].str.replace('分钟', '')
df_movie_all.loc[:, 'roles'] = df_movie_all['roles'].str.replace('主演:', '')
# 1、通过替换,截取,日期转化等提取出年份
def repair_date(df_x):
    """分割出日期"""
    r = re.match(r'(\d{4})[\-(\d{2})]?[\-(\d{2})]?', df_x['realease_time'])
    if len(r.groups()) == 1:
        # 此次分析对月份及日数值关系不大,固定补全
        return '%s-06-01' % r.groups()[0]
    else:
        return '-'.join(r.groups())

# 对固定字符串进行替换处理
df_movie_all.loc[:, 'realease_time'] = df_movie_all['realease_time'].str.replace('上映时间:', '')
# 上映时间里含国家等字符串,通过Series.str.slice_replace替换处理
df_movie_all.loc[:, 'realease_time'] = df_movie_all['realease_time'].str.slice_replace(start=10, repl='')
# 补全日期
df_movie_all.loc[:, 'realease_time'] = df_movie_all.apply(repair_date, axis=1)
# datetime转化
df_movie_all.loc[:, 'realease_time'] = pd.to_datetime(df_movie_all['realease_time'])
# 提取出年份
df_movie_all.loc[:, 'year'] = df_movie_all['realease_time'].dt.year

# 2、其实,如果仅分析数据年份的需求,直接通过数据字符串中截取出年份,更为简单方法;

字符串及日期处理后,需要对国家、题材、主演等字段进行拆分,便于接下来的聚类统计。

# 拆分角色列
df_movie_all = df_movie_all.drop('roles', axis=1).join(
    df_movie_all['roles'].str.split(',', expand=True).stack()
    .reset_index(level=1, drop=True).rename('roles'))

# 拆分国家列
df_movie_all = df_movie_all.drop('country', axis=1).join(
    df_movie_all['country'].str.split(',', expand=True).stack()
    .reset_index(level=1, drop=True).rename('country'))

# 拆分题材列
df_movie_all.loc[:, 'genres'] = df_movie_all['genres'].str.strip("[\'\n\t\s\]")
df_movie_all = df_movie_all.drop('genres', axis=1).join(
    df_movie_all['genres'].str.split(',', expand=True).stack()
    .reset_index(level=1, drop=True).rename('genres'))
df_movie_all.loc[:, 'genres'] = df_movie_all['genres'].str.strip("\s\'")

分析1:最耀眼的明星排行

stars_data = df_movie_all.groupby('roles').agg({ 'fid': 'nunique' }).sort_values(by='fid', ascending=False)
stars_data.columns = ['电影数量']

# 主演太多,这里仅取前30个进行展示
stars_data = stars_data[:30]
stars_data = stars_data.sort_values(by='电影数量', ascending=True)

# 绘制柱状图
stars_data.plot.barh(figsize=(12, 8))
plt.xlabel('电影数量', fontsize=14)
plt.ylabel('明星', fontsize=14)
plt.title('各个明星主演的电影数量', fontsize=18)

明星主演

分析2:TOP100诞生于哪些年份?

year_data = df_movie_all.groupby('year').agg({ 'fid': 'nunique' })
year_data.columns = ['电影数量']

# 绘制柱状图
year_data.plot.bar(figsize=(12, 8))
plt.xticks(range(len(year_data.index.to_list())), year_data.index.to_list(), rotation=45)
plt.xlabel('年份', fontsize=14)
plt.ylabel('数量', fontsize=14)
plt.title('各个年份诞生电影的数量', fontsize=18)

电源大片盛产的年代

分析3:TOP100最受欢迎的题材

df_movie_all.loc[:, 'genres'] = df_movie_all['genres'].str.strip()
genres_data = df_movie_all.groupby('genres').agg({ 'fid': 'nunique' }).sort_values(by='fid', ascending=False)

freqs = genres_data['fid'].to_list()
print(freqs)
labels = ['%s / %d个' % (l, v) for l, v in zip(genres_data.index.to_list(), freqs)]

explode = []
for i in range(len(genres_data.index.to_list())):
    if i > 3:
        explode.append(0.01)
    else:
        explode.append(0.05)
        
# 绘制饼图
plt.figure(figsize=(12, 8))
plt.axis('equal')   # 饼图长宽相等
plt.pie(freqs, 
        labels=labels, 
        explode=explode,
        autopct='%1.1f%%', 
        shadow=False,
        startangle=150)
plt.legend(loc='upper right', 
           fontsize=12,
           bbox_to_anchor=(1.15, 1.0),
           borderaxespad=1.5)
plt.title('最受欢迎的电影题材分布', fontsize=18)

最受欢迎的电影题材

分析4:TOP100都诞生哪些国家地区?

country_data = df_movie_all.groupby('country').agg({ 'fid': 'nunique' }).sort_values(by='fid', ascending=False)

分析5:TOP100 评分分布

score_data = df_movie_all.groupby('score').agg({ 'fid': 'nunique' })

# 绘制折线图
plt.figure(figsize=(12, 8))
plt.plot(score_data)
plt.xlabel('评分', fontsize=14)
plt.ylabel('数量', fontsize=14)
plt.title('评分与数量的变化', fontsize=18)

电影评分
从图可以看出TOP100质量之高,最低评分8.8,更有甚者最高分9.6,特别的好奇,到底是哪个大师之作,能这么受欢迎?

# 查看第一条
top1_data = df_movie_all.loc[df_movie_all.score == 9.6][:1]

星爷
没错,就是星爷最经典大话西游之月光宝盒,这部让很多人爱情共鸣的大作。

至此,对电影从数据抓取、清洗、处理及分析全过程,全部结束,当然,还有很多好玩的点,有机会继续玩起来~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值