一、爬取电影评论
壬寅年春节贺岁电影《长津湖-水门桥》,为了分析这部剧的各项数据,用爬虫的方法从猫眼上爬取了4万多条电影评论。
1、为了防止地址被禁,使用了代理地址池进行爬取:
设置代理地址方法,可以从如下几个免费网站获取代理地址
免费代理ip_服务器http代理_最新ip代理_免费ip提取网站_国内外代理_66免费代理ip
89免费代理IP - 完全免费的优质HTTP代理IP供应平台
云代理 - 高品质http代理ip供应平台/每天分享大量免费代理IP
我们只需要代理IP地址和端口,按如下方式保存为list
从猫眼爬取评论,我是模拟手机端的请求地址进行爬取的。https://m.maoyan.com/mmdb/comments/movie/1446115.json?_v_=yes&offset=0
这里的1446115是在猫眼网站上点开你要爬取的电影,导航栏中会有电影的id
然后设置报文头的时候'User-Agent': UserAgent().random,防止限制爬取。猫眼爬取的数据是按最新日期开始,一次15条,如果不带日期只能爬取1000条,所以带上日期可以往前面的日期爬。
自己增加一个items,里面填写代理地址,爬取评论的代码如下:
import requests
import json
import pandas as pd
import random
from fake_useragent import UserAgent
import time,datetime
import openpyxl
ran_time=random.random() #请求间隔时长
now_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') #当前时间
end_time="2022-02-01 08:00:00" #电影上映时间
tomato = pd.DataFrame(columns=['date', 'score', 'city', 'comment', 'nick'])
def startTime(time):
if time>end_time:#不转换为时间戳格式,因为时间戳格式的URL无法显示cmts部分评论
run(time)
def run(date):
global id # 定义为全局变量
global starttime
global tomato
for i in range(67): # 页号,一般只显示前1000条,67*15》1000
# 要把时间里面的空格改为%20,否则URL不完整
proxies = random.choice(items)
url = 'https://m.maoyan.com/mmdb/comments/movie/1446115.json?_v_=yes&offset={}&startTime={}'.format(i * 15,date.replace(' ','%20'))
print(url)
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Host': 'm.maoyan.com',
# 'Referer': 'http://m.maoyan.com/movie/1446115/comments?_v_=yes',
'Connection': "keep-alive",
'Cookie': '_lxsdk_cuid=17ebd6fcb4396-0232fccb2b266b-f791539-1fa400*****',
'User-Agent': UserAgent().random
}
rsp = requests.get(url, headers=headers, proxies=proxies, verify=False)
try:
comments = json.loads(rsp.content.decode('utf-8'))['cmts']
for item in comments:
tomato = tomato.append({'date': item['startTime'], 'city': item['cityName'], 'score': item['score'],
'comment': item['content'], 'nick': item['nick']}, ignore_index=True)
starttime = item['startTime'] # 最后一条评论的时间
except: # 可能有一天不足1000条评论
continue
time.sleep(ran_time)
startTime(starttime)
if __name__ == '__main__':
run(time_excu)
tomato.to_excel("水门桥评论.xlsx", index=False)
这里我保存成了xlsx文件,自己的电脑csv文件有乱码,小伙伴们也可以尝试保存为csv文件。
因为requests.get 爬取的地址是https地址,设置代理池地址的时候,选择支持https的,requests请求中,proxies=proxies为使用代理, verify=False为获取https的url。执行玩会保存成一个xlsx的文件,我这里爬取了四天左右的评论,保存的文件结果如下:
二、数据可视化
先把打分结果用数据可视化显示出来,代码如下:
# -*- coding: utf-8 -*-
import pyecharts
from pyecharts.charts import Pie
from pyecharts.charts import Bar
from pyecharts import options as opts
import pandas as pd
def score_view(data):
grouped = data.groupby(by="score")["nick"].size()
grouped = grouped.sort_values(ascending=False)
index = grouped.index
values = grouped.values
# 柱状图
bar = Bar() #(init_opts=opts.InitOpts(width="600px",height="1200px",page_title="2021年GDP"))
bar.add_xaxis(index.tolist())
bar.add_yaxis("", values.tolist())
bar.set_global_opts(
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=10)),
title_opts=opts.TitleOpts(title="评分值分布"),
datazoom_opts=opts.DataZoomOpts(), #提供区域缩放的功能
)
bar.render_notebook()
bar.render('电影水门桥评分值.html')
pie = Pie()
pie.add("", [list(z) for z in zip(index.tolist(), values.tolist())],
radius=["30%", "75%"],
center=["40%", "50%"],
rosetype="radius")
pie.set_global_opts(
title_opts=opts.TitleOpts(title="各评分值占比"),
legend_opts=opts.LegendOpts(
type_="scroll", pos_left="80%", orient="vertical"
),
).set_series_opts(label_opts=opts.LabelOpts(formatter="{d}%"))
pie.render_notebook()
pie.render('水门桥各评分值占比.html')
if __name__ == '__main__':
df = pd.read_excel("水门桥评论.xlsx")
data = df.drop_duplicates(keep="first") # 删掉重复值
score_view(data)
得到的评分视图如下:
各评分值在总评分人数中的占比
有1.11%的人打分0分,5分最多,占79.86%
三、柱状图+折线各城市观影人数
观察了下爬取的数据,好多人的城市信息显示的是区、县或者县级市,统计的维度是从市的维度统计的,所以就把爬取的城市行政级别调整了下,因为库的原因,还有很多城市处理不到。所以整理了一线城市和新一线城市观影人数。
把县区处理为城市的代码如下:
import cpca
df2 = pd.DataFrame(data)
city_name = data["city"].values.tolist()
df_city = cpca.transform(city_name)
for i in range(len(df_city)):
if df_city['省'][i]!=None and df_city["市"][i]==None:
city_name[i] = df_city['省'][i].replace("市","")
elif df_city['省'][i]!=None and df_city["市"][i]!=None:
city_name[i] = df_city['市'][i].replace("市","")
# city_new = {"city_new":city_name}
df2["city_new"] = pd.Series(city_name)
用到的是cpca库,但是库里的数据不全,部分地名还是识别不出来。
从处理后的数据中提取一线、新一线城市的评论人数、评分等
city_num = df2["city_new"].value_counts()
city_first = ["北京","上海","广州","深圳"] #一线城市
city_new_first = ["武汉","南京","成都","重庆","杭州","天津","苏州","长沙","青岛","西安","郑州","宁波","无锡","大连"] #新一线
citys_front = city_first + city_new_first
nums = []
city_comment = city_num.index.tolist()
count_comment = city_num.values.tolist() #计算城市评论人数
for city in citys_front:
count = 0
for i in range(len(city_comment)):
if city_comment[i] ==city :
count += count_comment[i]
nums.append(count) #一线+新一线城市评论人数
city_first_score_mean = df2[df2.city.isin(citys_front)].groupby(["city"], as_index=False)["score"].mean() #一线+新一线平均评分
bar = Bar(init_opts=opts.InitOpts(width="1500px",height="800px",page_title="一线和新一线评论人数")) #(init_opts=opts.InitOpts(width="600px",height="1200px",page_title="2021年GDP"))
bar.add_xaxis(citys_front)
bar.add_yaxis("", nums)
bar.set_global_opts(
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=10)),
title_opts=opts.TitleOpts(title="一线和新一线评论人数"),
datazoom_opts=opts.DataZoomOpts(), #提供区域缩放的功能
)
line = (
Line()
.add_xaxis(citys_front)
.add_yaxis("", nums,label_opts = opts.LabelOpts(is_show=False))
)
bar.overlap(line)
bar.render_notebook()
bar.render('电影水门桥一线和新一线评论人数.html')
line = (
Line()
.add_xaxis(city_first_score_mean.city.values.tolist())
.add_yaxis("", city_first_score_mean.score.values.round(2).tolist(),label_opts = opts.LabelOpts(is_show=False))
)
effe = (
EffectScatter(init_opts=opts.InitOpts(width="2000px",height="750px"))
.add_xaxis(city_first_score_mean.city.values.tolist())
.add_yaxis("", city_first_score_mean.score.values.round(2).tolist())
.set_global_opts(title_opts=opts.TitleOpts(title="平均得分"))
)
effe.overlap(line)
# bar.render_notebook()
effe.render('城市平均评分值.html')
这里我用到了overlap,就是可以把图像叠加起来,所以可以看到柱状图上还有折线图。
得到的评论人数分布如下:
评论人数分布再对比城市人口基本能反映出每个城市喜欢看电影的程度,从数据来看,最具烟火味的成都人参与评论最多。杭州、天津应该是疫情影响人数较少。而北京看来比上海喜欢看电影。
评分平均值结果如下,实际页面是带有涟漪效果的:
评分结果虽然反映不了什么,但是大家可以拿来分析分析,也许跟人的素质,满足心有关。
四、生成词云
词云就是利用分词(NLP)的方法,把评论内容拆分,然后提取频率高的一些词,生成云一样的图,词语出现频率越高,词云上字体越大。
import pandas as pd
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
import jieba
import matplotlib.pyplot as plt
import sys
df = pd.read_excel("水门桥评论.xlsx")
words = " ".join(jieba.cut(df.comment.str.cat(sep=" ")))
stopwords = STOPWORDS
stopwords.add(u"电影")
wc = WordCloud(stopwords=stopwords,
font_path="C:/Windows/Fonts/simkai.ttf", # 解决显示口字型乱码问题
background_color="white",width=1000,height=880, max_words=100
)
my_wc = wc.generate_from_text(words)
plt.imshow(my_wc )
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False #防止中文不显示
plt.title(r"水门桥")
# plt.imshow(my_wc.recolor(color_func=image_colors), )
plt.axis("off")
plt.show()
得到的词云如下: