如何在60分钟掌握爬虫和可视化?

如何在60分钟内掌握爬虫和数据可视化?

首先分享一个爬虫和数据可视化的案例:

网易云音乐爬虫 & 数据可视化分析
1. 数据爬取
1.1 评论爬取
1.2 用户信息爬取
2 数据清洗 & 可视化
歌评文本分析

1. 数据爬取

爬虫部分主要是调用官方API
工具:
Python3.6
sublime3
MySQL(数据存储)
scrapy(数据清洗)
pyecharts(可视化工具库)

1.1 评论爬取

实际操作过程中,网易云官方对于API的请求是有限制的,有条件的可以采用更换代理IP来防反爬,本次采用的是单线程爬取,所以IP封的并不太频繁,后面会对代码进行重构,实现多线程+更换IP来加快爬取速度。

根据获取评论的API,请求URL有3个可变部分:歌曲ID、每页限制数limit和评论总偏移量offset,通过API分析得知:当offeset=0时,返回json数据中包含有评论总数量total,所以根据API可设计爬虫如下:

# -*- coding:utf8 -*-
# python3.6
from urllib import request
import json
import pymysql
from datetime import datetime
import re

ROOT_URL = 'http://music.163.com/api/v1/resource/comments/R_SO_4_%s?limit=%s&offset=%s'
LIMIT_NUMS = 50	# 每页限制爬取数
DATABASE = ''	# 数据库名
TABLE = ''	# 数据库表名
# 数据表设计如下:
'''
id(int)				commentId(varchar) 
content(text) 		likedCount(int) 
userId(varchar)	time(datetime)
'''
PATTERN = re.compile(r'[\n\t\r\/]') # 替换掉评论中的特殊字符以防插入数据库时报错

def getData(url):
	if not url:
		return None, None
	headers = {
            "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36',
            "Host": "music.163.com",
}
	print('Crawling>>> ' + url)
	try:
		req = request.Request(url, headers=headers)
		content = request.urlopen(req).read().decode("utf-8")
		js = json.loads(content)
		total = int(js['total'])
		datas = []
		for c in js['comments']:
			data = dict()
			data['commentId'] = c['commentId']
			data['content'] = PATTERN.sub('', c['content'])
			data['time'] = datetime.fromtimestamp(c['time']//1000)
			data['likedCount'] = c['likedCount']
			data['userId'] = c['user']['userId']
			datas.append(data)
		return total, datas
	except Exception as e:
		print('Down err>>> ', e)
		pass
	
def saveData(data):
	if not data:
		return None
	conn = pymysql.connect(host='localhost', user='****', passwd='****', db='****', charset='utf8mb4') # 注意字符集要设为utf8mb4,以支持存储评论中的emoji表情
	cursor = conn.cursor()
	sql = 'insert into ' + TABLE + ' (id,commentId,content,likedCount,time,userId) VALUES (%s,%s,%s,%s,%s,%s)'
	for d in data:
		try:
			cursor.execute('SELECT max(id) FROM '+TABLE)
			id_ = cursor.fetchone()[0]
			cursor.execute(sql, (id_+1,d['commentId'], d['content'], d['likedCount'], d['time'], d['userId']))
			conn.commit()
		except Exception as e:
			print('mysql err>>> ',d['commentId'],e)
			pass

	cursor.close()
	conn.close()	
	
if __name__ == '__main__':
	songId = input('歌曲ID:').strip()
	total,data = getData(ROOT_URL%(songId, LIMIT_NUMS, 0))
	saveData(data)
	if total:
		for i in range(1, total//EVERY_PAGE_NUMS+1):
				_, data = getData(ROOT_URL%(songId, LIMIT_NUMS, i*(LIMIT_NUMS)))
				saveData(data)

以上代码实现了单线程爬取网易云音乐用户信息并存储进数据库。至此,已经完成了歌曲评论和对应用户信息的抓取。接下来,对抓取到的数据进行清洗及可视化分析。
如何在60分钟掌握爬虫和可视化?_第1张图片
如何在60分钟掌握爬虫和可视化?_第2张图片

2 数据清洗 & 可视化

关于数据的清洗,实际上在上一部分抓取数据的过程中已经做了一部分,包括:后台返回的空用户信息、重复数据的去重等。除此之外,还要进行一些清洗:用户年龄错误、用户城市编码转换等。

关于数据的去重,评论部分可以以sommentId为数据库索引,利用数据库来自动去重;用户信息部分以用户ID为数据库索引实现自动去重。
API返回的用户年龄一般是时间戳的形式(以毫秒计)、有时候也会返回一个负值或者一个大于当前时间的值,暂时没有找到这两种值代表的含义,故而一律按0来处理。
API返回的用户信息中,城市分为province和city两个字段,本此分析中只保存了city字段。实际上字段值是一个城市code码,具体对照在这里下载。

在这部分,利用Python的数据处理库pandas进行数据处理,利用可视化库pyecharts进行数据可视化。处理代码如下:

# -*- coding:utf8 -*-
# python3.6
import pandas as pd
import pymysql
from pyecharts import Bar,Pie,Line,Scatter,Map

TABLE_COMMENTS = '****'
TABLE_USERS = '****'
DATABASE = '****'

conn = pymysql.connect(host='localhost', user='****', passwd='****', db=DATABASE, charset='utf8mb4')
sql_users = 'SELECT id,gender,age,city FROM '+TABLE_USERS
sql_comments = 'SELECT id,time FROM '+TABLE_COMMENTS
comments = pd.read_sql(sql_comments, con=conn)
users = pd.read_sql(sql_users, con=conn)
# 评论时间(按天)分布分析
comments_day = comments['time'].dt.date
data = comments_day.id.groupby(comments_day['time']).count()
line = Line('评论时间(按天)分布')
line.use_theme('dark')
line.add(
	'',
	data.index.values,
	data.values,
	is_fill=True,
)
line.render(r'./评论时间(按天)分布.html')
# 评论时间(按小时)分布分析
comments_hour = comments['time'].dt.hour
data = comments_hour.id.groupby(comments_hour['time']).count()
line = Line('评论时间(按小时)分布')
line.use_theme('dark')
line.add(
	'',
	data.index.values,
	data.values,
	is_fill=True,
)
line.render(r'./评论时间(按小时)分布.html')
# 评论时间(按周)分布分析
comments_week = comments['time'].dt.dayofweek
data = comments_week.id.groupby(comments_week['time']).count()
line = Line('评论时间(按周)分布')
line.use_theme('dark')
line.add(
	'',
	data.index.values,
	data.values,
	is_fill=True,
)
line.render(r'./评论时间(按周)分布.html')

# 用户年龄分布分析
age = users[users['age']>0]	# 清洗掉年龄小于1的数据
age = age.id.groupby(age['age']).count()	# 以年龄值对数据分组
Bar = Bar('用户年龄分布')
Bar.use_theme('dark')
Bar.add(
	'',
	age.index.values,
	age.values,
	is_fill=True,
)
Bar.render(r'./用户年龄分布图.html')	# 生成渲染的html文件

# 用户地区分布分析
# 城市code编码转换
def city_group(cityCode):
    city_map = {
        '11': '北京',
        '12': '天津',
        '31': '上海',
        '50': '重庆',
        '5e': '重庆',
        '81': '香港',
        '82': '澳门',
        '13': '河北',
        '14': '山西',
        '15': '内蒙古',
        '21': '辽宁',
        '22': '吉林',
        '23': '黑龙江',
        '32': '江苏',
        '33': '浙江',
        '34': '安徽',
        '35': '福建',
        '36': '江西',
        '37': '山东',
        '41': '河南',
        '42': '湖北',
        '43': '湖南',
        '44': '广东',
        '45': '广西',
        '46': '海南',
        '51': '四川',
        '52': '贵州',
        '53': '云南',
        '54': '西藏',
        '61': '陕西',
        '62': '甘肃',
        '63': '青海',
        '64': '宁夏',
        '65': '新疆',
        '71': '台湾',
        '10': '其他',
    }
    return city_map[cityCode[:2]]
    
city = users['city'].apply(city_group)
city = city.id.groupby(city['city']).count()
map_ = Map('用户地区分布图')
map_.add(
	'',
	city.index.values,
	city.values,
	maptype='china',
	is_visualmap=True,
	visual_text_color='#000',
	is_label_show=True,
)
map_.render(r'./用户地区分布图.html')

以上,是对抓取到的数据采用可视化库pyecharts进行可视化分析,得到的结果如下:

评论数时间(按天)分布
如何在60分钟掌握爬虫和可视化?_第3张图片

评论数时间(按周)分布
如何在60分钟掌握爬虫和可视化?_第4张图片

评论时间按周分布图可以看出,评论数在一周当中前面较少,后面逐渐增多,这可以解释为往后接近周末,大家有更多时间来听听歌、刷刷歌评,而一旦周末过完,评论量马上下降(周日到周一的下降过渡),大家又回归到工作当中。

评论数时间(按小时)分布
如何在60分钟掌握爬虫和可视化?_第5张图片

评论时间按小时分布图可以看出,评论数在一天当中有两个小高峰:11点-13点和22点-0点。这可以解释为用户在中午午饭时间和晚上下班(课)在家时间有更多的时间来听歌刷评论,符合用户的日常。至于为什么早上没有出现一个小高峰,大概是早上大家都在抢时间上班(学),没有多少时间去刷评论。

用户年龄分布
如何在60分钟掌握爬虫和可视化?_第6张图片

用户年龄分布图可以看出,用户大多集中在14-30岁之间,以20岁左右居多,除去虚假年龄之外,这个年龄分布也符合网易云用户的年龄段。图中可以看出28岁有个高峰,猜测可能是包含了一些异常数据,有兴趣的化可以做进一步分析。

用户地区分布
如何在60分钟掌握爬虫和可视化?_第7张图片

用户地区分布图可以看出,用户涵盖了全国各大省份,因为中间数据(坑)的缺失,并没有展现出哪个省份特别突出的情况。对别的歌评(完全数据)的可视化分析,可以看出明显的地区分布差异。

接下来说说前面提到的坑!!!
细心观察评论数(按天)分布那张图,发现2017年到2018年间有很大一部分数据缺失,这实际上是因为在数据抓取过程中出现的问题。研究了一下发现,根据获取歌曲评论的API,实际上每首歌最多只能获得2w条左右(去重后)的评论,对于评论数超过2w的歌曲,只能获得前后(日期)各1w条评论,而且这个限制对于网易云官网也是存在的,具体表现为:对一首评论数超过2w的歌,如果一直往后浏览评论,会发现从第500页(网页端网易云每页20条评论)往后,后台返回的内容和第500页完全一样,从后往前同理。这应该是官方后台做了限制,连自家也不放过。。。

此次分析只是对某一首歌曲评论时间、用户年龄/地区分布进行的,实际上抓取到的信息不仅仅在于此,可以做进一步分析(比如利用评论内容进行文本内容分析等),这部分,未来会进一步分析。当然也可以根据自己情况对不同歌曲进行分析。

具体如何爬虫和可视化分析,分享一套教程:

从零开始教你如何爬虫和数据可视化。

更多文章和资料|点击下方文字直达 ↓↓↓
阿里云K8s实战手册
[阿里云CDN排坑指南]CDN
ECS运维指南
DevOps实践手册
Hadoop大数据实战手册
Knative云原生应用开发指南
OSS 运维实战手册

你可能感兴趣的:(如何在60分钟掌握爬虫和可视化?)