首页 > 编程语言 >Python网络爬虫—对京东口红销售的数据分析

Python网络爬虫—对京东口红销售的数据分析

时间:2022-12-23 22:22:50浏览次数:47  
标签:count good Python 口红 self 爬虫 df prod data

一、选题的背景

 对电商来说,抓取某些数据,再进行分析,可以有效地反映出数据在某个区间内变化情况。数据受某些因素而发生巨大的影响,也可以借助分析的数据来规划相关项目的后续发展。因此,如果能利用网页爬取数据技术获取数据并对各种数据进行统计分析,对后续电商的发展具有指导意义。所以本次通过对京东上口红的信息进行爬取和分析,以获取有关口红市场趋势、销售情况、品牌排名、价格分布等信息,为进行市场调研、产品开发、销售策略制定等活动提供依据。

二、主题式网络爬虫设计方案

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

相关文章

  • 快速排序 python
    步骤在一序列中定一个轴为基轴(通常为了方便定最左那个数),定序列左右指针,右指针开始扫描,比基轴大则指针继续往前扫,当扫到比基轴小时,把这个数放到最左边,再开始扫左......
  • 力扣26(java&python)-删除有序数组中的重复项(简单)
    题目:给你一个升序排列的数组nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持一致。由于在某些语言中......
  • python归并排序
    采用了分治法,把序列不断的等分序列,最后分成一个之后,再把它两两合并叠加起来,利用了扑克牌两个正序序列进行排序合并时间复杂度nlogn代码defmerge_sort(lists):if......
  • 初学python
    本章内容:Python的种类Python的环境Python入门(解释器、编码、pyc文件、脚步传入参数、变量、输入、流程控制与缩进、while循环)练习题Python的种类Cpyt......
  • python:文件操作:文件的读取
               ......
  • python:了解异常
    当检测到一个错误时,Python解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”,也就是我们常说的BUGbug单词的诞生早期计算机采用大量继电器工作,马克......
  • python:异常的捕获方法
    世界上没有完美的程序,任何程序在运行的过程中,都有可能出现:异常,也就是出现bug导致程序无法完美运行下去。我们要做的,不是力求程序完美运行。而是在力所能及的范围内,对可......
  • python:模块
    Python模块(Module),是一个Python文件,以.py结尾.模块能定义函数,类和变量,模块里也能包含可执行的代码.模块的作用:python中有很多各种不同的模块,每一个模块都可以......
  • python:包
           ......
  • python:第三方包
           ......