一、实验目的¶
使用 Selenium + chromedriver模拟浏览器行为获取数据,但由于爬虫采集的原始数据往往会存在许多问题,例如数据格式不正确,数据存在缺失、冗余等等。因此第一手获得的原始数据不能直接使用,需要进行数据清洗。本案例对爬取的泰山相关信息数据进行处理,使其成为符合我们要求的数据。
二、实验内容
1. 爬取数据:
- 利用在页码框输入页码从而跳转到所要到达的页面
- 获取指定范围的页面,避免评论时间太过接近
- 爬取内容为用户名,评分,评论日期,评论正文,评论点赞数
- 泰安泰山风景区游玩攻略简介,泰安泰山风景区门票/地址/图片/开放时间/照片/门票价格【携程攻略】
#爬取用户名,评分,评论日期,评论点赞数,评论正文 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from lxml import etree import pandas as pd import time import os import re from pymongo import * #获取网页源代码 def get_page(page): wait = WebDriverWait(driver,10) #找到页码输入框 search_box = driver.find_element_by_xpath('//li[@class="ant-pagination-options"]/div/input') #输入页码 search_box.send_keys(page) #找到确定按钮 search_btn = driver.find_element_by_xpath('//li[@class="ant-pagination-options"]/div/span/button') #找到某一位置 logo = driver.find_element_by_xpath('//*[@id="__next"]/div[5]/div[1]/div[1]/div/h2') driver.execute_script("arguments[0].scrollIntoView(false);", logo) #点击确定按钮 search_btn.click() time.sleep(5) return driver.page_source # 利用XPath提取网页数据 def parse_page(html): dom = etree.HTML(html) #用户名 name = dom.xpath('//div[@class="userName"]/text()') #评论日期 date = dom.xpath('//div[@class="commentTime"]/text()') #评分,直接查找span节点,会多出1个值,因此找div下的即可 score_list = dom.xpath('//div[@class="scroreInfo"]/span[@class="averageScore"]/text()') #取出数字:切片,每3个取一个 score = score_list[::3] #评论点赞数 #爬出的数字为实际点赞数,若爬出的是汉字‘点赞’则点赞数实际为0 dz = dom.xpath('//span[@class="toolsItem"]/text()') #评论 comment_org = dom.xpath('//div[@class="commentDetail"]/text()') #去掉换行符\n comment = [i.replace('\n','') for i in comment_org] #构建DataFrame data = pd.DataFrame({ '用户名':name, '评分':score, '日期':date, '评论点赞数':dz, '评论正文':comment }) return data #每页数据以追加形式保存至csv文件 def save_file(filename, data): #参数为DataFrame if os.path.exists(filename): data.to_csv(filename,mode='a',encoding='utf_8_sig',index=False,header=False) else: data.to_csv(filename,mode='a',encoding='utf_8_sig',index=False) if __name__ == '__main__': filename = '爬取泰山相关信息(原始).csv' url = 'https://you.ctrip.com/sight/mounttai6/136014.html#ctm_ref=www_hp_bs_lst' driver = webdriver.Chrome() driver.get(url) driver.maximize_window() #获取指定的页面,避免评论时间太近 for i in range(1,12): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(21,25): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(40,46): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(50,56): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(60,66): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(70,76): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(80,86): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(100,106): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(110,116): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") for i in range(120,126): html = get_page(i) data = parse_page(html) save_file(filename, data) print("OK!") driver.close() |
2. 读取数据:
(1)导入数据集:爬取泰山相关信息(原始).csv
import pandas as pd
df1 = pd.read_csv(r'C:\此处省略具体路径\爬取泰山相关信息(原始).csv')
df1
在特定位置插入一列‘序号’
df1.insert(0,'序号',[i for i in range (1,631)])
df1
(2)显示前5条记录
df1.head()
3. 查看数据的整体情况¶
(1)查看数据的规模:行数和列数
#查看维度
print(df1.shape)
#获得行数
print(df1.index.size)
#print(df1.shape[0])
#获得列数
print(df1.columns.size)
#print(df1.shape[1])
(2)利用info()查看数据的维度、字段名及类型等
df1.info()
(3)利用describe()查看数据初步统计信息
df1.describe()
4. 数据清洗
(1)删除‘序号’列
del df1['序号']
df1
(2)重复值处理
查看是否存在重复行
df1.duplicated()
查看重复行与非重复行的数量
cf = df1.duplicated()
cf.value_counts()
删除重复行(直接作用于原始数据)
df1.drop_duplicates(inplace=True)
df1
再次查看数据规模
df1.shape
(3)缺失值处理
查看各元素是否为空值
df1.isnull()
查看各列是否存在空值
#只要该列有空值,就为True
df1.isnull().any()
(4)某些值的替换
df1['评论点赞数'].replace('点赞',0,inplace=True)
df1
(5)异常值判断
a=0 #a用来统计异常值的数量
b=0 #b用来统计正常值的数量
for i in df1['评分']:
if i not in range(6):
a=a+1
else:
b=b+1
print("异常值的数量是 {} 个".format(a))
print("正常值的数量是 {} 个".format(b))
再看一下要保存的数据
df1
(6)将清洗完成的数据保存至“爬取泰山相关信息(新).xlsx”
df1.to_excel(r'C:\。。。\爬取泰山相关信息.xlsx',index=False,header=True)
5. 数据分析
(1)选出最有效的评论
#导入数据集
import pandas as pd
df2 = pd.read_excel(r'C:\。。。\爬取泰山相关信息.xlsx')
查看每一列的数据类型
df2.dtypes
将“点赞数”列转换为int类型
df2['评论点赞数'] = df2['评论点赞数'].astype('int')
取出最大点赞数的评论
#取点赞数最大值
df2['评论点赞数'].max()
#取点赞数最大值所在行
index = df2[df2["评论点赞数"] == 682].index.tolist()[0]
index
#取出点赞数最大值的一行数据并提取评论
yxpls = df2[df2.index==443]
yxpl = yxpls['评论正文']
yxpl
(2)查看评分的相关情况
#平均分
pjf = df2['评分'].mean()
print("平均分是{}".format(pjf))
#众数分
zsf = df2['评分'].mode()
print("众数是:{}".format(zsf))
fc = df2['评分'].var()
print("方差是:{}".format(fc))
(3)随机抽取10条数据查看数据情况
sj = df2.sample(10)
sj
#随机抽取数据的平均评分
sjpjf = sj['评分'].mean()
print("随机抽取数据的平均评分是 {} 分".format(sjpjf))
(4)绘制评论正文的词云
import pandas as pd import jieba from tkinter import _flatten import matplotlib.pyplot as plt import wordcloud data1 = pd.read_excel(r'C:\.。。。\爬取泰山相关信息.xlsx') comment = data1['评论正文'] #分词 comment_cut = comment.apply(jieba.lcut) comment_last = [] #一维列表,存放分词结果 for i in comment_cut: for j in i: comment_last.append(j) #或者用下述方法,将二维列表转为一维列表 #comment_last = list(_flatten(list(comment_cut))) #统计词频,去除单个字符 counts = {} for word in comment_last: if len(word) > 1: counts[word] = counts.get(word, 0) + 1 #显示词云 pic = plt.imread(r'C:\。。。\leaf1.jpg') w = wordcloud.WordCloud( mask = pic, #背景图片 background_color = 'white',#词云背景颜色 font_path='C:/Windows/Fonts/simhei.TTF' #设置为中文字体,否则无法正常显示 ) w.fit_words(counts)#传入词频为字典类型,dic为上述字典 #w.generate_from_frequencies(counts) plt.imshow(w) #转为plt图形数据 plt.axis('off')#取消显示x-y轴 plt.show()#展示图形 w.to_file(r'C:\。。。\泰山1.jpg') |
(5)查看点赞数前10的评论正文,并绘制词云
将数据集按点赞数排序并取出前十名
data2 = pd.read_excel(r'C:\。。。\爬取泰山相关信息.xlsx')
w1 = data2.sort_values('评论点赞数',ascending=False).head(10)
w1
import pandas as pd import jieba from tkinter import _flatten import matplotlib.pyplot as plt import wordcloud comment = w1['评论正文'] #分词 comment_cut = comment.apply(jieba.lcut) comment_last = [] #一维列表,存放分词结果 for i in comment_cut: for j in i: comment_last.append(j) #或者用下述方法,将二维列表转为一维列表 #comment_last = list(_flatten(list(comment_cut))) #统计词频,去除单个字符 counts = {} for word in comment_last: if len(word) > 1: counts[word] = counts.get(word, 0) + 1 #显示词云 pic = plt.imread(r'C:\.。。。\leaf1.jpg') w = wordcloud.WordCloud( mask = pic, #背景图片 background_color = 'white',#词云背景颜色 font_path='C:/Windows/Fonts/simhei.TTF' #设置为中文字体,否则无法正常显示 ) w.fit_words(counts)#传入词频为字典类型,dic为上述字典 #w.generate_from_frequencies(counts) plt.imshow(w) #转为plt图形数据 plt.axis('off')#取消显示x-y轴 plt.show()#展示图形 w.to_file(r'C:\。。。\泰山2.jpg') |
拓展查看点赞数前50的评论正文,并绘制词云
data2 = pd.read_excel(r'C:\。。。\爬取泰山相关信息.xlsx')
w2 = data2.sort_values('评论点赞数',ascending=False).head(50)
w2
import pandas as pd import jieba from tkinter import _flatten import matplotlib.pyplot as plt import wordcloud comment = w2['评论正文'] #分词 comment_cut = comment.apply(jieba.lcut) comment_last = [] #一维列表,存放分词结果 for i in comment_cut: for j in i: comment_last.append(j) #或者用下述方法,将二维列表转为一维列表 #comment_last = list(_flatten(list(comment_cut))) #统计词频,去除单个字符 counts = {} for word in comment_last: if len(word) > 1: counts[word] = counts.get(word, 0) + 1 #显示词云 pic = plt.imread(r'C:\。。。\leaf1.jpg') w = wordcloud.WordCloud( mask = pic, #背景图片 background_color = 'white',#词云背景颜色 font_path='C:/Windows/Fonts/simhei.TTF' #设置为中文字体,否则无法正常显示 ) w.fit_words(counts)#传入词频为字典类型,dic为上述字典 #w.generate_from_frequencies(counts) plt.imshow(w) #转为plt图形数据 plt.axis('off')#取消显示x-y轴 plt.show()#展示图形 w.to_file(r'C:\。。。\泰山3.jpg') |
拓展查看点赞数后30的评论正文,并绘制词云
data2 = pd.read_excel(r'C:\。。。\爬取泰山相关信息.xlsx')
w3 = data2.sort_values('评论点赞数').head(30)
w3
import pandas as pd import jieba from tkinter import _flatten import matplotlib.pyplot as plt import wordcloud comment = w3['评论正文'] #分词 comment_cut = comment.apply(jieba.lcut) comment_last = [] #一维列表,存放分词结果 for i in comment_cut: for j in i: comment_last.append(j) #或者用下述方法,将二维列表转为一维列表 #comment_last = list(_flatten(list(comment_cut))) #统计词频,去除单个字符 counts = {} for word in comment_last: if len(word) > 1: counts[word] = counts.get(word, 0) + 1 #显示词云 pic = plt.imread(r'C:\.。。。\leaf1.jpg') w = wordcloud.WordCloud( mask = pic, #背景图片 background_color = 'white',#词云背景颜色 font_path='C:/Windows/Fonts/simhei.TTF' #设置为中文字体,否则无法正常显示 ) w.fit_words(counts)#传入词频为字典类型,dic为上述字典 #w.generate_from_frequencies(counts) plt.imshow(w) #转为plt图形数据 plt.axis('off')#取消显示x-y轴 plt.show()#展示图形 w.to_file(r'C:\。。。。\泰山4.jpg') |
(6)统计各个评分对应的人数,并用饼图进行可视化展示
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
#统计每个评分对应的人数,结果为Series类型:索引为评分,值为人数
num = data1['评分'].value_counts()
print(num)
plt.figure(figsize=(8,8)) #设置画布
plt.pie(num, labels=num.index, autopct = '%.2f%%')
#plt.axis("equal")
plt.title("不同评分对应人数饼图")
plt.savefig(r'C:\。。。\饼图1')#保存图片
plt.show()
(7)统计不同日期评分的数量及评分的平均值
dd = data1.groupby('日期')['评分'].agg(['count','mean'])
dd
(8)统计不同月份评分的数量及评分的平均值
导入数据集
import pandas as pd
df3 = pd.read_excel(r'C:\。。。\爬取泰山相关信息.xlsx')
进行相关处理
#取出日期中的月份
a=[]
for i in df3['日期']:
a.append(i[5:7])
print(a)
#将月份插入到数据集中(位置指定)
df3.insert(2,'月份',a)
df3
#处理查看
ppp = df3.groupby('月份')['评分'].agg(['count','mean'])
ppp
#按评分的平均值进行排序
ttt = ppp.sort_values('mean',ascending=False)
ttt
#按count值进行排序
ttt2 = ppp.sort_values('count',ascending=False)
ttt2
#按评分的平均值,count值共同进行排序
ttt3 = ppp.sort_values(['mean','count'],ascending=False)
ttt3
(9)将评分与点赞数数据标准化
df3[['评分','评论点赞数']] = df3[['评分','评论点赞数']].apply(lambda x:(x-x.min())/(x.max()-x.min()))
df3.head()
df3.describe()
(10)将评分离散化并统计各等级的数量(优良差)
df4 = pd.read_excel(r'C:\。。。\爬取泰山相关信息.xlsx')
#数据类型的转化
df4['评分'] = df4['评分'].astype('int')
df4['评分'] = pd.cut(df4['评分'],bins=[0,3,4,5],labels=['差','良','好'], include_lowest=True)
df4
(11)将评分离散化后绘制饼图
import pandas as pd import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False #统计每个评分对应的人数,结果为Series类型:索引为评分,值为人数 num = df4['评分'].value_counts() print(num) plt.figure(figsize=(8,8)) #设置画布 plt.pie(num, labels=num.index, autopct = '%.2f%%') #plt.axis("equal") plt.title("评分离散化后") plt.savefig(r'C:\.。。。\饼图2')#保存图片 plt.show() |