一、选题的背景
对电商来说,抓取某些数据,再进行分析,可以有效地反映出数据在某个区间内变化情况。数据受某些因素而发生巨大的影响,也可以借助分析的数据来规划相关项目的后续发展。因此,如果能利用网页爬取数据技术获取数据并对各种数据进行统计分析,对后续电商的发展具有指导意义。所以本次通过对京东上口红的信息进行爬取和分析,以获取有关口红市场趋势、销售情况、品牌排名、价格分布等信息,为进行市场调研、产品开发、销售策略制定等活动提供依据。
二、主题式网络爬虫设计方案
1.主题式网络爬虫名称
Python网络爬虫—对京东口红销售的数据分析
2.主题式网络爬虫爬取的内容与数据特征分析
爬取相关口号数据,包含价格,色号,销量,评价等。并通过数据可视化表现出来。
3.主题式网络爬虫设计方案概述(包括实现思路与技术难点)
1. 数据采集。
2. 进行数据的清洗,对需要的数据进行定位和提取,并进行存取。
3. 传入数据,进一步数据可视化。
三、主题页面的结构特征分析
1.主题页面的结构与特征分析
商品界面:
2.Htmls 页面解析
解析每条数据中的商品名称、价格、用户评分、用户评论,爬取完毕后进行翻页,直到最后一页。
四、网络爬虫程序设计
1.数据爬取与采集并进行清洗
(1)
自定义抓取数据的类
# 自定义抓取数据的类 class CrawlSpider: # 类函数,定义初始调用时,传入文件名称参数,用于存储抓取的数据 def __init__(self, file_name): self.file_name = file_name # 写入数据表头部分 self.file_header = ['sku_num', 'shop', 'price', 'prod_amount', 'good_com_count', 'bad_com_count', 'p_brand', 'p_color', 'p_place', 'p_function', 'good_rate'] # pandas读取csv文件并保存为Excel文件 def csv_to_excel(self): data_df = pd.read_csv(self.file_name + '.csv', header=None, names=self.file_header) data_df.to_excel(self.file_name + '.xlsx', index=False) # 删除原csv文件 os.remove(self.file_name + '.csv') print(f'数据抓取完成,数据文件:{self.file_name}.xlsx 已生成!')
(2)请求异常等问题的解决
# 请求异常捕获函数,防止网络中断等问题造成访问失败而报错,最终返回请求url的网页源代码 def request_again(self, crawl_url, retry_times=6): while retry_times: retry_times -= 1 try: # 定义请求头 User-Agent headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" } response = t.get(crawl_url, headers=headers, timeout=5) resp = response code = response.status_code # 判断请求状态码以及响应是否为空,为空则继续,反之则返回请求体 if code == 200 and resp.content: return resp.text except Exception as e: print(e) # 重试次数用完,则直接返回异常数据 if not retry_times: return '' # 随机休眠 2-3s time.sleep(random.randint(2, 3))
(3)爬取评论并进行清洗
# 获取评论相关信息,总评论、好评、差评、好评率等,并做相应的清洗 def crawl_comments(self, sku_num): comm_url = 'https://club.jd.com/comment/productPageComments.action?productId={}&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0&fold=1'.format( sku_num) comm_infos = self.request_again(comm_url) try: comm_obj = json.loads(comm_infos).get('productCommentSummary') except: comm_obj = {} if comm_obj: try: # json解析总评论数,并将 万字转成对应的值乘10000 prod_amount_ = comm_obj.get("commentCountStr") if '万' in prod_amount_: prod_amount = int(eval(prod_amount_.replace('万', '').strip().strip('+')) * 10000) else: prod_amount = int(prod_amount_.strip()) except: prod_amount = None try: # json解析好评论数,并将 万字转成对应的值乘10000 good_count_ = comm_obj.get("goodCountStr") if '万' in good_count_: good_count = int(eval(good_count_.replace('万', '').strip().strip('+')) * 10000) else: good_count = int(good_count_.strip()) except: good_count = None try: # json解析差评论数,并将 万字转成对应的值乘10000 bad_count_ = comm_obj.get("poorCountStr") if '万' in bad_count_: bad_count = int(eval(bad_count_.replace('万').strip().strip('+')) * 10000) else: bad_count = int(bad_count_.strip()) except: bad_count = 0 # json解析好评率 good_rate = comm_obj.get("goodRate") return prod_amount, good_count, bad_count, good_rate return None, None, None, None
(4)爬取商品详情
# 抓取商品详情部分,解析相关字段 def crawl_detail(self, sku_num): de_url = 'https://item.jd.com/{}.html'.format(sku_num) print(de_url) kh_html = self.request_again(de_url) de_obj = etree.HTML(kh_html) # xpath解析 品牌 p_brand = ''.join(de_obj.xpath('//li[contains(text(), "品牌:")]/@title')).strip() # xpath解析 色系 p_color = ''.join(de_obj.xpath('//li[contains(text(), "色系:")]/@title')).strip() # xpath解析 商品产地 p_place = ''.join(de_obj.xpath('//li[contains(text(), "商品产地:")]/@title')).strip() # xpath解析 功效 p_function = ''.join(de_obj.xpath('//li[contains(text(), "功效:")]/@title')).strip() return p_brand, p_color, p_place, p_function
(5)爬取商品列表
# 抓取商品列表部分,依次遍历抓取每个商品的相关字段信息 def crawl_page(self, list_url): page_html = self.request_again(list_url) # xpath解析商品版块信息 prod_ports = etree.HTML(page_html).xpath('//li[contains(@class, "gl-item")]') for prod in prod_ports: # xpath解析商品店铺名称 shop = ''.join(prod.xpath('.//*[@class="curr-shop hd-shopname"]/@title')).strip() # xpath解析商品单价 try: price = eval(''.join(prod.xpath('.//i[@data-price]/text()')).strip()) except: price = None # xpath解析商品sku sku_num = prod.xpath('./@data-sku')[0] # 调用 crawl_comments 函数,获取总、好、差评数以及好评率 prod_amount, good_com_count, bad_com_count, good_rate = self.crawl_comments(sku_num) # 调用 crawl_detail函数,获取详情里的部分字段 p_brand, p_color, p_place, p_function = self.crawl_detail(sku_num) # 如果品牌数据不为空,则写入文件里 if p_brand: datas = [sku_num, shop, price, prod_amount, good_com_count, bad_com_count, p_brand, p_color, p_place, p_function, good_rate] print(datas) with open(f'{self.file_name}.csv', 'a', encoding='utf-8', newline='') as f: csvwriter = csv.writer(f) csvwriter.writerow(datas) #随机休眠2-3s time.sleep(random.randint(2, 3))
(6)主函数
# 爬虫运行的主函数 def run(self): # 翻页抓取1-41页 for page in range(1, 41): print(f'商品列表页:{page} 正在抓取!') url_ls = 'https://search.jd.com/Search?keyword=口红&qrst=1&wq=口红&stock=1&pvid=29fbc476422e4853bc216c0f56b12073&page={}' self.crawl_page(url_ls.format(page)) time.sleep(random.randint(2, 3)) # 抓取完成后,调用文件转换函数,将抓取结果装成Excel文件 self.csv_to_excel()
2.对数据进行清洗和处理
(1)原始数据
(2)进行数据清洗
def reduce_data(self): # 将品牌字段中括号后的部分全部清除 self.data_df['p_brand'] = self.data_df.p_brand.apply(lambda x: re.sub('(.*', '', x)) # 将口红颜色字段中,多种颜色情况下,只取第一个颜色,并且去除末尾的“系”字 self.data_df['p_color'] = self.data_df.p_color.astype('str') self.data_df['p_color'] = self.data_df.p_color.apply(lambda x: x.split('/')[0].strip('系')) # 将价格字段清洗成 float 类型 self.data_df['price'] = self.data_df.price.astype('float') # 按sku唯一字段去除数据集中重复的数据 self.data_df.drop_duplicates(subset=['sku_num'], keep='first', inplace=True) # 将好评率字段缺失值填充为 未知,并转化为带 % 号的形式 self.data_df['good_rate'] = self.data_df.good_rate.astype('str') self.data_df.good_rate.fillna('未知', inplace=True) self.data_df['good_rate'] = self.data_df.good_rate.apply(lambda x: str(eval(x) * 100) + '%' if '0.' in x else x) self.data_df['good_rate'] = self.data_df.good_rate.str.replace('1.0', '100%') # 将差评字段为空的,填充取值为0 self.data_df['bad_com_count'].fillna(0, inplace=True) # 将总评论数转为数值型: self.data_df['prod_amount'] = self.data_df.prod_amount.astype('float') # 将 产地取值为 “未知”的置为空置 self.data_df['p_place'] = self.data_df.p_place.str.replace('未知', '') # 将好评字段为空的,填充取值为0 self.data_df['good_com_count'].fillna(0, inplace=True) self.data_df.to_excel(r'分析结果\口红数据清洗结果.xlsx') return self.data_df
(3)数据清洗结果
3.数据分析与可视化
(1)绘制口红价格折线图
def plot_1(self): # 折线图将京东口红价格进行可视化,分价格区间进行统计分析 self.qx_df['df_price_area'] = self.qx_df.price.apply(lambda x: '50元以下' if x <= 50 else '51-100元' if x <= 100 else '101-150' if x <= 150 else '151-200元' if x <= 200 else '201-250元' if x <= 250 else '251-300元' if x <= 300 else '301-350元' if x <= 350 else '351元以上') # 按价格区间分组,统计 df_price_group = self.qx_df.groupby('df_price_area').count() df_price_group.sort_values(by='sku_num', ascending=False, inplace=True) # 绘制 口红价格分布折线图 折线图 line = ( Line().add_xaxis(xaxis_data=list(df_price_group.index)).add_yaxis( series_name="频数", y_axis=list(df_price_group.sku_num), symbol="arrow", is_symbol_show=True ) .set_global_opts(title_opts=opts.TitleOpts(title="口红价格分布折线图")) ) return line
(2)绘制销量和色号的玫瑰图
def pie_1(self): # 口红色号与销量玫瑰图, 圆心角越大,表示该类型销量更大 color_df = self.qx_df[self.qx_df.p_color != ''] # 按口红色系分组,求和 color_group = color_df.groupby('p_color').sum() # 绘制 口红色号与销量玫瑰图 c = ( Pie().add( series_name="", data_pair=[ list(z) for z in zip(color_group.index, color_group.prod_amount) ], radius=["20%", "75%"], rosetype="radius", label_opts=opts.LabelOpts(is_show=True), ) .set_global_opts( title_opts=opts.TitleOpts(title="口红色号与销量玫瑰图", pos_left='center'), legend_opts=opts.LegendOpts(pos_left="center", pos_bottom='0', is_show=True, type_='plain') ) ) return c
(3)绘制好评率柱状图
def bar_1(self): # 柱状图将京东口红好评率进行可视化 good_rate_df = self.qx_df.groupby('good_rate').count() # 按好评率统计结果升序排序 good_rate_df.sort_values(by='sku_num', ascending=True, inplace=True) # 绘制 口红好评率柱状图 bar = Bar() bar.set_global_opts( title_opts=opts.TitleOpts(title="口红好评率柱状图", pos_left='center'), legend_opts=opts.LegendOpts(pos_left="center", pos_bottom='0', is_show=True, type_='plain'), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 60}) ) # 柱状图添加x轴 bar.add_xaxis(list(good_rate_df.index)) # 柱状图添加y轴 bar.add_yaxis("数量", list(good_rate_df.sku_num)) return bar
(4)绘制评价折线图
def plot_2(self): # 折线图图京东口红店铺总评论数前十 df_shop = self.qx_df.groupby('shop').sum() df_shop.sort_values(by='prod_amount', ascending=False, inplace=True) line = ( Line() .add_xaxis(xaxis_data=list(df_shop.index)[:10]).add_yaxis(series_name="总评论数", y_axis=list(df_shop.prod_amount)[:10], symbol="arrow", is_symbol_show=True) .set_global_opts(title_opts=opts.TitleOpts(title="店铺总评论数Top10"), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 15}) ) ) return line
(5)绘制产品总数柱状图
def bar_2(self): # 各品牌产品总数前10柱状图展示 df_brand = self.qx_df.groupby('p_brand').count() df_brand.sort_values(by='sku_num', ascending=False, inplace=True) df_brand = df_brand.iloc[:15, :] brand_bar = Bar() brand_bar.set_global_opts( title_opts=opts.TitleOpts(title="产品总数前15品牌榜", pos_left='center'), legend_opts=opts.LegendOpts(pos_left="right", pos_top=20, is_show=True, type_='plain'), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 60}) ) brand_bar.add_xaxis(list(df_brand.index)) brand_bar.add_yaxis("产品总数", list(df_brand.sku_num), color='steelblue') return brand_bar
(6)绘制词云图
def word_cloud(self): # 特效词云图 # 添加词频数据 filter_df = self.qx_df[self.qx_df.p_function.notnull()] sent_list = list(filter_df.p_function) # 构造词字典,统计个各个词出现的总次数 dic = {} # 遍历每个词并统计 for sent in sent_list: if sent: # 按 ','分割,然后依次统计每个 产品功效描述的出现频次 f_list = sent.split(',') if ',' in sent else [sent] for word in f_list: # 统计结果 dic[word.strip()] = dic.get(word.strip(), 0) + 1 # 加载词云背景图 mask = np.array(Image.open('词云背景图.png')) wc = wordcloud.WordCloud( background_color='white', # 设定图背景色 font_path=r'C:\Windows\Fonts\STXINWEI.ttf',# 加载图字体 width=800,# 设定图宽度 height=600,# 设定图高度 mask=mask,# 设定图外形 ) # 将词填充到图上 wc.fit_words(dic) plt.imshow(wc) # 关闭图坐标轴 plt.axis('off') plt.show()
(7)绘制评价对比折线图
def plot_3(self): # 产地总评论数前10的好差评对比折线图 place_df = self.qx_df[self.qx_df.p_place != ''] # 按产地分组求和 place_df = place_df.groupby('p_place').sum() # 然后降序排列 place_df.sort_values(by='prod_amount', ascending=False, inplace=True) place_df = place_df.head(10) # 绘制 总评论数前10的产地对应好总评对比图 comment_line = ( Line() .add_xaxis(xaxis_data=list(place_df.index)) .add_yaxis(series_name="好评数", y_axis=list(place_df.good_com_count), symbol="arrow", is_symbol_show=True) .add_yaxis(series_name="总评数", y_axis=list(place_df.prod_amount), is_symbol_show=True) .set_global_opts( title_opts=opts.TitleOpts(title="总评论数前10的产地对应好总评对比图"), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 60}), legend_opts=opts.LegendOpts(pos_right="right", pos_bottom='center', is_show=True, type_='plain') ) ) return comment_line
(8)绘制差评区间饼图
def pie_2(self): # 差评区间饼图展示 self.qx_df['bad_com_count'] = self.qx_df.bad_com_count.apply( lambda x: '无差评' if x == 0 else '1-500' if 1 < x <= 500 else '500-1000个' if 500 < x <= 1000 else '1000-1500个' if 1000 < x <= 1500 else '1500个以上') bad_df = self.qx_df.groupby('bad_com_count').count() # 绘制差评区间饼图 pie = ( Pie() .add( "", [list(i) for i in zip(list(bad_df.index), list(bad_df.sku_num))], radius=["45%", "75%"], center=["50%", "56%"], label_opts=opts.LabelOpts(is_show=False, position="center"), ).set_global_opts( title_opts=opts.TitleOpts(title='差评区间饼图', pos_top="13", pos_left="center"), legend_opts=opts.LegendOpts(orient="vertical", pos_top="25%", pos_left="8%") ).set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {d}%"))) return pie
五.源码
import json import os import random import re import pandas as pd from lxml import etree import requests as t import time import csv from pyecharts import options as opts from pyecharts.charts import Line, Bar, Pie, Tab import wordcloud from PIL import Image import matplotlib.pyplot as plt import numpy as np plt.rcParams['font.sans-serif'] = ['SimHei'] # 用于正常显示中文标签 # 自定义抓取数据的类 class CrawlSpider: # 类函数,定义初始调用时,传入文件名称参数,用于存储抓取的数据 def __init__(self, file_name): self.file_name = file_name # 写入数据表头部分 self.file_header = ['sku_num', 'shop', 'price', 'prod_amount', 'good_com_count', 'bad_com_count', 'p_brand', 'p_color', 'p_place', 'p_function', 'good_rate'] # pandas读取csv文件并保存为Excel文件 def csv_to_excel(self): data_df = pd.read_csv(self.file_name + '.csv', header=None, names=self.file_header) data_df.to_excel(self.file_name + '.xlsx', index=False) # 删除原csv文件 os.remove(self.file_name + '.csv') print(f'数据抓取完成,数据文件:{self.file_name}.xlsx 已生成!') # 请求异常捕获函数,防止网络中断等问题造成访问失败而报错,最终返回请求url的网页源代码 def request_again(self, crawl_url, retry_times=6): while retry_times: retry_times -= 1 try: # 定义请求头 User-Agent headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" } response = t.get(crawl_url, headers=headers, timeout=5) resp = response code = response.status_code # 判断请求状态码以及响应是否为空,为空则继续,反之则返回请求体 if code == 200 and resp.content: return resp.text except Exception as e: print(e) # 重试次数用完,则直接返回异常数据 if not retry_times: return '' # 随机休眠 2-3s time.sleep(random.randint(2, 3)) # 获取评论相关信息,总评论、好评、差评、好评率等,并做相应的清洗 def crawl_comments(self, sku_num): comm_url = 'https://club.jd.com/comment/productPageComments.action?productId={}&score=0&sortType=5&page=0&pageSize=10&isShadowSku=0&fold=1'.format( sku_num) comm_infos = self.request_again(comm_url) try: comm_obj = json.loads(comm_infos).get('productCommentSummary') except: comm_obj = {} if comm_obj: try: # json解析总评论数,并将 万字转成对应的值乘10000 prod_amount_ = comm_obj.get("commentCountStr") if '万' in prod_amount_: prod_amount = int(eval(prod_amount_.replace('万', '').strip().strip('+')) * 10000) else: prod_amount = int(prod_amount_.strip()) except: prod_amount = None try: # json解析好评论数,并将 万字转成对应的值乘10000 good_count_ = comm_obj.get("goodCountStr") if '万' in good_count_: good_count = int(eval(good_count_.replace('万', '').strip().strip('+')) * 10000) else: good_count = int(good_count_.strip()) except: good_count = None try: # json解析差评论数,并将 万字转成对应的值乘10000 bad_count_ = comm_obj.get("poorCountStr") if '万' in bad_count_: bad_count = int(eval(bad_count_.replace('万').strip().strip('+')) * 10000) else: bad_count = int(bad_count_.strip()) except: bad_count = 0 # json解析好评率 good_rate = comm_obj.get("goodRate") return prod_amount, good_count, bad_count, good_rate return None, None, None, None # 抓取商品详情部分,解析相关字段 def crawl_detail(self, sku_num): de_url = 'https://item.jd.com/{}.html'.format(sku_num) print(de_url) kh_html = self.request_again(de_url) de_obj = etree.HTML(kh_html) # xpath解析 品牌 p_brand = ''.join(de_obj.xpath('//li[contains(text(), "品牌:")]/@title')).strip() # xpath解析 色系 p_color = ''.join(de_obj.xpath('//li[contains(text(), "色系:")]/@title')).strip() # xpath解析 商品产地 p_place = ''.join(de_obj.xpath('//li[contains(text(), "商品产地:")]/@title')).strip() # xpath解析 功效 p_function = ''.join(de_obj.xpath('//li[contains(text(), "功效:")]/@title')).strip() return p_brand, p_color, p_place, p_function # 抓取商品列表部分,依次遍历抓取每个商品的相关字段信息 def crawl_page(self, list_url): page_html = self.request_again(list_url) # xpath解析商品版块信息 prod_ports = etree.HTML(page_html).xpath('//li[contains(@class, "gl-item")]') for prod in prod_ports: # xpath解析商品店铺名称 shop = ''.join(prod.xpath('.//*[@class="curr-shop hd-shopname"]/@title')).strip() # xpath解析商品单价 try: price = eval(''.join(prod.xpath('.//i[@data-price]/text()')).strip()) except: price = None # xpath解析商品sku sku_num = prod.xpath('./@data-sku')[0] # 调用 crawl_comments 函数,获取总、好、差评数以及好评率 prod_amount, good_com_count, bad_com_count, good_rate = self.crawl_comments(sku_num) # 调用 crawl_detail函数,获取详情里的部分字段 p_brand, p_color, p_place, p_function = self.crawl_detail(sku_num) # 如果品牌数据不为空,则写入文件里 if p_brand: datas = [sku_num, shop, price, prod_amount, good_com_count, bad_com_count, p_brand, p_color, p_place, p_function, good_rate] print(datas) with open(f'{self.file_name}.csv', 'a', encoding='utf-8', newline='') as f: csvwriter = csv.writer(f) csvwriter.writerow(datas) #随机休眠2-3s time.sleep(random.randint(2, 3)) # 爬虫运行的主函数 def run(self): # 翻页抓取1-41页 for page in range(1, 41): print(f'商品列表页:{page} 正在抓取!') url_ls = 'https://search.jd.com/Search?keyword=口红&qrst=1&wq=口红&stock=1&pvid=29fbc476422e4853bc216c0f56b12073&page={}' self.crawl_page(url_ls.format(page)) time.sleep(random.randint(2, 3)) # 抓取完成后,调用文件转换函数,将抓取结果装成Excel文件 self.csv_to_excel() # 自定义 数据分析可视化类 class AnalysisData: # 类函数,传入文件名,方便加载数据文件 def __init__(self, file_name): self.file_name = file_name self.data_df = pd.read_excel(self.file_name + '.xlsx', sheet_name=0, header=0) def reduce_data(self): # 将品牌字段中括号后的部分全部清除 self.data_df['p_brand'] = self.data_df.p_brand.apply(lambda x: re.sub('(.*', '', x)) # 将口红颜色字段中,多种颜色情况下,只取第一个颜色,并且去除末尾的“系”字 self.data_df['p_color'] = self.data_df.p_color.astype('str') self.data_df['p_color'] = self.data_df.p_color.apply(lambda x: x.split('/')[0].strip('系')) # 将价格字段清洗成 float 类型 self.data_df['price'] = self.data_df.price.astype('float') # 按sku唯一字段去除数据集中重复的数据 self.data_df.drop_duplicates(subset=['sku_num'], keep='first', inplace=True) # 将好评率字段缺失值填充为 未知,并转化为带 % 号的形式 self.data_df['good_rate'] = self.data_df.good_rate.astype('str') self.data_df.good_rate.fillna('未知', inplace=True) self.data_df['good_rate'] = self.data_df.good_rate.apply(lambda x: str(eval(x) * 100) + '%' if '0.' in x else x) self.data_df['good_rate'] = self.data_df.good_rate.str.replace('1.0', '100%') # 将差评字段为空的,填充取值为0 self.data_df['bad_com_count'].fillna(0, inplace=True) # 将总评论数转为数值型: self.data_df['prod_amount'] = self.data_df.prod_amount.astype('float') # 将 产地取值为 “未知”的置为空置 self.data_df['p_place'] = self.data_df.p_place.str.replace('未知', '') # 将好评字段为空的,填充取值为0 self.data_df['good_com_count'].fillna(0, inplace=True) self.data_df.to_excel(r'分析结果\口红数据清洗结果.xlsx') return self.data_df def plot_1(self): # 折线图将京东口红价格进行可视化,分价格区间进行统计分析 self.qx_df['df_price_area'] = self.qx_df.price.apply(lambda x: '50元以下' if x <= 50 else '51-100元' if x <= 100 else '101-150' if x <= 150 else '151-200元' if x <= 200 else '201-250元' if x <= 250 else '251-300元' if x <= 300 else '301-350元' if x <= 350 else '351元以上') # 按价格区间分组,统计 df_price_group = self.qx_df.groupby('df_price_area').count() df_price_group.sort_values(by='sku_num', ascending=False, inplace=True) # 绘制 口红价格分布折线图 折线图 line = ( Line().add_xaxis(xaxis_data=list(df_price_group.index)).add_yaxis( series_name="频数", y_axis=list(df_price_group.sku_num), symbol="arrow", is_symbol_show=True ) .set_global_opts(title_opts=opts.TitleOpts(title="口红价格分布折线图")) ) return line def pie_1(self): # 口红色号与销量玫瑰图, 圆心角越大,表示该类型销量更大 color_df = self.qx_df[self.qx_df.p_color != ''] # 按口红色系分组,求和 color_group = color_df.groupby('p_color').sum() # 绘制 口红色号与销量玫瑰图 c = ( Pie().add( series_name="", data_pair=[ list(z) for z in zip(color_group.index, color_group.prod_amount) ], radius=["20%", "75%"], rosetype="radius", label_opts=opts.LabelOpts(is_show=True), ) .set_global_opts( title_opts=opts.TitleOpts(title="口红色号与销量玫瑰图", pos_left='center'), legend_opts=opts.LegendOpts(pos_left="center", pos_bottom='0', is_show=True, type_='plain') ) ) return c def bar_1(self): # 柱状图将京东口红好评率进行可视化 good_rate_df = self.qx_df.groupby('good_rate').count() # 按好评率统计结果升序排序 good_rate_df.sort_values(by='sku_num', ascending=True, inplace=True) # 绘制 口红好评率柱状图 bar = Bar() bar.set_global_opts( title_opts=opts.TitleOpts(title="口红好评率柱状图", pos_left='center'), legend_opts=opts.LegendOpts(pos_left="center", pos_bottom='0', is_show=True, type_='plain'), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 60}) ) # 柱状图添加x轴 bar.add_xaxis(list(good_rate_df.index)) # 柱状图添加y轴 bar.add_yaxis("数量", list(good_rate_df.sku_num)) return bar def plot_2(self): # 折线图图京东口红店铺总评论数前十 df_shop = self.qx_df.groupby('shop').sum() df_shop.sort_values(by='prod_amount', ascending=False, inplace=True) line = ( Line() .add_xaxis(xaxis_data=list(df_shop.index)[:10]).add_yaxis(series_name="总评论数", y_axis=list(df_shop.prod_amount)[:10], symbol="arrow", is_symbol_show=True) .set_global_opts(title_opts=opts.TitleOpts(title="店铺总评论数Top10"), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 15}) ) ) return line def bar_2(self): # 各品牌产品总数前10柱状图展示 df_brand = self.qx_df.groupby('p_brand').count() df_brand.sort_values(by='sku_num', ascending=False, inplace=True) df_brand = df_brand.iloc[:15, :] brand_bar = Bar() brand_bar.set_global_opts( title_opts=opts.TitleOpts(title="产品总数前15品牌榜", pos_left='center'), legend_opts=opts.LegendOpts(pos_left="right", pos_top=20, is_show=True, type_='plain'), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 60}) ) brand_bar.add_xaxis(list(df_brand.index)) brand_bar.add_yaxis("产品总数", list(df_brand.sku_num), color='steelblue') return brand_bar def word_cloud(self): # 特效词云图 # 添加词频数据 filter_df = self.qx_df[self.qx_df.p_function.notnull()] sent_list = list(filter_df.p_function) # 构造词字典,统计个各个词出现的总次数 dic = {} # 遍历每个词并统计 for sent in sent_list: if sent: # 按 ','分割,然后依次统计每个 产品功效描述的出现频次 f_list = sent.split(',') if ',' in sent else [sent] for word in f_list: # 统计结果 dic[word.strip()] = dic.get(word.strip(), 0) + 1 # 加载词云背景图 mask = np.array(Image.open('词云背景图.png')) wc = wordcloud.WordCloud( background_color='white', # 设定图背景色 font_path=r'C:\Windows\Fonts\STXINWEI.ttf',# 加载图字体 width=800,# 设定图宽度 height=600,# 设定图高度 mask=mask,# 设定图外形 ) # 将词填充到图上 wc.fit_words(dic) plt.imshow(wc) # 关闭图坐标轴 plt.axis('off') plt.show() # 词云保存到文件里 wc.to_file(r'分析结果\特效词云图.png') def plot_3(self): # 产地总评论数前10的好差评对比折线图 place_df = self.qx_df[self.qx_df.p_place != ''] # 按产地分组求和 place_df = place_df.groupby('p_place').sum() # 然后降序排列 place_df.sort_values(by='prod_amount', ascending=False, inplace=True) place_df = place_df.head(10) # 绘制 总评论数前10的产地对应好总评对比图 comment_line = ( Line() .add_xaxis(xaxis_data=list(place_df.index)) .add_yaxis(series_name="好评数", y_axis=list(place_df.good_com_count), symbol="arrow", is_symbol_show=True) .add_yaxis(series_name="总评数", y_axis=list(place_df.prod_amount), is_symbol_show=True) .set_global_opts( title_opts=opts.TitleOpts(title="总评论数前10的产地对应好总评对比图"), xaxis_opts=opts.AxisOpts(name_rotate=100, axislabel_opts={"rotate": 60}), legend_opts=opts.LegendOpts(pos_right="right", pos_bottom='center', is_show=True, type_='plain') ) ) return comment_line def pie_2(self): # 差评区间饼图展示 self.qx_df['bad_com_count'] = self.qx_df.bad_com_count.apply( lambda x: '无差评' if x == 0 else '1-500' if 1 < x <= 500 else '500-1000个' if 500 < x <= 1000 else '1000-1500个' if 1000 < x <= 1500 else '1500个以上') bad_df = self.qx_df.groupby('bad_com_count').count() # 绘制差评区间饼图 pie = ( Pie() .add( "", [list(i) for i in zip(list(bad_df.index), list(bad_df.sku_num))], radius=["45%", "75%"], center=["50%", "56%"], label_opts=opts.LabelOpts(is_show=False, position="center"), ).set_global_opts( title_opts=opts.TitleOpts(title='差评区间饼图', pos_top="13", pos_left="center"), legend_opts=opts.LegendOpts(orient="vertical", pos_top="25%", pos_left="8%") ).set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {d}%"))) return pie # 分析程序入口函数 def run(self): # 如果 分析结果 文件路径不存在,则创建,反之则跳过 if not os.path.exists('分析结果'): os.mkdir('分析结果') self.qx_df = self.reduce_data() # 可视化函数调用 plot_1_pic = self.plot_1() pie_1_pic = self.pie_1() bar_1_pic = self.bar_1() plot_2_pic = self.plot_2() bar_2_pic = self.bar_2() # 调用 构造词云的函数 self.word_cloud() plot_3_pic = self.plot_3() pie_2_pic = self.pie_2() # 通过pyecharts的table功能,将每个图汇集到一张图上 tab_nav = Tab('京东口红商品数据可视化结果') tab_nav.add(plot_1_pic, '口红价格分布图') tab_nav.add(pie_1_pic, '口红色号与销量图') tab_nav.add(bar_1_pic, '口红好评率分布图') tab_nav.add(plot_2_pic, '店铺总评论数Top10分布图') tab_nav.add(bar_2_pic, '产品总数前15品牌榜') tab_nav.add(plot_3_pic, '总评论数前10的产地对应好总评对比图') tab_nav.add(pie_2_pic, '差评区间饼图') # 将分析汇集的结果保存到 京东口红商品数据可视化图结果.html 文件中 tab_nav.render('分析结果/京东口红商品数据可视化图结果.html') # 程序执行入口函数 def main(): # 定义 数据文件名称 file_name = '口红数据' print(f'启动抓取数据!') # 调数理化抓取数据的类 spider_class = CrawlSpider(file_name) spider_class.run() print(f'数据抓取完成!\n') print(f'启动数据可视化程序!') analysis_class = AnalysisData(file_name) analysis_class.run() print(f'数据可视化完成!') if __name__ == '__main__': main()
六.总结
通过本学期的学习,让我了解网络爬虫的基本原理,能够使用Python编写爬虫程序抓取网页内容。熟悉并掌握使用Python请求网页的基本方法,包括使用标准库中的urllib、requests等模块,使用解析库(如Beautiful Soup)分析网页源代码,从中提取有用的信息,使用爬虫框架(如Scrapy)来编写大型的、分布式的爬虫程序。以及了解如何使用代理、Cookies、User-Agent等技术来应对反爬虫措施,如何使用Python处理网络爬取到的数据,包括存储、分析、可视化等。
标签:count,good,Python,口红,self,爬虫,df,prod,data From: https://www.cnblogs.com/zzp-1399564357/p/17001746.html