龙空技术网

Python3爬虫及数据分析实战:猫眼电影榜

老Q量化投研 348

前言:

现在姐妹们对“python电影推荐大数据分析”大约比较讲究,同学们都需要知道一些“python电影推荐大数据分析”的相关文章。那么小编也在网摘上网罗了一些关于“python电影推荐大数据分析””的相关资讯,希望小伙伴们能喜欢,看官们一起来了解一下吧!

一、抓取网页源代码

import matplotlib as mpl

mpl.use('agg')

%matplotlib inline

import requests

import re

import pandas as pd

import time

import seaborn as sns

sns.set()

mpl.rcParams['font.sans-serif']=[u'SimHei']

mpl.rcParams['axes.unicode_minus']=False

requests是一个强大的模块,可以帮我们模拟绝大多数的浏览器网络请求,这次我们使用它的get方法来获取网页的源代码。

def get_one_page(url, headers):

'''

抓取单个网页的源码

'''

# 添加headers参数是为了伪装成浏览器,避免被反爬虫策略封禁

response = requests.get(url, headers=headers)

# 200意味着成功的请求

if response.status_code == 200:

return response.content.decode('utf-8')

return None

通过观察,我们可以看到猫眼电影TOP100页面的url地址是,其中0可以替换成10、20、……、90。这是因为TOP100榜单分了十页,每页十部电影,这个可替换的数字参数相当于每页的电影的第一部的序号。这里的编号跟Python中的编号规则一致,从0开始。

# 设置猫眼电影TOP100的url

# 为了方便,我们使用列表推导式来实现url的列举

urls = ['{0}'.format(i) for i in range(0, 100, 10)]

# 用header来假装自己是浏览器,这一部分可以通过浏览器的检查功能来找到,不清楚的可以百度搜索一下,非常简单。

headers = {

'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

}

# 先把所有网页源码爬下来

data = []

for url in urls:

tmp = get_one_page(url, headers=headers)

if not tmp == None:

data.append(tmp)

time.sleep(0.5)

# 我们查看一下爬取的网页数量是否符合预期

print('{0} pages crawled'.format(len(data)))

10 pages crawled

十个网页,符合预期,那接下来我们就应该解析数据了。

二、解析网页数据

Python中存在许多网页解析库,比如使用bs4中的BeautifulSoup、通过lxml使用xpath、使用pymysql,这些都是常用且好用的方案。而这次,我们要自讨苦吃,通过re模块,使用正则表达式的方法来解析数据。

关于正则表达式的语法和规则,可以自行百度。为了简单易懂,我们可以把不同数据的解析拆分开来,通过多个正则表达式的解析来实现各个字段的数据提取。但这种办法有一些缺点,比如网站的源码中对于缺失数据的处理不合预期时,就可能导致某些字段出现缺失数据,这样不同字段的数据列表长度就产生了差异,我们就无法简单地进行合并了。

事实上,针对这种列表式展示内容的网页,针对每个条目,它的不同字段都是放在一起的,前后顺序一般也是固定的。因此,假如我们将每个条目的所有字段一起解析,就可以方便地应对字段缺失的问题了。

不过猫眼电影TOP100的榜单应该是有小编进行手动维护的,所以数据比较规整,暂时不用考虑这个问题。

# 利用正则表达式,解析电影名、主演、排名、上映时间、分数数据# 使用re.compile将各个正则表达式封装成正则表达式对象,方便后边解析使用。re.S参数是为了让'.'能匹配空格。actor_pattern = re.compile('<p\sclass="star">\s*(.*?)\s*</p>', re.S)title_pattern = re.compile('class="name".*?movieId.*?>(.*?)</a></p>', re.S)index_pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>', re.S)time_pattern = re.compile('<p\sclass="releasetime">(.*?)</p>', re.S)score_pattern = re.compile('<p\sclass="score"><i\sclass="integer">(\d+)\.</i><i\sclass="fraction">(\d+)</i></p>', re.S)# 使用列表来存储数据indexes = []actors = []titles = []release_times = []scores = []# 循环解析十个网页,将解析出来的数据附加在对应的列表中for page in data: indexes.extend(re.findall(index_pattern, page)) titles.extend(re.findall(title_pattern, page)) actors.extend(re.findall(actor_pattern, page)) release_times.extend(re.findall(time_pattern, page)) scores.extend(re.findall(score_pattern, page))# 清洗主演、上映时间、上映国家或地区、评分数据actors = [i.strip('主演:') for i in actors]# 可以看到,上映地区的数据在上映时间后边的括号里,有很多电影上映时间后边没有括号了,通过观察我们发现这些都是中国大陆上映的电影,# 那我们就将这些默认缺失的部分补充为'中国'locs = [i.strip('上映时间:')[10:].strip('()') if len(i.strip('上映时间:')) > 10 else '中国' for i in release_times]# 我们把字符串中‘上映时间:’这些没用的去掉,然后取十位,也就是'YYYY-mm-dd'的长度,事实上这一步我们也可以在正则表达式中解决,# 比如用'\d'匹配数字等,详细的大家可以自己尝试,这样还可以解决数据格式不符合预期的问题。# 事实上电影天空之城的上映时间的格式还真的跟其他的不一样,不过此次我们不考虑这个问题release_times = [i.strip('上映时间:')[:10] for i in release_times]# 网页里边将分数的个位数与小数用了不同的格式,所以解析的时候我们分开提取了它们,因此需要处理一下scores = [int(i) + int(j)/10 for i, j in scores]

pandas是Python中数据分析的一个神器,它的很多功能和用法都借鉴了R语言。

这里我们就使用DataFrame来存储并分析数据。

# 生成DataFrame

df = pd.DataFrame({

'rank': indexes,

'title': titles,

'actor': actors,

'release_time': release_times,

'score': scores,

'location': locs

})

# 修改列名

df = df[['rank', 'title', 'actor', 'score', 'location', 'release_time']]

# 保存到本地csv文件中

df.to_csv('./maoyan_top100_movie.csv', index=False)

# 展示一下数据

df.head()

三、数据分析

1. 上映时间分布

首先,我们看一下猫眼TOP100电影都是什么年头的。

# 我们的上映日期是以字符串存储的,需要将上映年份解析出来

df['上映年份'] = df['release_time'].map(lambda x: int(x[:4]))

df['上映年份'].value_counts()

2011 9

2010 7

2013 6

1993 5

2012 5

1994 5

2008 5

2006 4

1998 4

2003 4

2002 4

2001 3

2000 3

1997 3

1999 3

2004 3

1992 3

1965 2

2009 2

2014 2

1954 1

1966 1

1957 1

2017 1

1953 1

1974 1

1940 1

1972 1

1995 1

1975 1

1984 1

1987 1

1988 1

1989 1

1990 1

2015 1

2007 1

1939 1

Name: 上映年份, dtype: int64

虽然我们能看到有不少电影集中分散在千禧年之后的某几年,比如2010-2013年就占了100部电影中的27部,但是这样数据看起来还是太过分散,我们可以考虑以5年为一个区间将数据分布集中起来。

df['上映年份区间'] = pd.cut(df['上映年份'], bins=[1938, 1980, 1990, 1995, 2000, 2005, 2010, 2015, 2018])df['上映年份区间'].value_counts().sort_index().plot(kind='bar')

可以看到,年头近一些的电影还是更符合当代人的口味,那么我们看看最古老和最新的电影分别是什么。

df.iloc[df['上映年份'].idxmin()]

rank 10

title 乱世佳人

actor 费雯·丽,克拉克·盖博,奥利维娅·德哈维兰

score 9.1

location 美国

release_time 1939-12-15

上映年份 1939

上映年份区间 (1938, 1980]

Name: 9, dtype: object

df.iloc[df['上映年份'].idxmax()]

rank 100

title 英雄本色

actor 狄龙,张国荣,周润发

score 9.2

location 中国

release_time 2017-11-17

上映年份 2017

上映年份区间 (2015, 2018]

Name: 99, dtype: object

可以看到,最古老的电影是1939年上映的由费雯·丽主演的《乱世佳人》,鼎鼎大名,名不虚传。数洞更感兴趣的,是最新的电影《英雄本色》,这部电影面世三十多年之后,经过4K技术的修复,在国内正式上映,着实赚了不少忠实粉丝的眼泪。小马哥不是一个角色,而是一个时代。

2. 上映地区分布

看完了上映时间情况,我们再看看上映地区的信息。

df['location'].value_counts().plot(kind='bar')

可以看到,大陆片和美国片最受大家欢迎,日本韩国也有一定受众,素有浪漫之风的法国、意大利紧随其后,中国香港排名第七有些出乎意料,看来当年港片的辉煌已经一去不复返了。

3. 分数情况

我们来看看这TOP100电影评分的分布情况如何:

df.groupby('score')['title'].count().sort_index().plot(kind='bar')
df.score.hist(bins=5)

看来高分电影还是很稀有的,即使在TOP100中,评分在9.5以上的仅有5部。另外分数越高电影越少这一现象也符合预期。

那接下来呢,我们看看哪个地区的电影评分更高:

import matplotlib.pyplot as pltplt.figure(figsize=(20,8))sns.boxplot(x='location', y='score', data=df)

可以看到,香港和意大利虽然量少,但整体风评更好,韩国电影相对来说评价较差。

那我们再看看哪些年头的电影评价更好:

plt.figure(figsize=(20,8))sns.boxplot(x='上映年份区间', y='score', data=df)

看起来1995-2000年的电影质量相当不错

接下来我们综合上映地区和年份来看看分数的情况:

df_heat = df.groupby(['上映年份区间', 'location'])['score'].mean().reset_index().pivot('上映年份区间', 'location', 'score')cmap = sns.diverging_palette(220, 10, as_cmap=True)sns.heatmap(df_heat, center=8.8, annot=True, cmap=cmap, linewidths=.5)
df_heat = df.groupby(['上映年份区间', 'location'])['title'].count().reset_index().pivot('上映年份区间', 'location', 'title')cmap = sns.diverging_palette(220, 10, as_cmap=True)sns.heatmap(df_heat, center=1, annot=True, cmap=cmap, linewidths=.5)

嗯……由于总共只有100部电影,分不到这么多区间里还是太过稀疏,数据量较大的时候基本上就不会出现这种情况了。数据这么稀疏的情况下,我们看到的结果可能不太具有代表性,不过在此我们就不纠结这个问题。

df['rank'] = df['rank'].map(int)sns.lmplot(x='score', y='rank', data=df)

榜单排名和评分大致呈反比,即分数越高,排名越靠前,符合预期。

4. 演员情况

我们使用collections中的defaultdict来统计每个演员入榜的电影数:

from collections import defaultdict

actor_movie_cnt = defaultdict(int)

for index, row in df.iterrows():

for actor in row['actor'].split(','):

actor_movie_cnt[actor] += 1

sorted(actor_movie_cnt.items(), key=lambda x: x[1], reverse=True)[:10]

[('张国荣', 6),

('周星驰', 4),

('梁朝伟', 4),

('巩俐', 3),

('阿尔·帕西诺', 3),

('莫文蔚', 3),

('克里斯蒂安·贝尔', 3),

('布拉德·皮特', 3),

('加里·奥德曼', 2),

('娜塔莉·波特曼', 2)]

前三名分别是哥哥、星爷以及小编心目中最帅的男人之一——梁朝伟,这三位小编都非常喜欢。

到此为止,我们就完成了猫眼TOP100的抓取,也进行了简单的描述统计分析,下次我们再考虑下其他的网页解析工具的使用。

标签: #python电影推荐大数据分析