电影评论爬取与分析
用户电影评论分析
from snapshot_selenium import snapshot as driver
from pyecharts.render import make_snapshot
from pyecharts.globals import CurrentConfig,NotebookType
CurrentConfig.NOTEBOOK_TYPE=NotebookType.JUPYTER_LAB
import pandas as pd
import json
datas=[]
with open("review_final.json","r",encoding="utf-8") as f:
for line in f.readlines():
data=list(json.loads(line).values())
datas.append(data)
f.close()
import csv
with open('review_final.csv', 'w', encoding='utf-8', newline='') as csvFile:
csv.writer(csvFile).writerow(['用户id','用户名','评论内容','用户链接','电影id','电影title','电影tag','电影评分','导演','电影类型','国家','片长','发布时间'])
for rows in datas:
csv.writer(csvFile).writerow(rows)
df=pd.read_csv('review_final.csv',engine='python',encoding='utf-8')
df.shape
(85692, 13)
df.head(5)
用户id | 用户名 | 评论内容 | 用户链接 | 电影id | 电影title | 电影tag | 电影评分 | 导演 | 电影类型 | 国家 | 片长 | 发布时间 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | phoebe627 | 小月 | 标题和故事的idea大概出自松本清张的《富士山禁恋》,一个有能力的左右逢源的男人的妻子出轨,... | https://www.douban.com/people/phoebe627/ | 30405087 | 火口的两人 | 爱情 | 7.0 | 霍建起 | ['剧情', '爱情'] | 中国大陆 | 96分钟 | 2020-03-31 12:04:50 |
1 | yukiyuko | 口袋_肥力士 | 吃饭睡觉聊天打豆豆【误】两位演员的表演都很自然平实,音乐也好听。其实有的时候人和人在一起,做... | https://www.douban.com/people/yukiyuko/ | 30405087 | 火口的两人 | 爱情 | 7.0 | 霍建起 | ['剧情', '爱情'] | 中国大陆 | 96分钟 | 2020-03-25 20:16:00 |
2 | luhua | 芦哲峰 | 特别喜欢这没羞没臊的两个人,没日没夜没完没了地做啊做,在床上做,在桌上做,在路边胡同里做,在... | https://www.douban.com/people/luhua/ | 30405087 | 火口的两人 | 爱情 | 7.0 | 霍建起 | ['剧情', '爱情'] | 中国大陆 | 96分钟 | 2020-04-01 09:33:19 |
3 | luzhiyu | 陆支羽 | 4.0。两个想过去死的人。 | https://www.douban.com/people/luzhiyu/ | 30405087 | 火口的两人 | 爱情 | 7.0 | 霍建起 | ['剧情', '爱情'] | 中国大陆 | 96分钟 | 2020-03-17 01:54:49 |
4 | 99556117 | 八屋 | (被开动了贤者模式) | https://www.douban.com/people/99556117/ | 30405087 | 火口的两人 | 爱情 | 7.0 | 霍建起 | ['剧情', '爱情'] | 中国大陆 | 96分钟 | 2020-03-23 19:13:52 |
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 85692 entries, 0 to 85691
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 用户id 85692 non-null object
1 用户名 85685 non-null object
2 评论内容 85691 non-null object
3 用户链接 85692 non-null object
4 电影id 85692 non-null int64
5 电影title 85692 non-null object
6 电影tag 85692 non-null object
7 电影评分 85692 non-null float64
8 导演 85692 non-null object
9 电影类型 85692 non-null object
10 国家 85692 non-null object
11 片长 85692 non-null object
12 发布时间 83504 non-null object
dtypes: float64(1), int64(1), object(11)
memory usage: 8.5+ MB
df.index=range(len(df))
1.用户评论次数分析
df['用户id'].value_counts()
qijiuzhiyue 731
luzhiyu 578
ifanmu 564
lingrui1995 558
3540441 475
...
23260274 1
168654517 1
zxc_suki 1
qingyewuchen 1
65507794 1
Name: 用户id, Length: 17959, dtype: int64
x=list(df['用户id'].value_counts().index)[:10]
x
['qijiuzhiyue',
'luzhiyu',
'ifanmu',
'lingrui1995',
'3540441',
'zhangzongqian',
'ruo1996',
'tjz230',
'diewithme',
'xilouchen']
y=list(df['用户名'].value_counts().head(10))
y
[731, 578, 564, 558, 475, 462, 428, 418, 413, 356]
data_pairs=[i for i in zip(x,y)]
from pyecharts import options as opts
from pyecharts.charts import Pie
p1=(
Pie(init_opts=opts.InitOpts(width="1000px",height="600px"))
.add(
series_name="用户评论数量",
data_pair=data_pairs
)
.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="{b}:{c}"))
)
make_snapshot(driver,p1.render("Top10-user.html"),"Top10-user.png")
2.用户观看电影类型分析
user=df[df['用户id']==x[0]]
user['电影类型'].index
Int64Index([ 167, 182, 234, 300, 475, 510, 527, 540, 664,
775,
...
82568, 82803, 83268, 83584, 84110, 84198, 84446, 84505, 85283,
85410],
dtype='int64', length=731)
user['电影类型']
167 ['剧情', '爱情']
182 ['剧情', '喜剧', '悬疑', '犯罪']
234 ['爱情', '动画']
300 ['剧情', '爱情']
475 ['剧情', '动作', '冒险']
...
84198 ['动作', '奇幻']
84446 ['动作', '奇幻']
84505 ['动作', '奇幻']
85283 ['喜剧', '动作', '动画']
85410 ['喜剧', '动作', '动画']
Name: 电影类型, Length: 731, dtype: object
user_type=user.loc[:,'电影类型']
x=user_type.value_counts()
x
['剧情', '喜剧'] 50
['剧情', '家庭'] 47
['剧情'] 42
['动作', '科幻', '冒险'] 37
['剧情', '爱情'] 33
..
['爱情', '动画'] 1
['悬疑', '惊悚', '恐怖'] 1
['剧情', '传记'] 1
['剧情', '动作'] 1
['科幻', '恐怖'] 1
Name: 电影类型, Length: 82, dtype: int64
res="".join(list(x.index))
res
"['剧情', '喜剧']['剧情', '家庭']['剧情']['动作', '科幻', '冒险']['剧情', '爱情']['动作', '科幻', '奇幻', '冒险']['动作', '科幻', '动画', '冒险']['剧情', '爱情', '战争']['喜剧', '爱情']['剧情', '动画', '冒险']['动作', '奇幻']['剧情', '喜剧', '传记']['喜剧', '动作', '犯罪']['剧情', '同性', '音乐', '传记']['爱情', '动画', '奇幻']['动作', '惊悚', '冒险']['剧情', '爱情', '悬疑', '战争']['剧情', '家庭', '犯罪']['科幻', '悬疑', '惊悚']['喜剧', '动作', '冒险']['剧情', '悬疑', '犯罪']['喜剧', '动作', '悬疑', '犯罪']['科幻', '惊悚']['动作', '惊悚', '犯罪']['喜剧', '动作']['剧情', '爱情', '同性']['剧情', '喜剧', '冒险']['剧情', '犯罪']['动作']['剧情', '惊悚', '犯罪']['惊悚', '恐怖']['科幻', '惊悚', '恐怖']['喜剧']['剧情', '家庭', '传记']['爱情']['动画', '奇幻']['喜剧', '动画', '奇幻']['喜剧', '爱情', '奇幻']['剧情', '喜剧', '爱情', '战争']['剧情', '喜剧', '动画', '奇幻']['动作', '犯罪']['动作', '惊悚']['剧情', '传记', '犯罪']['喜剧', '动画', '奇幻', '冒险']['喜剧', '惊悚', '奇幻', '冒险']['科幻', '悬疑', '犯罪']['剧情', '悬疑', '恐怖']['喜剧', '动画', '冒险']['动画']['科幻', '冒险', '灾难']['动作', '奇幻', '冒险']['剧情', '爱情', '情色', '传记']['动作', '悬疑', '奇幻', '武侠', '古装']['剧情', '悬疑']['动作', '灾难']['剧情', '动作', '冒险']['剧情', '喜剧', '动作']['剧情', '动作', '武侠']['剧情', '爱情', '情色']['动作', '恐怖']['恐怖']['惊悚', '犯罪']['剧情', '动作', '科幻', '动画']['剧情', '科幻', '动画']['动作', '动画', '犯罪']['喜剧', '动作', '动画']['剧情', '喜剧', '悬疑', '犯罪']['喜剧', '爱情', '家庭']['科幻', '动画']['喜剧', '同性']['剧情', '惊悚', '历史']['喜剧', '动作', '奇幻', '冒险']['喜剧', '情色']['动作', '战争']['动作', '动画', '冒险']['动作', '动画', '家庭']['奇幻', '古装']['爱情', '动画']['悬疑', '惊悚', '恐怖']['剧情', '传记']['剧情', '动作']['科幻', '恐怖']"
import re
pattern=re.compile(r'\w+')
s=pattern.findall(res)
from collections import Counter
res=Counter(s)
x=[]
y=[]
for k,v in res.items():
x.append(k)
y.append(v)
data_pairs=[]
for k,v in res.items():
data_pairs.append((k,v))
data_pairs
[('剧情', 32),
('喜剧', 23),
('家庭', 5),
('动作', 28),
('科幻', 12),
('冒险', 15),
('爱情', 13),
('奇幻', 13),
('动画', 17),
('战争', 4),
('传记', 6),
('犯罪', 13),
('同性', 3),
('音乐', 1),
('惊悚', 12),
('悬疑', 10),
('恐怖', 7),
('灾难', 2),
('情色', 3),
('武侠', 2),
('古装', 2),
('历史', 1)]
from pyecharts import options as opts
from pyecharts.charts import Bar
b1=(
Bar(init_opts=opts.InitOpts(width="1000px",height="600px"))
.add_xaxis(x)
.add_yaxis("电影类型次数",y)
.set_global_opts(
title_opts=opts.TitleOpts(title="用户观看电影类型分析"),
datazoom_opts=opts.DataZoomOpts(),
)
)
make_snapshot(driver,b1.render("User_movie.html"),"User_movie.png")
3.用户某个电影分析
users=list(df['用户id'].value_counts().index)[:10]
users
['qijiuzhiyue',
'luzhiyu',
'ifanmu',
'lingrui1995',
'3540441',
'zhangzongqian',
'ruo1996',
'tjz230',
'diewithme',
'xilouchen']
user_1=df[df['用户id']==users[0]]
user_1['电影title'].value_counts()
阴曹使者 36
好莱坞往事 20
星球大战9:天行者崛起 20
刺猬的优雅 20
第一滴血5:最后的血 20
..
一百样东西 2
朋克武士 2
大人物 1
虽然只是弄丢了手机 1
我的100分男友 1
Name: 电影title, Length: 65, dtype: int64
user_1=df[df['用户id']==users[0]]
user_1[user_1['电影title']=='阴曹使者'].info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 36 entries, 922 to 84505
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 用户id 36 non-null object
1 用户名 36 non-null object
2 评论内容 36 non-null object
3 用户链接 36 non-null object
4 电影id 36 non-null int64
5 电影title 36 non-null object
6 电影tag 36 non-null object
7 电影评分 36 non-null float64
8 导演 36 non-null object
9 电影类型 36 non-null object
10 国家 36 non-null object
11 片长 36 non-null object
12 发布时间 36 non-null object
dtypes: float64(1), int64(1), object(11)
memory usage: 3.9+ KB
comment="".join(user_1['评论内容'].to_list())
pattern = re.compile(r"[^a-zA-Z0-9\u4e00-\u9fa5]")
comment = pattern.sub('',comment)
import jieba
from pyecharts import options as opts
from pyecharts.charts import WordCloud
def split_word(comment):
word_list=list(jieba.cut(comment))
with open("stop_words.txt",encoding="utf-8") as f:
meaningless_word=f.read().splitlines()
result=[]
for i in word_list:
if i not in meaningless_word:
result.append(i.replace(" ",""))
return result
def word_counter(words):
words_counter=Counter(words)
words_list=words_counter.most_common(100)
return words_list
def word_cloud(data):
w=(
WordCloud()
.add(
series_name="用户评论词云",
data_pair=data,
word_size_range=[10,120],
shape="cursive"
)
.set_global_opts(
title_opts=opts.TitleOpts(title="用户评论",title_textstyle_opts=opts.TextStyleOpts(font_size=23)),
tooltip_opts=opts.TooltipOpts(is_show=True)
)
)
return w
def main():
words=split_word(comment)
data=word_counter(words)
w=word_cloud(data)
return w
if __name__=="__main__":
w1=main()
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\lenovo\AppData\Local\Temp\jieba.cache
Loading model cost 1.450 seconds.
Prefix dict has been built successfully.
make_snapshot(driver,w1.render('User_word.html'),"User_word.png")
4.电影评论客观词云分析
df['电影title'].value_counts()
阴曹使者 4740
秦明·生死语者 2400
刺猬的优雅 2380
路边野餐 2320
幻视 1780
...
生日乐园 540
我的100分男友 240
一百样东西 240
少林寺十八罗汉 60
龙牌之谜 60
Name: 电影title, Length: 71, dtype: int64
comment1=df[df["电影title"]=="阴曹使者"]
comment1="".join(set(comment1['评论内容'].to_list()))
pattern = re.compile(r"[^a-zA-Z0-9\u4e00-\u9fa5]")
comment1 = pattern.sub('',comment1)
def split_word(comment):
word_list=list(jieba.cut(comment))
with open("stop_words.txt",encoding="utf-8") as f:
meaningless_word=f.read().splitlines()
result=[]
for i in word_list:
if i not in meaningless_word:
result.append(i.replace(" ",""))
return result
def word_counter(words):
words_counter=Counter(words)
words_list=words_counter.most_common(200)
return words_list
def word_cloud(data):
w=(
WordCloud()
.add(
series_name="电影评论客观词云",
data_pair=data,
word_size_range=[10,120],
shape="cursive"
)
.set_global_opts(
title_opts=opts.TitleOpts(title="电影评论客观词云",title_textstyle_opts=opts.TextStyleOpts(font_size=23)),
tooltip_opts=opts.TooltipOpts(is_show=True)
)
)
return w
def main():
words=split_word(comment1)
data=word_counter(words)
w=word_cloud(data)
return w
if __name__=="__main__":
w2=main()
make_snapshot(driver,w2.render('Movie_word.html'),"Movie_word.png")
5.用户观看电影类别分析
movie_type=df['国家'].value_counts()
x=list(movie_type.index)[:10]
y=movie_type.to_list()[:10]
x
[' 美国',
' 日本',
' 韩国',
' 中国大陆',
' 美国 / 英国',
' 英国 / 法国',
' 美国 / 中国大陆',
' 法国',
' 美国 / 新西兰',
' 西班牙']
y
[18680, 15600, 13362, 11590, 2800, 1680, 1680, 1620, 1620, 1560]
data_pairs=[z for z in zip(x,y)]
from pyecharts import options as opts
from pyecharts.charts import Funnel
f1=(
Funnel()
.add(
series_name="",
data_pair=data_pairs
)
.set_global_opts(
title_opts=opts.TitleOpts(title="电影国家比重"),
legend_opts=opts.LegendOpts(type_="scroll",pos_left="0%",pos_top="20%",orient="vertical")
)
)
make_snapshot(driver,f1.render("电影国家比重.html"),"电影国家比重.png")
6.电影月份评分
data=df.dropna()
data.index=range(len(data))
def Month(s):
s=s.split('-')
if s[1]=="01":
return "一月"
elif s[1]=="02":
return "二月"
elif s[1]=="03":
return "三月"
else:
return "四月"
data["电影月份"]=data["发布时间"].apply(Month)
E:\Anaconda\envs\mypython\lib\site-packages\ipykernel_launcher.py:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
"""Entry point for launching an IPython kernel.
month_one=data[data['电影月份']=="一月"]["电影评分"]
one_result=float(sum(month_one.to_list())/len(month_one.to_list()))
one_result=float(str(one_result)[:4])
one_result
6.91
month_one=data[data['电影月份']=="一月"]["电影评分"]
one_result=float(sum(month_one.to_list())/len(month_one.to_list()))
month=["一月","二月","三月","四月"]
def average_rate():
rate=[]
for i in month:
month_res=data[data['电影月份']==i]["电影评分"]
result=float(sum(month_res.to_list())/len(month_res.to_list()))
result=float(str(result)[:4])
rate.append(result)
return rate
rate=average_rate()
month
['一月', '二月', '三月', '四月']
rate
[6.91, 6.97, 6.88, 6.85]
from pyecharts import options as opts
from pyecharts.charts import Line
l1=(
Line(init_opts=opts.InitOpts(width="1000px",height="600px"))
.add_xaxis(month)
.add_yaxis("电影评分",rate)
.set_global_opts(
title_opts=opts.TitleOpts(title="评分")
)
)
make_snapshot(driver,l1.render("每月评分.html"),"每月评分.png")
7.电影类别评分
types=df['电影tag'].value_counts()
x=list(types.index)
y=types.to_list()
from pyecharts import options as opts
from pyecharts.charts import Pie
p2=(
Pie()
.add("电影Tag",
[z for z in zip(x,y)]
)
.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="{b}:{c}"))
)
make_snapshot(driver,p2.render("电影类别统计.html"),"电影类别统计.png")
8.各种电影评分
def average_rate():
rate=[]
for i in x:
month_res=df[df['电影tag']==i]["电影评分"]
result=float(sum(month_res.to_list())/len(month_res.to_list()))
result=float(str(result)[:4])
rate.append(result)
return rate
rate=average_rate()
rate
[5.82,
8.42,
6.85,
6.9,
7.39,
6.04,
6.57,
5.5,
6.25,
8.84,
8.29,
5.0,
5.74,
6.58,
9.25,
6.89]
from pyecharts import options as opts
from pyecharts.charts import Pie
from pyecharts.faker import Faker
p3 = (
Pie()
.add(
"",
[list(z) for z in zip(x,rate)],
radius=["40%", "55%"],
label_opts=opts.LabelOpts(
position="outside",
formatter="{a|{a}}{abg|}\n{hr|}\n {b|{b}: }{c} {per|{d}%} ",
background_color="#eee",
border_color="#aaa",
border_width=1,
border_radius=4,
rich={
"a": {"color": "#999", "lineHeight": 22, "align": "center"},
"abg": {
"backgroundColor": "#e3e3e3",
"width": "100%",
"align": "right",
"height": 22,
"borderRadius": [4, 4, 0, 0],
},
"hr": {
"borderColor": "#aaa",
"width": "100%",
"borderWidth": 0.5,
"height": 0,
},
"b": {"fontSize": 16, "lineHeight": 33},
"per": {
"color": "#eee",
"backgroundColor": "#334455",
"padding": [2, 4],
"borderRadius": 2,
},
},
),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="电影类别平均评分"),
legend_opts=opts.LegendOpts(type_="scroll",pos_left="0%",pos_top="20%",orient="vertical"),
toolbox_opts=opts.ToolboxOpts(is_show=True)
)
)
make_snapshot(driver,p3.render('电影类别平均评分.html'),"电影类别平均评分.png")
类别柱状图
from pyecharts import options as opts
from pyecharts.charts import Bar
b2=(
Bar(init_opts=opts.InitOpts(width="1000px",height="600px"))
.add_xaxis(x)
.add_yaxis("电影Tag",rate)
.set_global_opts(
title_opts=opts.TitleOpts(title="电影Tag评分"),
datazoom_opts=opts.DataZoomOpts(),
)
)
make_snapshot(driver,b2.render('电影Tag2.html'),"电影Tag2.png")
9.电影导演分析
df["导演"].value_counts()
安东尼·明格拉 2800
是枝裕和 2520
福田雄一 2500
金容华 2460
鲍勃·佩尔西凯蒂 1900
...
彼得·博格 60
森义隆 40
郑建国 40
欧·帕克 40
J·A·巴亚纳 20
Name: 导演, Length: 158, dtype: int64
x=list(df["导演"].value_counts().index)[:10]
def average_rate():
rate=[]
for i in x:
month_res=df[df['导演']==i]["电影评分"]
result=float(sum(month_res.to_list())/len(month_res.to_list()))
result=float(str(result)[:4])
rate.append(result)
return rate
rate=average_rate()
rate
[7.94, 8.0, 6.15, 6.29, 6.65, 7.49, 8.79, 7.0, 6.59, 8.26]
from pyecharts import options as opts
from pyecharts.charts import Line
b3=(
Bar(init_opts=opts.InitOpts(width="1000px",height="600px"))
.add_xaxis(x)
.add_yaxis("电影Tag",rate)
.set_global_opts(
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15)),
title_opts=opts.TitleOpts(title="电影导演评分"),
#datazoom_opts=opts.DataZoomOpts(),
)
)
make_snapshot(driver,b3.render("导演评分.html"),"导演评分.png")
十.图形合并
from pyecharts.charts import Page
page=Page(layout=Page.DraggablePageLayout)
page.add(p1,b1,w1,w2,f1,l1,p2,p3,b2,b3)
page.render('用户电影评论分析.html')
'F:\\Python\\Pytest\\spider\\movietest2\\用户电影评论分析.html'
代码链接:代码