数据采集部分:
目标网址:
https://item.jd.com/100066896338.html#none
爬虫思路分析:
1.确定采集目标:爬取“苹果15”的评论包括好评、差评、中评以及不同的评论对应的用户名、设备颜色、设备内存大小、版本号、评论发布时间等字段,共3000条以上的评论数目,如下图所示:
2.查看评论来源:打开网页源代码,按住“Ctrl+F”搜索相应评论,发现并无相关对应信息,即可判断该网站使用了“AJax”动态加载技术。
通过谷歌开发者工具,查看评论来源,发现其来自于API接口,点击负载即可获取其样式(包含其页数、评论数目、评论类型等),这里主要是观察score;根据score值的不同其评论类型也不一样。
3.确定数据采集技术:根据观察得一个页面具有10条评论,则每一种评论类型至少需要100个页面的评论数目才可达到需求,为了提高采集效率,这里我主要是运用了协程爬虫技术进行数据采集,并通过定义DateFrame数据框将数据放入其中,最后通过pandas库的pancat()方法对数据框进行拼接并保存为csv文件。
import aiohttp
import asyncio
import json
import pandas as pd
import numpy as np
import time
# 使用async关键字修饰一个协程函数main()
async def main(page, score, file_name):
import requests
url_str = 'https://api.m.jd.com/'
url_list = []
# 将样式复制过来
params = {
'appid': 'item-v3',
'functionId': 'pc_club_productPageComments',
'client': 'pc',
'clientVersion': '1.0.0',
't': '1711815668207',
'loginType': '3',
'uuid': '181111935.16981540772521479889794.1698154077.1711813565.1711815580.10',
'productId': '100066896338',
'score': score,
'sortType': 5,
# 页数从0开始
'page': page,
# 每页具有10条评论
'pageSize': 10,
'isShadowSku': 0,
'fold': 1,
}
# 目的:为了返回完整的请求网址
for i in range(page):
params['page'] = i
response = requests.get(url_str, params=params)
url_list.append(response.url)
# 任务列表
tasks = []
for url in url_list:
print("正在爬取第{}页数据".format(url_list.index(url) + 1))
# task可视为调用协程函数所返回的对象
tasks.append(parse_html(url))
# await用于等待一个Promise对象的解决(即状态变为resolved)后,再继续执行后面的代码。
# 经过实验得知,使用await asyncio.wait(tasks)时会报错,因为asyncio.wait()返回的是一个元组,包含已完成任务的集合和已取消任务的集合,而不是单独的已完成任务。因此,你应该使用await asyncio.gather(*tasks)来等待所有任务完成并获取它们的结果。
# results是一个列表,每个元素都是一个DataFrame;每个异步任务的结果会以列表的形式返回,而不会自动合并为一个DataFrame
results = await asyncio.gather(*tasks)
all_data = pd.concat(results)
print(all_data)
all_data.to_csv(f'{file_name}.csv', encoding='utf-8-sig')
# 解析部分详细代码可查看我往期作品,或者私聊我
if __name__ == '__main__':
# 开始时间
time_start = time.time()
page = int(input("请输入需要爬取的页数:"))
file_name = input("请输入需要保存的文件名称:")
score = input("请输入需要爬取的评论类型(1:差评,2:中评,3:好评):")
# 创建事件循环
loop = asyncio.get_event_loop()
loop.run_until_complete(main(page, score, file_name))
# 结束时间
time_end = time.time()
print(f"一共用了{time_end-time_start}秒")
结果演示:
控制台输出拼接完后的数据框信息以及程序完成的最终运行时间,如下图所示:
输出数据框内数据:
控制台输出程序完成时间:
通过all_data.to_csv('file_name.csv', encoding='')语句即可将数据框保存为csv文件;如下图所示:
结论:
可看出程序完成后的数据框共有10列的1000条数据且爬取100个页面的时间仅用了26.8秒。
数据预处理部分:
1.将分散的文件进行合并:将带有好评、中评、差评的三个csv文件通过读取文件并合并数据框的方式,合并完后的csv文件共3000条数据如下图所示:
2.转变数据类型:通过pands库的to_datetime方法将评论发布时间和购买时间进行数据类型转化,转化结果如下图所示:
import pandas as pd
data_all = [] # 创建一个空列表来存储每个数据框
file_name = ['data1.csv', 'data2.csv', 'data3.csv']
# 合并三个csv文件,统一管理
for file in file_name:
data = pd.read_csv(file)
# 将每个数据框添加到列表中
data_all.append(data)
# 使用 pd.concat() 合并所有数据框
data_all = pd.concat(data_all, ignore_index=True)
# 将指定列数据的时间字符串转化为标准时间
data_all['creationTime'] = pd.to_datetime(data_all['creationTime'])
data_all['referenceTime'] = pd.to_datetime(data_all['referenceTime'])
# 将整合的数据框保存为csv文件
data_all.to_csv('data.csv', encoding='utf-8-sig')
转化前:
转化后:
3.观察列表框内相关数据:通过value_counts()方法来查看该列中的元素出现了几次,通过shape()方法即可观察数据框剩余几行数据;通过columns()方法可看出清洗后的数据框的列索引。
结果展示:
数据可视化部分:
(1)根据评论分词出现频率生成词云图:
1.分词:导入jieba库,将拼接后的data.csv文件进行读取,再对data_all中的content列中的每个文本进行分词操作,使用jieba.lcut函数对文本进行切词。非常值得一提的是:关于中文停用词表可以去我往期作品中直接复制,都是很常用的停用词。
import jieba
from tkinter import _flatten
from wordcloud import WordCloud
import matplotlib.pyplot as plt
import pandas as pd
import matplotlib as mpl
import numpy as np
from PIL import Image
# 不是在Jupyter中运行代码,就可能会出现这个问题。可以通过直接在py文件中设置后端use函数来修复这个错误
mpl.use('TkAgg')
# (1)分词
data_all = pd.read_csv('data.csv', encoding='utf-8')
# 对data_all中的content列中的每个文本进行分词操作,使用jieba.lcut函数对文本进行切词
# apply是DataFrame中的一个方法,用于对DataFrame中的某一列(Series)应用指定的函数
data_cut = data_all['content'].apply(jieba.lcut)
2.去除停用词:读取名为stopword.txt的停用词文件,将文件中的停用词加载到stop_list列表中,然后将额外的词语'买'、'手机'、'苹果'添加到stop_list列表中。接着,对经过分词后的文本数据进行停用词过滤,去除stop_list中的词语。
# 读取名为stopword.txt的停用词文件
with open('stopword.txt', encoding='utf-8')as f:
stop_list = f.read()
# 添加额外通用词
stop_list = stop_list + '买' + '手机' + '苹果'
# 对经过分词后的文本数据data_cut应用lambda函数,该函数会对每个分词后的文本列表进行遍历,只保留不在stop_list中的词语,从而实现停用词过滤
data_after_stop = data_cut.apply(lambda x: [i for i in x if i not in stop_list])
3.统计词频:将经过停用词过滤后的文本数据转换为一个包含所有词语的列表words,然后使用pd.Series(words).value_counts()方法统计每个词语出现的频率,得到词频统计结果。
data_after_stop_list = list(data_after_stop)
words = _flatten(list(data_after_stop_list))
# print(type(words))
# 转化为Serious可使用value_count()方法,统计频率高的词
word_freq = pd.Series(words).value_counts()
4.生成词云图:根据本机内文字字体路径确定生成词云图的文字字体(font_path)、自定义背景图、背景颜色;若未定义文字字体这一步将导致生成图形的文字无法看见,最后通过to_file()方法保存词云图即可。
mask = np.array(Image.open('p11.png'))
# print(mask)
# 创建词云图对象
wc = WordCloud(font_path = 'C:/Windows/Fonts/simhei.ttf' ,mask = mask, background_color='white')
wc.fit_words(word_freq[:80])
plt.imshow(wc)
plt.axis('off')
plt.show()
# 保存生成的词云图
wc.to_file('p13.png')
结果演示:
词云图1:
词云图2:
(2)生成销售数量和评论数量和日期的关系折线图
1.设置绘图参数:字体。
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.use('TkAgg')
# 设置绘图参数
# 指定了 Matplotlib 使用的字体为宋体(SimSun),即 'simHei'。这样设置可以确保在绘制图表时使用宋体字体显示中文文本,避免中文显示乱码的问题。
plt.rcParams['font.sans-serif'] = 'simHei'
# 设置了 Matplotlib 绘图时坐标轴上的负号显示为正常的减号。在某些情况下,默认设置下负号可能显示不正确,通过将该参数设置为 False,可以确保负号显示正确。
plt.rcParams['axes.unicode_minus'] = False
2.将在数据预处理部分的步骤2转化得到的标准时间‘creationTime’、‘referenceTime’求和并排序赋值给num1、num2。
num1 = data_all['referenceTime'].dt.date.value_counts().sort_index()
num2 = data_all['creationTime'].dt.date.value_counts().sort_index()
# 按列方向拼接两份数据
data = pd.concat([num1, num2], axis=1).fillna(0)
3.使用‘gglot’的绘图风格,使用matplotlib.pyplot的plot方法建立折线图;横坐标为数据data的索引范围,纵坐标为data中名为'referenceTime'以及‘creationTime’的列的数值。
采用ggplot的绘图风格
plt.style.use('ggplot')
# 销量随日期的变化趋势, 后者为列索引中前者数量对应的内容(即该内容出现了多少次)
plt.plot(range(len(data)), data['referenceTime'])
# 评论数据随日期变化趋势
plt.plot(range(len(data)), data['creationTime'])
4.最后使用matplotlib.pyplot的xticks方法设置x轴刻度标签、标题等。
# plt.xticks() 是 Matplotlib 库中用来设置 x 轴刻度标签的函数
# range(len(data))[::21] 生成一个从 0 到数据长度的序列,间隔为 21,(0,21,42...),用来指定要显示的刻度位置。
# data.index[::21] 是根据数据的索引生成一个新的索引序列,也是步长为 21,(0,21,42...)用来作为刻度标签的内容。
plt.xticks(range(len(data))[::21], data.index[::21], rotation = 45)
plt.legend(['购买数量', '评论数量'])
plt.xlabel('日期')
plt.ylabel('数量')
plt.title('销售数量和评论数量和日期的关系')
plt.show()
结果演示:
结论:
由图可得2023年10.26日(京东于惊喜红包发放第一阶段)该手机销量呈现爆发式增长乃至11月20日销量发生第二波爆发式增长。
注意:因文章篇幅问题,部分源代码无法展现,如有需要,请在后台私信博主哦~
创作不易,请点个赞哦~
还有更多优秀作品在博主主页~
标签:plt,数据,list,爬虫,干货,import,csv,data From: https://blog.csdn.net/2201_75422674/article/details/137403384