菜鸟一枚,所学的东西不多,记性也不好,因此对我来说巩固和实践很重要。这里简单的以爬取某电商网站某电脑品牌评论数据为例,简单回顾了爬虫python的写过程,可视化分析,以及介绍了文本关键字抽取。
学习Python中有不明白推荐加入交流群
号:984632579
群里有志同道合的小伙伴,互帮互助,
群里有不错的视频学习教程和PDF!
全文以代码和分析的形式展现具体的过程:
步骤:
1.网站的简单分析:爬取特定网页内容时,我们必须要找到其正确的
URL,并可以从中找到对应的规律。由于现有的电商网站评论信息都通过JS加载,因此商品详情页中并不会存在评论的具体信息。因此我们首先借助谷歌浏览器开发者工具进行查找:
a.具体方法是在商品详情页(请参考图2-1)点击鼠标右键,选择检查(请参考图2-2),在弹出的开发者工具界面(可参考图16-3)中选择Network,设置为禁用缓存(Disable cache)和只查看JS文件;
b.然后刷新页面。页面加载完成后向下滚动鼠标找到商品评价部分,等商品评价信息显示出来后,在下面Network界面的左侧筛选框中输入productPageComments,这时下面的加载记录中只有一条信息,这里包含的就是商品详情页的商品评论信息。点击这条信息,在右侧的Preview界面中可以看到其中包含了当前页面中的评论信息。
2.根据1中的简单分析,我们已经得到评论信息的URL:
https://sclub.jd.com/comment/productPageComments.action?callback= fetchJSON_comment98vv12892&productId=8674557&score=0&sortType=5& page=0&pageSize=10&isShadowSku=0&fold=1
仔细观察这条url可以发现,pageSize=10是页码。如果我们想要获取这个商品所有的评论,只需改变pageSize的值即可。
3.接下来开始爬虫的编写工作。2中已经分析了url,我们只获取一个商品的评论,因此id值不需要改变;为了获取更多该商品的评论,我们需要将pageSize在程序中作为一个变量,动态的获取多页的评论数据;由于只是进行简单的练习,pageSize在0到40变化。
程序主要分为三大部分:
part1 爬虫部分(数据下载函数,html转dataframe部分,数据清洗部分):
#导入必要的库 import sys sys.path.append('d:/anocoda/envs/py3_cpu/libsite-packages/') import requests #请求和页面抓取) import time import random import re import numpy as np import pandas as pd import matplotlib.pyplot as plt import jieba from datetime import datetime
#配置表头信息 header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', 'Accept':'text/html;q=0.9,*/*;q=0.8', 'Accept-Charset':'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'Connection':'close', 'Referer':'https://item.jd.com/10809260821.html' } cookie = {'TrackID':'1mJoJegxxximdOIuMj1L78NM9IEUYBloQE8lNf5Kr0SN4bLXqWbNQGsuWLT7VSxXgBrnuOwGj9xdFUbqz1sLwrpxkzkjTA-HSsgVP9iJhv-g', '__jda':'122270672.413567069.1502329865.1505359716.1505377343.17', '__jdb':'122270672.4.413567069|17.1505377343', '__jdc':'122270672', '__jdu':'413567069', '__jdv':'122270672|p.egou.com|t_36378_879430_c|tuiguang|5191ffe38de346c28750ae3309faf11a|1505288084897', 'areaId':'2', 'cn':'17', 'ipLoc-djd':'2-2830-51811-0.137557061', 'ipLocation':'%u4E0A%u6D77', 'mx':'0_X', 'rkv':'V0800', 'user-key':'acce01e8-1533-4aaa-bcea-34ac9b6d6a53', 'xtest':'4657.553.d9798cdf31c02d86b8b81cc119d94836.b7a782741f667201b54880c925faec4b'}
上述部分主要是导入必要的python库,以及初始化表头信息部分(这样做的目的是伪装术,把自己伪装成浏览器,防止被服务端封掉IP);
#网页下载器 def downloadPage(url1,url2): ran_num = random.sample(range(40),40) a = ran_num[0] for i in ran_num: url = url1 + str(i) + url2 r = requests.get(url, headers=header, cookies=cookie) if i==a: html1 = r.content else: html2 = r.content html1 = html1+html2 time.sleep(10) print('当前抓取页面:',url,'状态:',r) html = str(html1,encoding='gbk') file = open('./spiner_jd.txt','w') file.write(html) file.close()
爬取每一页的评论数据,并将这些数据拼接和编码存到txt文件中,后面的数据处理直接操作txt文件中的数据;
# html->dataframe 通过正则表达式处理数据 def dataProcess(): html = open('./spiner_jd.txt','r').read() #使用正则表达式提取结构化信息 #1.content评论信息 #2.createTime 评论时间 #3.isMobile 客户端类型 #4.productColor 产品介绍 #5.userLevelName 用户级别 #6.nickName 用户名称 # 使用正则提取userClient字段信息 content = re.findall(r'"id":d*?,"guid".*?,"content":(.*?),',html) createTime = re.findall(r'"id":d*?,"guid".*?,"creationTime":(.*?),"isTop":.*?,',html) # isMobile = re.findall(r'"id":d*?,"guid".*?,"userClientShow":.*?","isMobile":(.*?)}',html) productColor = re.findall(r'"discussionId":.*?,"productColor":(.*?),',html) userLevelName = re.findall(r'"anonymousFlag".*?,"userLevelName":(.*?),',html) # nickName = re.findall(r'"isReplyGrade":.*?,"nickname":(.*?),',html) print('content_len:',len(content),';time_len:',len(createTime),';productColor:',len(productColor), ':userLevelName:',len(userLevelName)) df = pd.DataFrame({'content':content,'createTime':createTime,'productColor':productColor, 'userLevelName':userLevelName}) df.to_csv('./spiner_df.csv',index=None)
df = pd.read_csv('./spiner_df.csv') df = df.applymap(lambda x:re.sub('"','',x)) #这里使用局部替换法进行了双引号的去除 df['createTime'] = df['createTime'].map(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S')) #使用datetime中的strptime将字符串转为了时间 df.head() df['createTime'] = df['createTime'].map(lambda x:datetime.strptime(x,'%Y-%m-%d %H:%M:%S')) #使用datetime中的strptime将字符串转为了时间 df['year'] = df['createTime'].map(lambda x:x.year) df['month'] = df['createTime'].map(lambda x:x.month) df['day'] = df['createTime'].map(lambda x:x.day) df['hour'] = df['createTime'].map(lambda x:x.hour) df.head()
读取txt文件中的数据,并通过正则化表达式获得对应含义的数据,将获得的数据以字典的形式化初始化成DataFrame格式(这样做的目的是,DataFrame格式的数据更容易用来分析和可视化);
part2 可视化分析部分:
#可视化分析 #1.获取年——月 对应的评论数量 plt.rcParams['font.sans-serif']=['SimHei'] ###显示中文 plt.rcParams['axes.unicode_minus']=False ##防止坐标轴上的-号变为方块 con_count = df.groupby(['year','month'])['content'].count() #2018.8-2019.2月 x = np.array(range(7)) plt.figure(figsize=(10,4),dpi=90) plt.bar(x,con_count.values,color='#99CC01') plt.title('分月评论数量变化趋势') plt.xlabel('月份') plt.ylabel('数量') labels = ['2018-8','2018-9','2018-10','2018-11','2018-12','2019-1','2019-2'] plt.grid(color='#95a5a6',linestyle='--',axis='y') plt.xticks(x,labels) plt.show()
首先通过groupby获取对应月份的评论数量,然后借matplotlib.pyplot库构建了一个直方图,对月份评论数量变化趋势进行可视化。
#用户购买会员分布情况查看 client_count = df.groupby(['userLevelName'])['content'].count() plt.figure(figsize=(10,4),dpi=90) x = np.array(range(client_count.shape[0])) plt.bar(x,client_count,color='#99CC01') plt.xlabel('会员类型') plt.ylabel('数量') labels = ['PLUS会员','PLUS会员(试用)','企业会员','注册会员','金牌会员','砖石会员','铜牌会员','银牌会员'] plt.title('购买会员分布情况') plt.grid(color='#95a5a6',linestyle='--',axis='y') plt.grid() plt.xticks(x,labels) plt.show()
part3 关键字抽取部分:
#评论语义分析,前20关键字抽取 content_str = ' '.join(df['content'].values) stop_word = ['div','class','jpg','jfs','shaidan','buyimg','com','http','src','uploading','img','uploadimgdiv','uploadimg','border','img30.360'] f = open('./stop_word.txt','w') for i in stop_word: f.write(i) f.write(' ') f.close() import jieba.analyse word_rank = jieba.analyse.extract_tags(content_str, topK=20, withWeight=True, allowPOS=(),) jieba.analyse.set_stop_words('./stop_word.txt') #转化为dataframe word_rank = pd.DataFrame(word_rank,columns=['word','rank']) #查看关键词及权重 word_rank.sort_values(by='rank',ascending=False)
这里运用中文分词工具jieba库中的jieba.analyse抽取了爬取的评论数据中排名前20的词汇;需要注意的是由于爬取的评论信息中可能混有html标签,在抽取关键字时会影响正确的排名顺序;为此,首先将可能出现的html标签构造为一个停用词表,并在抽取关键字之前,通jieba.analyse.s-et_stop_words(path)函数引入停用词;最终我们获得了比较合理的排名前20的关键字。