Python爬虫之猫眼APP电影数据(十八)

原创不易,转载前请注明博主的链接地址:Blessy_Zhu https://blog.csdn.net/weixin_42555080
本次代码的环境:
运行平台: Windows
Python版本: Python3.x
IDE: PyCharm

0 前言

好久没有写爬虫了,为了让自己的不至于对爬虫那么陌生,于是准备拿猫眼App电影的数据进行上手。你肯定不会忘记今年上映的《复仇者联盟4:终局之战》,满满的回忆、满满的震撼。接下里我们就用这个电影作为爬取数据的例子进行分析。
 


在这里插入图片描述

一声响指,宇宙间半数生命灰飞烟灭。几近绝望的复仇者们在惊奇队长(布丽·拉尔森 饰)的帮助下找到灭霸(乔什·布洛林 饰)归隐之处,却得知六颗无限宝石均被销毁,希望彻底破灭。如是过了五年,迷失在量子领域的蚁人(保罗·路德 饰)意外回到现实世界,他的出现为幸存的复仇者们点燃了希望。与美国队长(克里斯·埃文斯 饰)冰释前嫌的托尼(小罗伯特·唐尼 饰)找到了穿越时空的方法,星散各地的超级英雄再度集结,他们分别穿越不同的时代去搜集无限宝石。而在这一过程中,平行宇宙的灭霸察觉了他们的计划。 注定要载入史册的最终决战,超级英雄们为了心中恪守的信念前仆后继……
在这里插入图片描述

一、猫眼数据简介

1.1 PC端与APP端数据对比

在博文Python爬虫之豆瓣电影评论数据的爬取(十四),我曾经爬取过豆瓣电影评论数据,那个相对来说比较简单,为什么呢?因为它都是静态网页,只需要更改爬取评论的url,然后解析就可以了。但是今天要爬取的猫眼数据呢?他可没有这么简单!!!
在猫眼PC端的网页中,只存在最热门的10条热评数据,这显示是不够支撑我们进行后续的数据分析的。

猫眼PC端网页地址: https://maoyan.com/films/248172

在这里插入图片描述
手机APP网页版本,即

猫眼移动端网页地址:https://m.maoyan.com/movie/248172/comments?_v_yes

可以看到,这里面有我们需要的全部数据,为了获得较多的数据并进行分析,那就来爬去手机APP端的数据吧!!!!通过下面可以清楚的知道,这个又是通过Ajax进行异步传输的数据。对这块内容有所遗忘的可以参考博文:AJAX数据爬取基本认识及原理,这里就不再具体介绍了!!!
 


在这里插入图片描述

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:
 


在这里插入图片描述

通过对评论数据进行分析,得到如下信息:

  • 返回的是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
        }

这篇文章就到这里了,欢迎大佬们多批评指正,也欢迎大家积极评论多多交流。
 

 
在这里插入图片描述

  • 13
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值