原创不易,转载前请注明博主的链接地址:Blessy_Zhu https://blog.csdn.net/weixin_42555080
本次代码的环境:
运行平台: Windows
Python版本: Python3.x
IDE: PyCharm
0 前言
好久没有写爬虫了,为了让自己的不至于对爬虫那么陌生,于是准备拿猫眼App电影的数据进行上手。你肯定不会忘记今年上映的《复仇者联盟4:终局之战》,满满的回忆、满满的震撼。接下里我们就用这个电影作为爬取数据的例子进行分析。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6353f0362294bbd3e2f6c6e488d18a91.png)
一声响指,宇宙间半数生命灰飞烟灭。几近绝望的复仇者们在惊奇队长(布丽·拉尔森 饰)的帮助下找到灭霸(乔什·布洛林 饰)归隐之处,却得知六颗无限宝石均被销毁,希望彻底破灭。如是过了五年,迷失在量子领域的蚁人(保罗·路德 饰)意外回到现实世界,他的出现为幸存的复仇者们点燃了希望。与美国队长(克里斯·埃文斯 饰)冰释前嫌的托尼(小罗伯特·唐尼 饰)找到了穿越时空的方法,星散各地的超级英雄再度集结,他们分别穿越不同的时代去搜集无限宝石。而在这一过程中,平行宇宙的灭霸察觉了他们的计划。 注定要载入史册的最终决战,超级英雄们为了心中恪守的信念前仆后继……
一、猫眼数据简介
1.1 PC端与APP端数据对比
在博文Python爬虫之豆瓣电影评论数据的爬取(十四),我曾经爬取过豆瓣电影评论数据,那个相对来说比较简单,为什么呢?因为它都是静态网页,只需要更改爬取评论的url,然后解析就可以了。但是今天要爬取的猫眼数据呢?他可没有这么简单!!!
在猫眼PC端的网页中,只存在最热门的10条热评数据,这显示是不够支撑我们进行后续的数据分析的。
猫眼PC端网页地址: https://maoyan.com/films/248172
手机APP网页版本,即
可以看到,这里面有我们需要的全部数据,为了获得较多的数据并进行分析,那就来爬去手机APP端的数据吧!!!!通过下面可以清楚的知道,这个又是通过Ajax进行异步传输的数据。对这块内容有所遗忘的可以参考博文:AJAX数据爬取基本认识及原理,这里就不再具体介绍了!!!
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b4d35465c3b37e75f62da1dc57b197bb.png)
1.2 猫眼数据分析
首先,先找到数据接口:{ http://m.maoyan.com/mmdb/comments/movie/248172.json?v=yes&offset=1&startTime=2019-07-13%2022:24:21 }
对于接口中的数据,可以通过一下JSON在线编辑器网址:http://www.bejson.com/jsoneditoronline/ 进行字典数据的编辑,使得数据更加的规整。效果如下:
对于接口连接,这里面的“248172”是每个电影对应的ID号,是该电影的唯一标识。如何找到呢?
打开猫眼电影主页:https://maoyan.com/ ,找到待爬取的电影《复仇者联盟4:终局之战》,可以看到它的URL如下,这个就是电影的唯一标识ID:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/34847d459a1e96322b014d377d7f7f18.png)
通过对评论数据进行分析,得到如下信息:
- 返回的是json格式数据
- 248172表示电影的专属id;offset表示偏移量;startTime表示获取评论的起始时间,从该时间向前取数据,即获取最新的评论
- cmts表示评论,每次获取15条,offset偏移量是指每次获取评论时的起始索引,向后取15条;通过下面的内容就可以看出这个端倪。
https://m.maoyan.com/review/v2/comments.json?movieId=248172&userId=-1&offset=0&limit=15&ts=0&type=3
https://m.maoyan.com/review/v2/comments.json?movieId=248172&userId=-1&offset=15&limit=15&ts=1563091719066&type=3
https://m.maoyan.com/review/v2/comments.json?movieId=248172&userId=-1&offset=30&limit=15&ts=1563091719066&type=3
https://m.maoyan.com/review/v2/comments.json?movieId=248172&userId=-1&offset=45&limit=15&ts=1563091719066&type=3
https://m.maoyan.com/review/v2/comments.json?movieId=248172&userId=-1&offset=60&limit=15&ts=1563091719066&type=3
- hcmts表示热门评论前10条
- total表示总评论数
二、代码实现
这里我用《复仇者联盟3:无限战争》作为代码演示的例子。
安装必要的Python库:
from urllib import request
import time
from datetime import datetime
from datetime import timedelta
2.1 获取数据get_data()并处理数据parse_data()
#获取数据
def get_data(url):
headers = {
'User_Agent' : 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36'
}
req = request.Request(url,headers = headers)
response = request.urlopen(req)
if response.getcode() == 200:
return response.read()
return None
# 处理数据
def parse_data(html):
data = json.loads(html)['cmts'] # 将str转换为json
comments = []
for item in data:
comment = {
'id': item['id'],
'nickName': item['nickName'],
'cityName': item['cityName'] if 'cityName' in item else '', # 处理cityName不存在的情况
'content': item['content'].replace('\n', ' ', 10), # 处理评论内容换行的情况
'score': item['score'],
'startTime': item['startTime']
}
comments.append(comment)
return comments
2.2 存储数据save_to_txt()
# 存储数据,存储到文本文件
def save_to_txt():
# 获取当前时间,从当前时间向前获取
start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
end_time = '2018-05-11 00:00:00'
# while start_time > end_time:
#因为是演示,所以只爬取前10页的数据
for i in range(10):
url = 'http://m.maoyan.com/mmdb/comments/movie/248170.json?_v_=yes&offset=' + str(15 * i) + '&startTime='+ start_time.replace(' ', '%20')
try:
html = get_data(url)
time.sleep(1)
except Exception as e:
time.sleep(3)
html = get_data(url)
else:
time.sleep(1)
comments = parse_data(html)
print(comments)
start_time = comments[14]['startTime'] # 获得末尾评论的时间
start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S') + timedelta(seconds=-1) # 转换为datetime类型,减1秒,避免获取到重复数据
start_time = datetime.strftime(start_time, '%Y-%m-%d %H:%M:%S') # 转换为str
for item in comments:
with open('comments.txt', 'a', encoding='utf-8') as f:
f.write(str(item['id']) + ',' + item['nickName'] + ',' + item['cityName'] + ',' + item[
'content'] + ',' + str(item['score']) + ',' + item['startTime'] + '\n')
爬虫结果:
2.3 粉丝位置数据可视化
这里使用的是pyecharts,pyecharts是一个用于生成Echarts图表的类库,便于在Python中根据数据生成可视化的图表。Echarts是百度开源的一个数据可视化JS库,主要用于数据可视化。
# 导入Style类,用于定义样式风格
from pyecharts import Style
# 导入Geo组件,用于生成地理坐标类图
from pyecharts import Geo
import json
# 导入Geo组件,用于生成柱状图
from pyecharts import Bar
# 导入Counter类,用于统计值出现的次数
from collections import Counter
# 数据可视化
def funsLoctions():
# 获取评论中所有城市
cities = []
with open('comments.txt', mode='r', encoding='utf-8') as f:
rows = f.readlines()
for row in rows:
city = row.split(',')[2]
if city != '': # 去掉城市名为空的值
cities.append(city)
# 对城市数据和坐标文件中的地名进行处理
#handle(cities)
# 统计每个城市出现的次数
data = Counter(cities).most_common() # 使用Counter类统计出现的次数,并转换为元组列表
print(data)
# 定义样式
style = Style(
title_color='#fff',
title_pos='center',
width=1200,
height=600,
background_color='#404a59'
)
# 根据城市数据生成地理坐标图
geo = Geo('《一出好戏》粉丝位置分布', '数据来源:猫眼电影数据', **style.init_style)
attr, value = geo.cast(data)
geo.add('', attr, value, visual_range=[0, 3500],
visual_text_color='#fff', symbol_size=15,
is_visualmap=True, is_piecewise=True, visual_split_number=10)
geo.render('粉丝位置分布-地理坐标图.html')
# 根据城市数据生成柱状图
data_top20 = Counter(cities).most_common(20) # 返回出现次数最多的20条
bar = Bar("《一出好戏》粉丝来源排行TOP20", "数据来源:猫眼电影数据", title_pos='center', width=1200, height=60)
attr, value = bar.cast(data_top20)
bar.add("", attr, value, is_visualmap=True, visual_range=[0, 3500], visual_text_color='#fff', is_more_utils=True,
is_label_show=True)
bar.render("粉丝来源排行-柱状图.html")
此时我的代码里面报了两个错误:
-
1 未找到pyecharts_snapshot库
解决办法如下:
官网下载pyecharts_snapshot 安装。
或者直接在pycharm中File–>Settings–>Project:XX–>Project Interpreter中添加pyecharts_snapshot -
2 报错:ValueError: No coordinate is specified for xxx(地名)
原因:pyecharts的坐标文件中没有该地名,实际上是名称不一致导致的,如数据中地名为’达州’,而坐标文件中为’达州市’
坐标文件所在路径:项目/venv/lib/python3.6/site-packages/pyecharts/datasets/city_coordinates.json
解决:修改坐标文件,在原位置下复制个同样的,然后修改下地名
{
"达州市": [
107.5,
31.22
],
"达州": [
107.5,
31.22
],
}
不过由于要修改的地名太多,上面的方法实在是麻烦,所以可以定义了一个函数,用来处理地名数据找不到的问题
# 处理地名数据,解决坐标文件中找不到地名的问题
def handle(cities):
# print(len(cities), len(set(cities)))
# 获取坐标文件中所有地名
data = None
with open('/项目绝对地址/venv/lib/python3.6/site-packages/pyecharts/datasets/city_coordinates.json',
mode='r', encoding='utf-8') as f:
data = json.loads(f.read()) # 将str转换为json
# 循环判断处理
data_new = data.copy() # 拷贝所有地名数据
for city in set(cities): # 使用set去重
# 处理地名为空的数据
if city == '':
while city in cities:
cities.remove(city)
count = 0
for k in data.keys():
count += 1
if k == city:
break
if k.startswith(city): # 处理简写的地名,如 达州市 简写为 达州
# print(k, city)
data_new[city] = data[k]
break
if k.startswith(city[0:-1]) and len(city) >= 3: # 处理行政变更的地名,如县改区 或 县改市等
data_new[city] = data[k]
break
# 处理不存在的地名
if count == len(data):
while city in cities:
cities.remove(city)
# 写入覆盖坐标文件
with open('/项目绝对地址/venv/lib/python3.6/site-packages/pyecharts/datasets/city_coordinates.json',mode='w', encoding='utf-8') as f:
f.write(json.dumps(data_new, ensure_ascii=False)) # 将json转换为str
效果如下:
2.4 评分星级可视化
# coding=utf-8
# 导入Pie组件,用于生成饼图
from pyecharts import Pie
# 获取评论中所有评分
rates = []
with open('comments.txt', mode='r', encoding='utf-8') as f:
rows = f.readlines()
for row in rows:
rates.append(row.split(',')[4])
# 定义星级,并统计各星级评分数量
attr = ["五星", "四星", "三星", "二星", "一星"]
value = [
rates.count('5') + rates.count('4.5'),
rates.count('4') + rates.count('3.5'),
rates.count('3') + rates.count('2.5'),
rates.count('2') + rates.count('1.5'),
rates.count('1') + rates.count('0.5')
]
pie = Pie('评分星级比例', title_pos='center', width=900)
pie.add("7-17", attr, value, center=[75, 50], is_random=True,
radius=[30, 75], rosetype='area',is_legend_show=False, is_label_show=True)
pie.render('评分.html')
结果,因为数据量较少,评价为二星的样本竟然没有:
2.5 评论词云可视化
# coding=utf-8
# 导入jieba模块,用于中文分词
import jieba
# 导入matplotlib,用于生成2D图形
import matplotlib.pyplot as plt
# 导入wordcount,用于制作词云图
from wordcloud import WordCloud
# 获取所有评论
comments = []
with open('comments.txt', mode='r', encoding='utf-8') as f:
rows = f.readlines()
for row in rows:
comment = row.split(',')[3]
if comment != '':
comments.append(comment)
# 设置分词
comment_after_split = jieba.cut(str(comments), cut_all=False) # 非全模式分词,cut_all=false
words = " ".join(comment_after_split) # 以空格进行拼接
# 设置词云参数,参数分别表示:画布宽高、背景颜色、字体、最大词的字体大小
wc = WordCloud(width=1024, height=768, background_color='white',font_path='STKAITI.TTF',max_font_size=400, random_state=50)
# 将分词后数据传入云图
wc.generate_from_text(words)
plt.imshow(wc)
plt.axis('off') # 不显示坐标轴
plt.show()
# 保存结果到本地
wc.to_file('wc.jpg')
效果展示:
三、总结
其实,从接口获取的数据维度还真不少,如下,截取了一个用户的猫眼数据,里面的数据大家可以自己仔细分析一下,用到什么就下载什么就可以了:
{
"approve": 0,
"approved": false,
"assistAwardInfo": {
"avatar": "",
"celebrityId": 0,
"celebrityName": "",
"rank": 0,
"title": ""
},
"authInfo": "",
"avatarurl": "https://img.meituan.net/avatar/77ec05f3cd886ec3eb9cde0ddbf3634c151592.jpg",
"cityName": "珠海",
"content": "简直太赞了,对于漫威迷来说,这部真的很走心了",
"filmView": false,
"gender": 2,
"id": 1071346062,
"isMajor": false,
"juryLevel": 0,
"majorType": 0,
"movieId": 248172,
"nick": "WDYGY",
"nickName": "WDYGY",
"oppose": 0,
"pro": false,
"reply": 0,
"score": 5,
"spoiler": 0,
"startTime": "2019-07-13 22:23:20",
"supportComment": true,
"supportLike": true,
"sureViewed": 1,
"tagList": {
"fixed": [
{
"id": 1,
"name": "好评"
},
{
"id": 4,
"name": "购票"
}
]
},
"time": "2019-07-13 22:23",
"userId": 245966367,
"userLevel": 2,
"videoDuration": 0,
"vipInfo": "",
"vipType": 0
}
这篇文章就到这里了,欢迎大佬们多批评指正,也欢迎大家积极评论多多交流。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5c34ad1b66500faccdc26169454a0ae3.png)