• [Python] 通过采集两万条数据,对《无名之辈》影评分析


    、说明

      本文主要讲述采集猫眼电影用户评论进行分析,相关爬虫采集程序可以爬取多个电影评论。

      运行环境:Win10/Python3.5。

      分析工具:jieba、wordcloud、pyecharts、matplotlib。

      基本流程:下载内容 ---> 分析获取关键数据 ---> 保存本地文件 ---> 分析本地文件制作图表

      注意:本文所有图文和源码仅供学习,请勿他用,转发请注明出处!

      本文主要参考:https://mp.weixin.qq.com/s/mTxxkwRZPgBiKC3Sv-jo3g

      

    二、开始采集

      2.1、分析数据接口:

        为了健全数据样本,数据直接从移动端接口进行采集,连接如下,其中橙色部分为猫眼电影ID,修改即可爬取其他电影。

        链接地址:http://m.maoyan.com/mmdb/comments/movie/1208282.json?v=yes&offset=15&startTime=

        

        

        接口返回的数据如下,主要采集(昵称、城市、评论、评分和时间),用户评论在 json['cmts'] 中:

        

      2.2、爬虫程序核心内容(详细可以看后面源代码):

        >启动脚本需要的参数如下(脚本名+猫眼电影ID+上映日期+数据保存的文件名):.myMovieComment.py 1208282 2016-11-16 myCmts2.txt

        

        >下载html内容:download(self, url),通过python的requests模块进行下载,将下载的数据转成json格式  

     1     def download(self, url):
     2         """下载html内容"""
     3 
     4         print("正在下载URL: "+url)
     5         # 下载html内容
     6         response = requests.get(url, headers=self.headers)
     7 
     8         # 转成json格式数据
     9         if response.status_code == 200:
    10             return response.json()
    11         else:
    12             # print(html.status_code)
    13             print('下载数据为空!')
    14             return ""

        

        >然后就是对已下载的内容进行分析,就是取出我们需要的数据:

     1     def parse(self, content):
     2         """分析数据"""
     3 
     4         comments = []
     5         try:
     6             for item in content['cmts']:
     7                 comment = {
     8                     'nickName': item['nickName'],       # 昵称
     9                     'cityName': item['cityName'],       # 城市
    10                     'content': item['content'],         # 评论内容
    11                     'score': item['score'],             # 评分
    12                     'startTime': item['startTime'],    # 时间
    13                 }
    14                 comments.append(comment)
    15 
    16         except Exception as e:
    17             print(e)
    18 
    19         finally:
    20             return comments

        >将分析出来的数据,进行本地保存,方便后续的分析工作: 

    1  def save(self, data):
    2         """写入文件"""
    3 
    4         print("保存数据,写入文件中...")
    5         self.save_file.write(data)

       

        > 爬虫的核心控制也即爬虫的程序启动入口,管理上面几个方法的有序执行:

     1     def start(self):
     2         """启动控制方法"""
     3 
     4         print("爬虫开始...
    ")
     5 
     6         start_time = self.start_time
     7         end_time = self.end_time
     8 
     9         num = 1
    10         while start_time > end_time:
    11             print("执行次数:", num)
    12             # 1、下载html
    13             content = self.download(self.target_url + str(start_time))
    14 
    15             # 2、分析获取关键数据
    16             comments = ''
    17             if content != "":
    18                 comments = self.parse(content)
    19 
    20             if len(comments) <= 0:
    21                 print("本次数据量为:0,退出爬取!
    ")
    22                 break
    23 
    24             # 3、写入文件
    25             res = ''
    26             for cmt in comments:
    27                 res += "%s###%s###%s###%s###%s
    " % (cmt['nickName'], cmt['cityName'], cmt['content'], cmt['score'], cmt['startTime'])
    28             self.save(res)
    29 
    30             print("本次数据量:%s
    " % len(comments))
    31 
    32             # 获取最后一条数据的时间 ,然后减去一秒
    33             start_time = datetime.strptime(comments[len(comments) - 1]['startTime'], "%Y-%m-%d %H:%M:%S") + timedelta(seconds=-1)
    34             # start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
    35 
    36             # 休眠3s
    37             num += 1
    38             time.sleep(3)
    39 
    40         self.save_file.close()
    41         print("爬虫结束...")

        

      2.3 数据样本,最终爬取将近2万条数据,每条记录的每个数据使用 ### 进行分割:

      

    三、图形化分析数据

      3.1、制作观众城市分布热点图,(pyecharts-geo):

        从图表可以轻松看出,用户主要分布地区,主要以沿海一些发达城市群为主:

        

     1     def createCharts(self):
     2         """生成图表"""
     3 
     4         # 读取数据,格式:[{"北京", 10}, {"上海",10}]
     5         data = self.readCityNum()
     6 
     7         # 1 热点图
     8         geo1 = Geo("《无名之辈》观众位置分布热点图", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600, background_color="#404A59")
     9 
    10         attr1, value1 = geo1.cast(data)
    11 
    12         geo1.add("", attr1, value1, type="heatmap", visual_range=[0, 1000], visual_text_color="#FFF", symbol_size=15, is_visualmap=True, is_piecewise=False, visual_split_number=10)
    13         geo1.render("files/无名之辈-观众位置热点图.html")
    14 
    15         # 2 位置图
    16         geo2 = Geo("《无名之辈》观众位置分布", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600,
    17                    background_color="#404A59")
    18 
    19         attr2, value2 = geo1.cast(data)
    20         geo2.add("", attr2, value2, visual_range=[0, 1000], visual_text_color="#FFF", symbol_size=15,
    21                 is_visualmap=True, is_piecewise=False, visual_split_number=10)
    22         geo2.render("files/无名之辈-观众位置图.html")
    23 
    24         # 3、top20 柱状图
    25         data_top20 = data[:20]
    26         bar = Bar("《无名之辈》观众来源排行 TOP20", "数据来源:猫眼,Fly采集", title_pos="center", width="100%", height=600)
    27         attr, value = bar.cast(data_top20)
    28         bar.add('', attr, value, is_visualmap=True, visual_range=[0, 3500], visual_text_color="#FFF", is_more_utils=True, is_label_show=True)
    29         bar.render("files/无名之辈-观众来源top20.html")
    30 
    31         print("图表生成完成")

      3.2、制作观众人数TOP20的柱形图,(pyecharts-bar):

        

      3.3、制作评论词云,(jieba、wordcloud):

        

        生成词云核心代码:

     1     def createWordCloud(self):
     2         """生成评论词云"""
     3         comments = self.readAllComments()  # 19185
     4 
     5         # 使用 jieba 分词
     6         commens_split = jieba.cut(str(comments), cut_all=False)
     7         words = ''.join(commens_split)
     8 
     9         # 给词库添加停止词
    10         stopwords = STOPWORDS.copy()
    11         stopwords.add("电影")
    12         stopwords.add("一部")
    13         stopwords.add("无名之辈")
    14         stopwords.add("一部")
    15         stopwords.add("一个")
    16         stopwords.add("有点")
    17         stopwords.add("觉得")
    18 
    19         # 加载背景图片
    20         bg_image = plt.imread("files/2048_bg.png")
    21 
    22         # 初始化 WordCloud
    23         wc = WordCloud(width=1200, height=600, background_color='#FFF', mask=bg_image, font_path='C:/Windows/Fonts/STFANGSO.ttf', stopwords=stopwords, max_font_size=400, random_state=50)
    24 
    25         # 生成,显示图片
    26         wc.generate_from_text(words)
    27         plt.imshow(wc)
    28         plt.axis('off')
    29         plt.show()

        

    四、修改pyecharts源码

      4.1、样本数据的城市简称与数据集完整城市名匹配不上:

        使用位置热点图时候,由于采集数据城市是一些简称,与pyecharts的已存在数据的城市名对不上, 所以对源码进行一些修改,方便匹配一些简称。

        黔南 => 黔南布依族苗族自治州

        模块自带的全国主要市县经纬度在:[python安装路径]Libsite-packagespyechartsdatasetscity_coordinates.json

        由于默认情况下,一旦城市名不能完全匹配就会报异常,程序会停止,所以对源码修改如下(报错方法为 Geo.add()),其中添加注析为个人修改部分:

     1   def get_coordinate(self, name, region="中国", raise_exception=False):
     2         """
     3         Return coordinate for the city name.
     4 
     5         :param name: City name or any custom name string.
     6         :param raise_exception: Whether to raise exception if not exist.
     7         :return: A list like [longitude, latitude] or None
     8         """
     9         if name in self._coordinates:
    10             return self._coordinates[name]
    11 
    12 
    13         coordinate = get_coordinate(name, region=region)
    14 
    15         # [ 20181204 添加
    16         # print(name, coordinate)
    17         if coordinate is None:
    18             # 如果字典key匹配不上,尝试进行模糊查询
    19             search_res = search_coordinates_by_region_and_keyword(region, name)
    20             # print("###",search_res)
    21             if search_res:
    22                 coordinate = sorted(search_res.values())[0]
    23         # 20181204 添加 ]
    24 
    25         if coordinate is None and raise_exception:
    26             raise ValueError("No coordinate is specified for {}".format(name))
    27 
    28         return coordinate

       

        相应的需要对 __add()方法进行如下修改:

      

    五、附录-源码

      *说明:源码为本人所写,数据来源为猫眼,全部内容仅供学习,拒绝其他用途!转发请注明出处!

      5.1 采集源码

      1 # -*- coding:utf-8 -*-
      2 
      3 import requests
      4 from datetime import datetime, timedelta
      5 import os
      6 import time
      7 import sys
      8 
      9 
     10 class MaoyanFilmReviewSpider:
     11     """猫眼影评爬虫"""
     12 
     13     def __init__(self, url, end_time, filename):
     14         # 头部
     15         self.headers = {
     16             'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'
     17         }
     18 
     19         # 目标URL
     20         self.target_url = url
     21 
     22         # 数据获取时间段,start_time:截止日期,end_time:上映时间
     23         now = datetime.now()
     24 
     25         # 获取当天的 零点
     26         self.start_time = now + timedelta(hours=-now.hour, minutes=-now.minute, seconds=-now.second)
     27         self.start_time = self.start_time.replace(microsecond=0)
     28         self.end_time = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")
     29 
     30         # 打开写入文件, 创建目录
     31         self.save_path = "files/"
     32         if not os.path.exists(self.save_path):
     33             os.makedirs(self.save_path)
     34         self.save_file = open(self.save_path + filename, "a", encoding="utf-8")
     35 
     36     def download(self, url):
     37         """下载html内容"""
     38 
     39         print("正在下载URL: "+url)
     40         # 下载html内容
     41         response = requests.get(url, headers=self.headers)
     42 
     43         # 转成json格式数据
     44         if response.status_code == 200:
     45             return response.json()
     46         else:
     47             # print(html.status_code)
     48             print('下载数据为空!')
     49             return ""
     50 
     51     def parse(self, content):
     52         """分析数据"""
     53 
     54         comments = []
     55         try:
     56             for item in content['cmts']:
     57                 comment = {
     58                     'nickName': item['nickName'],       # 昵称
     59                     'cityName': item['cityName'],       # 城市
     60                     'content': item['content'],         # 评论内容
     61                     'score': item['score'],             # 评分
     62                     'startTime': item['startTime'],    # 时间
     63                 }
     64                 comments.append(comment)
     65 
     66         except Exception as e:
     67             print(e)
     68 
     69         finally:
     70             return comments
     71 
     72     def save(self, data):
     73         """写入文件"""
     74 
     75         print("保存数据,写入文件中...")
     76         self.save_file.write(data)
     77 
     78     def start(self):
     79         """启动控制方法"""
     80 
     81         print("爬虫开始...
    ")
     82 
     83         start_time = self.start_time
     84         end_time = self.end_time
     85 
     86         num = 1
     87         while start_time > end_time:
     88             print("执行次数:", num)
     89             # 1、下载html
     90             content = self.download(self.target_url + str(start_time))
     91 
     92             # 2、分析获取关键数据
     93             comments = ''
     94             if content != "":
     95                 comments = self.parse(content)
     96 
     97             if len(comments) <= 0:
     98                 print("本次数据量为:0,退出爬取!
    ")
     99                 break
    100 
    101             # 3、写入文件
    102             res = ''
    103             for cmt in comments:
    104                 res += "%s###%s###%s###%s###%s
    " % (cmt['nickName'], cmt['cityName'], cmt['content'], cmt['score'], cmt['startTime'])
    105             self.save(res)
    106 
    107             print("本次数据量:%s
    " % len(comments))
    108 
    109             # 获取最后一条数据的时间 ,然后减去一秒
    110             start_time = datetime.strptime(comments[len(comments) - 1]['startTime'], "%Y-%m-%d %H:%M:%S") + timedelta(seconds=-1)
    111             # start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
    112 
    113             # 休眠3s
    114             num += 1
    115             time.sleep(3)
    116 
    117         self.save_file.close()
    118         print("爬虫结束...")
    119 
    120 
    121 if __name__ == "__main__":
    122     # 确保输入参数
    123     if len(sys.argv) != 4:
    124         print("请输入相关参数:[moveid]、[上映日期]和[保存文件名],如:xxx.py 42962 2018-11-09 text.txt")
    125         exit()
    126 
    127     # 猫眼电影ID
    128     mid = sys.argv[1] # "1208282"  # "42964"
    129     # 电影上映日期
    130     end_time = sys.argv[2]  # "2018-11-16"  # "2018-11-09"
    131     # 每次爬取条数
    132     offset = 15
    133     # 保存文件名
    134     filename = sys.argv[3]
    135 
    136     spider = MaoyanFilmReviewSpider(url="http://m.maoyan.com/mmdb/comments/movie/%s.json?v=yes&offset=%d&startTime=" % (mid, offset), end_time="%s 00:00:00" % end_time, filename=filename)
    137     # spider.start()
    138 
    139     spider.start()
    140     # t1 = "2018-11-09 23:56:23"
    141     # t2 = "2018-11-25"
    142     #
    143     # res = datetime.strptime(t1, "%Y-%m-%d %H:%M:%S") + timedelta(days=-1)
    144     # print(type(res))
    MaoyanFilmReviewSpider.py

      5.2 分析制图源码

      1 # -*- coding:utf-8 -*-
      2 from pyecharts import Geo, Bar, Bar3D
      3 import jieba
      4 from wordcloud import STOPWORDS, WordCloud
      5 import matplotlib.pyplot as plt
      6 
      7 
      8 class ACoolFishAnalysis:
      9     """无名之辈 --- 数据分析"""
     10     def __init__(self):
     11         pass
     12 
     13     def readCityNum(self):
     14         """读取观众城市分布数量"""
     15         d = {}
     16 
     17         with open("files/myCmts2.txt", "r", encoding="utf-8") as f:
     18             row = f.readline()
     19 
     20             while row != "":
     21                 arr = row.split('###')
     22 
     23                 # 确保每条记录长度为 5
     24                 while len(arr) < 5:
     25                     row += f.readline()
     26                     arr = row.split('###')
     27 
     28                 # 记录每个城市的人数
     29                 if arr[1] in d:
     30                     d[arr[1]] += 1
     31                 else:
     32                     d[arr[1]] = 1   # 首次加入字典,为 1
     33 
     34                 row = f.readline()
     35 
     36 
     37             # print(len(comments))
     38             # print(d)
     39 
     40         # 字典 转 元组数组
     41         res = []
     42         for ks in d.keys():
     43             if ks == "":
     44                 continue
     45             tmp = (ks, d[ks])
     46             res.append(tmp)
     47 
     48         # 按地点人数降序
     49         res = sorted(res, key=lambda x: (x[1]),reverse=True)
     50         return res
     51 
     52     def readAllComments(self):
     53         """读取所有评论"""
     54         comments = []
     55 
     56         # 打开文件读取数据
     57         with open("files/myCmts2.txt", "r", encoding="utf-8") as f:
     58             row = f.readline()
     59 
     60             while row != "":
     61                 arr = row.split('###')
     62 
     63                 # 每天记录长度为 5
     64                 while len(arr) < 5:
     65                     row += f.readline()
     66                     arr = row.split('###')
     67 
     68                 if len(arr) == 5:
     69                     comments.append(arr[2])
     70 
     71                 # if len(comments) > 20:
     72                 #     break
     73                 row = f.readline()
     74 
     75         return comments
     76 
     77     def createCharts(self):
     78         """生成图表"""
     79 
     80         # 读取数据,格式:[{"北京", 10}, {"上海",10}]
     81         data = self.readCityNum()
     82 
     83         # 1 热点图
     84         geo1 = Geo("《无名之辈》观众位置分布热点图", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600, background_color="#404A59")
     85 
     86         attr1, value1 = geo1.cast(data)
     87 
     88         geo1.add("", attr1, value1, type="heatmap", visual_range=[0, 1000], visual_text_color="#FFF", symbol_size=15, is_visualmap=True, is_piecewise=False, visual_split_number=10)
     89         geo1.render("files/无名之辈-观众位置热点图.html")
     90 
     91         # 2 位置图
     92         geo2 = Geo("《无名之辈》观众位置分布", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600,
     93                    background_color="#404A59")
     94 
     95         attr2, value2 = geo1.cast(data)
     96         geo2.add("", attr2, value2, visual_range=[0, 1000], visual_text_color="#FFF", symbol_size=15,
     97                 is_visualmap=True, is_piecewise=False, visual_split_number=10)
     98         geo2.render("files/无名之辈-观众位置图.html")
     99 
    100         # 3、top20 柱状图
    101         data_top20 = data[:20]
    102         bar = Bar("《无名之辈》观众来源排行 TOP20", "数据来源:猫眼,Fly采集", title_pos="center", width="100%", height=600)
    103         attr, value = bar.cast(data_top20)
    104         bar.add('', attr, value, is_visualmap=True, visual_range=[0, 3500], visual_text_color="#FFF", is_more_utils=True, is_label_show=True)
    105         bar.render("files/无名之辈-观众来源top20.html")
    106 
    107         print("图表生成完成")
    108 
    109     def createWordCloud(self):
    110         """生成评论词云"""
    111         comments = self.readAllComments()  # 19185
    112 
    113         # 使用 jieba 分词
    114         commens_split = jieba.cut(str(comments), cut_all=False)
    115         words = ''.join(commens_split)
    116 
    117         # 给词库添加停止词
    118         stopwords = STOPWORDS.copy()
    119         stopwords.add("电影")
    120         stopwords.add("一部")
    121         stopwords.add("无名之辈")
    122         stopwords.add("一部")
    123         stopwords.add("一个")
    124         stopwords.add("有点")
    125         stopwords.add("觉得")
    126 
    127         # 加载背景图片
    128         bg_image = plt.imread("files/2048_bg.png")
    129 
    130         # 初始化 WordCloud
    131         wc = WordCloud(width=1200, height=600, background_color='#FFF', mask=bg_image, font_path='C:/Windows/Fonts/STFANGSO.ttf', stopwords=stopwords, max_font_size=400, random_state=50)
    132 
    133         # 生成,显示图片
    134         wc.generate_from_text(words)
    135         plt.imshow(wc)
    136         plt.axis('off')
    137         plt.show()
    138 
    139 
    140 
    141 if __name__ == "__main__":
    142     demo = ACoolFishAnalysis()
    143     demo.createWordCloud()
    View Code
  • 相关阅读:
    NHibernate 做个小项目来试一下吧 四 (我们继续)
    NHibernate 做个小项目来试一下吧 三
    NHibernate 做个小项目来试一下吧(数据分页) 七
    用SWFUpload插件进行多文件上传(上传页获得自定义后的文件名)
    SQL:找出我(uid=2)所有的好友信息,和这些好友发布的最新的一篇文章
    介绍生成PHP网站页面静态化的方法
    smarty if 操作符
    php 做注册邮件发送成功
    200多个js技巧代码
    生成列表页分页的HTML静态页
  • 原文地址:https://www.cnblogs.com/reader/p/10070629.html
Copyright © 2020-2023  润新知