Python采集猫眼两万条数据 对《无名之辈》影评进行分析


Posted in Python onDecember 05, 2018

一、说明

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

运行环境: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=

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

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

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

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

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

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

def download(self, url):
 """下载html内容"""

 print("正在下载URL: "+url)
 # 下载html内容
 response = requests.get(url, headers=self.headers)

 # 转成json格式数据
 if response.status_code == 200:
  return response.json()
 else:
  # print(html.status_code)
  print('下载数据为空!')
  return ""

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

def parse(self, content):
 """分析数据"""

 comments = []
 try:
  for item in content['cmts']:
  comment = {
   'nickName': item['nickName'], # 昵称
   'cityName': item['cityName'], # 城市
   'content': item['content'],  # 评论内容
   'score': item['score'],  # 评分
   'startTime': item['startTime'], # 时间
  }
  comments.append(comment)

 except Exception as e:
  print(e)

 finally:
  return comments

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

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

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

def start(self):
 """启动控制方法"""

 print("爬虫开始...\r\n")

 start_time = self.start_time
 end_time = self.end_time

 num = 1
 while start_time > end_time:
  print("执行次数:", num)
  # 1、下载html
  content = self.download(self.target_url + str(start_time))

  # 2、分析获取关键数据
  comments = ''
  if content != "":
  comments = self.parse(content)

  if len(comments) <= 0:
  print("本次数据量为:0,退出爬取!\r\n")
  break

  # 3、写入文件
  res = ''
  for cmt in comments:
  res += "%s###%s###%s###%s###%s\n" % (cmt['nickName'], cmt['cityName'], cmt['content'], cmt['score'], cmt['startTime'])
  self.save(res)

  print("本次数据量:%s\r\n" % len(comments))

  # 获取最后一条数据的时间 ,然后减去一秒
  start_time = datetime.strptime(comments[len(comments) - 1]['startTime'], "%Y-%m-%d %H:%M:%S") + timedelta(seconds=-1)
  # start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")

  # 休眠3s
  num += 1
  time.sleep(3)

 self.save_file.close()
 print("爬虫结束...")

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

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

三、图形化分析数据

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

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

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

def createCharts(self):
 """生成图表"""

 # 读取数据,格式:[{"北京", 10}, {"上海",10}]
 data = self.readCityNum()

 # 1 热点图
 geo1 = Geo("《无名之辈》观众位置分布热点图", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600, background_color="#404A59")

 attr1, value1 = geo1.cast(data)

 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)
 geo1.render("files/无名之辈-观众位置热点图.html")

 # 2 位置图
 geo2 = Geo("《无名之辈》观众位置分布", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600,
   background_color="#404A59")

 attr2, value2 = geo1.cast(data)
 geo2.add("", attr2, value2, visual_range=[0, 1000], visual_text_color="#FFF", symbol_size=15,
  is_visualmap=True, is_piecewise=False, visual_split_number=10)
 geo2.render("files/无名之辈-观众位置图.html")

 # 3、top20 柱状图
 data_top20 = data[:20]
 bar = Bar("《无名之辈》观众来源排行 TOP20", "数据来源:猫眼,Fly采集", title_pos="center", width="100%", height=600)
 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("files/无名之辈-观众来源top20.html")

 print("图表生成完成")

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

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

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

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

生成词云核心代码:

def createWordCloud(self):
 """生成评论词云"""
 comments = self.readAllComments() # 19185

 # 使用 jieba 分词
 commens_split = jieba.cut(str(comments), cut_all=False)
 words = ''.join(commens_split)

 # 给词库添加停止词
 stopwords = STOPWORDS.copy()
 stopwords.add("电影")
 stopwords.add("一部")
 stopwords.add("无名之辈")
 stopwords.add("一部")
 stopwords.add("一个")
 stopwords.add("有点")
 stopwords.add("觉得")

 # 加载背景图片
 bg_image = plt.imread("files/2048_bg.png")

 # 初始化 WordCloud
 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)

 # 生成,显示图片
 wc.generate_from_text(words)
 plt.imshow(wc)
 plt.axis('off')
 plt.show()

四、修改pyecharts源码

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

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

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

模块自带的全国主要市县经纬度在:[python安装路径]\Lib\site-packages\pyecharts\datasets\city_coordinates.json

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

def get_coordinate(self, name, region="中国", raise_exception=False):
 """
 Return coordinate for the city name.

 :param name: City name or any custom name string.
 :param raise_exception: Whether to raise exception if not exist.
 :return: A list like [longitude, latitude] or None
 """
 if name in self._coordinates:
  return self._coordinates[name]


 coordinate = get_coordinate(name, region=region)

 # [ 20181204 添加
 # print(name, coordinate)
 if coordinate is None:
  # 如果字典key匹配不上,尝试进行模糊查询
  search_res = search_coordinates_by_region_and_keyword(region, name)
  # print("###",search_res)
  if search_res:
  coordinate = sorted(search_res.values())[0]
 # 20181204 添加 ]

 if coordinate is None and raise_exception:
  raise ValueError("No coordinate is specified for {}".format(name))

 return coordinate

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

Python采集猫眼两万条数据 对《无名之辈》影评进行分析

五、附录-源码

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

5.1 采集源码

# -*- coding:utf-8 -*-

import requests
from datetime import datetime, timedelta
import os
import time
import sys


class MaoyanFilmReviewSpider:
 """猫眼影评爬虫"""

 def __init__(self, url, end_time, filename):
  # 头部
  self.headers = {
   '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'
  }

  # 目标URL
  self.target_url = url

  # 数据获取时间段,start_time:截止日期,end_time:上映时间
  now = datetime.now()

  # 获取当天的 零点
  self.start_time = now + timedelta(hours=-now.hour, minutes=-now.minute, seconds=-now.second)
  self.start_time = self.start_time.replace(microsecond=0)
  self.end_time = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")

  # 打开写入文件, 创建目录
  self.save_path = "files/"
  if not os.path.exists(self.save_path):
   os.makedirs(self.save_path)
  self.save_file = open(self.save_path + filename, "a", encoding="utf-8")

 def download(self, url):
  """下载html内容"""

  print("正在下载URL: "+url)
  # 下载html内容
  response = requests.get(url, headers=self.headers)

  # 转成json格式数据
  if response.status_code == 200:
   return response.json()
  else:
   # print(html.status_code)
   print('下载数据为空!')
   return ""

 def parse(self, content):
  """分析数据"""

  comments = []
  try:
   for item in content['cmts']:
    comment = {
     'nickName': item['nickName'],  # 昵称
     'cityName': item['cityName'],  # 城市
     'content': item['content'],   # 评论内容
     'score': item['score'],    # 评分
     'startTime': item['startTime'], # 时间
    }
    comments.append(comment)

  except Exception as e:
   print(e)

  finally:
   return comments

 def save(self, data):
  """写入文件"""

  print("保存数据,写入文件中...")
  self.save_file.write(data)

 def start(self):
  """启动控制方法"""

  print("爬虫开始...\r\n")

  start_time = self.start_time
  end_time = self.end_time

  num = 1
  while start_time > end_time:
   print("执行次数:", num)
   # 1、下载html
   content = self.download(self.target_url + str(start_time))

   # 2、分析获取关键数据
   comments = ''
   if content != "":
    comments = self.parse(content)

   if len(comments) <= 0:
    print("本次数据量为:0,退出爬取!\r\n")
    break

   # 3、写入文件
   res = ''
   for cmt in comments:
    res += "%s###%s###%s###%s###%s\n" % (cmt['nickName'], cmt['cityName'], cmt['content'], cmt['score'], cmt['startTime'])
   self.save(res)

   print("本次数据量:%s\r\n" % len(comments))

   # 获取最后一条数据的时间 ,然后减去一秒
   start_time = datetime.strptime(comments[len(comments) - 1]['startTime'], "%Y-%m-%d %H:%M:%S") + timedelta(seconds=-1)
   # start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")

   # 休眠3s
   num += 1
   time.sleep(3)

  self.save_file.close()
  print("爬虫结束...")


if __name__ == "__main__":
 # 确保输入参数
 if len(sys.argv) != 4:
  print("请输入相关参数:[moveid]、[上映日期]和[保存文件名],如:xxx.py 42962 2018-11-09 text.txt")
  exit()

 # 猫眼电影ID
 mid = sys.argv[1] # "1208282" # "42964"
 # 电影上映日期
 end_time = sys.argv[2] # "2018-11-16" # "2018-11-09"
 # 每次爬取条数
 offset = 15
 # 保存文件名
 filename = sys.argv[3]

 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)
 # spider.start()

 spider.start()
 # t1 = "2018-11-09 23:56:23"
 # t2 = "2018-11-25"
 #
 # res = datetime.strptime(t1, "%Y-%m-%d %H:%M:%S") + timedelta(days=-1)
 # print(type(res))

MaoyanFilmReviewSpider.py

5.2 分析制图源码

# -*- coding:utf-8 -*-
from pyecharts import Geo, Bar, Bar3D
import jieba
from wordcloud import STOPWORDS, WordCloud
import matplotlib.pyplot as plt


class ACoolFishAnalysis:
 """无名之辈 --- 数据分析"""
 def __init__(self):
  pass

 def readCityNum(self):
  """读取观众城市分布数量"""
  d = {}

  with open("files/myCmts2.txt", "r", encoding="utf-8") as f:
   row = f.readline()

   while row != "":
    arr = row.split('###')

    # 确保每条记录长度为 5
    while len(arr) < 5:
     row += f.readline()
     arr = row.split('###')

    # 记录每个城市的人数
    if arr[1] in d:
     d[arr[1]] += 1
    else:
     d[arr[1]] = 1 # 首次加入字典,为 1

    row = f.readline()


   # print(len(comments))
   # print(d)

  # 字典 转 元组数组
  res = []
  for ks in d.keys():
   if ks == "":
    continue
   tmp = (ks, d[ks])
   res.append(tmp)

  # 按地点人数降序
  res = sorted(res, key=lambda x: (x[1]),reverse=True)
  return res

 def readAllComments(self):
  """读取所有评论"""
  comments = []

  # 打开文件读取数据
  with open("files/myCmts2.txt", "r", encoding="utf-8") as f:
   row = f.readline()

   while row != "":
    arr = row.split('###')

    # 每天记录长度为 5
    while len(arr) < 5:
     row += f.readline()
     arr = row.split('###')

    if len(arr) == 5:
     comments.append(arr[2])

    # if len(comments) > 20:
    #  break
    row = f.readline()

  return comments

 def createCharts(self):
  """生成图表"""

  # 读取数据,格式:[{"北京", 10}, {"上海",10}]
  data = self.readCityNum()

  # 1 热点图
  geo1 = Geo("《无名之辈》观众位置分布热点图", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600, background_color="#404A59")

  attr1, value1 = geo1.cast(data)

  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)
  geo1.render("files/无名之辈-观众位置热点图.html")

  # 2 位置图
  geo2 = Geo("《无名之辈》观众位置分布", "数据来源:猫眼,Fly采集", title_color="#FFF", title_pos="center", width="100%", height=600,
     background_color="#404A59")

  attr2, value2 = geo1.cast(data)
  geo2.add("", attr2, value2, visual_range=[0, 1000], visual_text_color="#FFF", symbol_size=15,
    is_visualmap=True, is_piecewise=False, visual_split_number=10)
  geo2.render("files/无名之辈-观众位置图.html")

  # 3、top20 柱状图
  data_top20 = data[:20]
  bar = Bar("《无名之辈》观众来源排行 TOP20", "数据来源:猫眼,Fly采集", title_pos="center", width="100%", height=600)
  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("files/无名之辈-观众来源top20.html")

  print("图表生成完成")

 def createWordCloud(self):
  """生成评论词云"""
  comments = self.readAllComments() # 19185

  # 使用 jieba 分词
  commens_split = jieba.cut(str(comments), cut_all=False)
  words = ''.join(commens_split)

  # 给词库添加停止词
  stopwords = STOPWORDS.copy()
  stopwords.add("电影")
  stopwords.add("一部")
  stopwords.add("无名之辈")
  stopwords.add("一部")
  stopwords.add("一个")
  stopwords.add("有点")
  stopwords.add("觉得")

  # 加载背景图片
  bg_image = plt.imread("files/2048_bg.png")

  # 初始化 WordCloud
  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)

  # 生成,显示图片
  wc.generate_from_text(words)
  plt.imshow(wc)
  plt.axis('off')
  plt.show()



if __name__ == "__main__":
 demo = ACoolFishAnalysis()
 demo.createWordCloud()

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
python和shell获取文本内容的方法
Jun 05 Python
Python对CSV、Excel、txt、dat文件的处理
Sep 18 Python
详解Python函数式编程—高阶函数
Mar 29 Python
在win10和linux上分别安装Python虚拟环境的方法步骤
May 09 Python
python找出一个列表中相同元素的多个索引实例
Jun 11 Python
python分数表示方式和写法
Jun 26 Python
python线程join方法原理解析
Feb 11 Python
python实现打砖块游戏
Feb 25 Python
Python如何操作docker redis过程解析
Aug 10 Python
pytorch 一行代码查看网络参数总量的实现
May 12 Python
python基础入门之字典和集合
Jun 13 Python
Python OpenCV超详细讲解调整大小与图像操作的实现
Apr 02 Python
python_opencv用线段画封闭矩形的实例
Dec 05 #Python
在python中实现将一张图片剪切成四份的方法
Dec 05 #Python
python实现对任意大小图片均匀切割的示例
Dec 05 #Python
windows下 兼容Python2和Python3的解决方法
Dec 05 #Python
Django中更改默认数据库为mysql的方法示例
Dec 05 #Python
Python3爬虫爬取英雄联盟高清桌面壁纸功能示例【基于Scrapy框架】
Dec 05 #Python
在python2.7中用numpy.reshape 对图像进行切割的方法
Dec 05 #Python
You might like
linux系统下php安装mbstring扩展的二种方法
2014/01/20 PHP
WordPress中的shortcode短代码功能使用详解
2016/05/17 PHP
php微信公众号开发之快递查询
2018/10/20 PHP
PHP获取当前时间不准确问题解决方案
2020/08/14 PHP
javascript globalStorage类代码
2009/06/04 Javascript
跨域表单提交状态的变相判断代码
2009/11/12 Javascript
理解Javascript_02_理解undefined和null
2010/10/11 Javascript
JQuery中对服务器控件 DropdownList, RadioButtonList, CheckboxList的操作总结
2011/06/28 Javascript
根据配置文件加载js依赖模块
2014/12/29 Javascript
moment.js轻松实现获取当前日期是当年的第几周
2015/02/05 Javascript
举例讲解JavaScript中将数组元素转换为字符串的方法
2015/10/25 Javascript
点评js异步加载的4种方式
2015/12/22 Javascript
jQuery实现简单的点赞效果
2020/05/29 Javascript
微信小程序开发(二)图片上传+服务端接收详解
2017/01/11 Javascript
vue2 中二级路由高亮问题及配置方法
2019/06/10 Javascript
处理JavaScript值为undefined的7个小技巧
2020/07/28 Javascript
谈谈JavaScript中的垃圾回收机制
2020/09/17 Javascript
[03:14]2014DOTA2西雅图国际邀请赛 EG战队巡礼
2014/07/07 DOTA
Python下的Mysql模块MySQLdb安装详解
2014/04/09 Python
pandas通过索引进行排序的示例
2018/11/16 Python
详解Python装饰器
2019/03/25 Python
解决django中ModelForm多表单组合的问题
2019/07/18 Python
pytorch获取vgg16-feature层输出的例子
2019/08/20 Python
详解html5 shiv.js和respond.min.js
2018/01/24 HTML / CSS
Nike德国官网:Nike.com (DE)
2018/11/13 全球购物
巴塞罗那观光通票:Barcelona Pass
2019/10/30 全球购物
销售自荐信
2013/10/22 职场文书
汽车运用工程系毕业生自荐信
2013/12/27 职场文书
厂区绿化方案
2014/05/08 职场文书
小学班级口号
2014/06/09 职场文书
我们的节日国庆活动方案
2014/08/19 职场文书
Java基础之this关键字的使用
2021/06/30 Java/Android
MySQL修炼之联结与集合浅析
2021/10/05 MySQL
pytorch中的torch.nn.Conv2d()函数图文详解
2022/02/28 Python
python 离散点图画法的实现
2022/04/01 Python
python数据分析之单因素分析线性拟合及地理编码
2022/06/25 Python