实战 | 某电商平台类目SKU数获取与可视化展示

news2024/11/17 4:34:17

一、项目背景

最近又及年底,各类分析与规划报告纷至沓来,于是接到了公司平台类目商品增长方向的分析需求,其中需要结合外部电商平台做对比。我选择了国内某电商平台作为比较对象,通过获取最细层级前台类目下的SKU数以及结构占比,找出差异和可提升方向。

我的初步思路是:通过爬虫获取类目名称和链接——>获取SKU数——>可视化展现

由于这个项目并不需要对商品信息和用户评论信息进行获取,难度比较低,不会遇到强力的反爬机制,因此可以用来日常练手,尤其是对于我这种退出爬虫界很久的同学来说是比较友好,毕竟谁都不想去踩缝纫机对不对(手动狗头)。

二、实现过程

(一)三层级类目及链接获取

下图是该电商平台前台展示的三层级类目。

图片

1. 通过 f12 进入 JS 抓包

可以找到类目的真实地址:「https://dc.3.cn/category/get」,幸运的是返回的数据是 JSON 格式的,这样处理起来就简单了。

图片

图片

2. 通过观察返回的数据,可以发现一定的规律。

写爬虫就是这样,不断地找规律,仔细核对返回的数据,斗智斗勇的同时会觉得很有挑战乐趣,但也会觉得挺麻烦的。

图片

  • 分类信息格式

    • 格式1:

        • 1318-2628-12131|户外风衣||0

        • 对应URL: https://list.jd.com/list.html?cat=1318,2628,12131

        • 特点: 第一项为分类ID, 包含两个 - 

    • 格式2:

      • 652-654|摄影摄像||0

      • 对应的URL: https://channel.jd.com/652-654.html

      • 特点:第一项是频道ID, 包含一个 -

    • 格式3:

      • jiadian.jd.com|家用电器||0

      • 特点: 第一项分类URL,第二项分类名称

3. 代码实现

import requests
import json
import pandas as pd
import warnings
warnings.filterwarnings('ignore')

headers={
        'Content-Type':'application/json',
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
        }

url = 'https://dc.3.cn/category/get'
res = requests.get(url,headers=headers)
# 把传递过来的信息GBK进行解码
res.encoding='GBK'
json_data=json.loads(res.text)
# 取出"data" 键中分类列表
categorys = json_data['data']


def get_category_item(category_info):
    # 使用 `|` 分割类型信息字符串
    categorys =   category_info.split('|')
    # 类别的名称
    category_name = categorys[1]
    # 类别的URL
    category_url = categorys[0]
    # 获取 category_url 中 `-` 个数
    count = category_url.count('-')

    if category_url.count('xx.com') != 0:
        # 其他就是本身就是URL, 前面补一个协议头
        category_url = 'https://' + category_url
    elif count == 1:
        # 如果包含一个 '-' 是二级分类的频道
        category_url = 'https://channel.xx.com/{}.html'.format(category_url)
    else:
        # 如果包含2个 '-' 是三级分类的列表
        # 1. 把 `-` 替换为 ','
        category_url = category_url.replace('-', ',')
        # 2. 生成具体列表的URL
        category_url = 'https://list.xx.com/list.html?cat={}'.format(category_url)
    return category_name, category_url


result = pd.DataFrame()
df = dict()
# 遍历分类列表
for category in categorys:
    # 获取大分类,包含子分类; 注: 第一层的分类都在在0索引上;
    b_category = category['s'][0]
    # 获取大分类信息(分类URL,名称)
    b_category_info =  b_category['n']
    # 解析大分类信息, 获取大分类名称和URL
    df['大分类名'], df['大分类链接'] = get_category_item(b_category_info)

    # 获取中分类列表
    m_category_s =  b_category['s']

    # 遍历第二层分类列表
    for m_category in m_category_s:
        # 获取中分类信息
        m_category_info = m_category['n']
        df['中分类名'], df['中分类链接'] = get_category_item(m_category_info)
        # 获取小分类列表
        s_category_s = m_category['s']
        # 遍历小分类分类列表
        for s_category in s_category_s:
            # 获取第三层分类名称
            s_category_info = s_category['n']
            # 获取三级分类信息
            df['小分类名'], df['小分类链接'] = get_category_item(s_category_info)
            print('{} 已爬取……'.format(df['小分类名']))
            table = pd.DataFrame.from_dict(df,orient='index').T
            result = pd.concat([result, table])
result.to_excel('./2. 输出类目表.xlsx',sheet_name='result',index=False)
print('爬取成功!!')

(二)小分类下SKU数获取

进入任一级页面,这个平台非常人性化,已经把大致的SKU数放在了页面上,只要通过 xpath 就能直接提取的到啦,轻松写意,直接放代码吧。

图片

图片

import requests
from lxml import etree
import pandas as pd
import time
from alive_progress import alive_bar
import warnings
warnings.filterwarnings('ignore')

headers={
        'Content-Type':'application/json',
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
        }

df = pd.read_excel('./2. 输出类目表.xlsx',sheet_name='result')
datas=[]
urls = df['小分类链接']
with alive_bar(len(urls)) as bar:   
    for url in urls:
        res = requests.get(url,headers=headers).text
        selector = etree.HTML(res)
        try:
            sku_count = selector.xpath('//*[@id="J_resCount"]/text()')[0]
        except IndexError:
            sku_count = '异常'
        data = {
                'url':url,
                'sku_count': sku_count.strip()
                }
        
        with open('SKU.txt','a') as f:
            f.write(str(data))
            
        datas.append(data)
        print(data)
    
df_SKU = pd.DataFrame(datas)
df_result = pd.merge(df,df_SKU,left_on='小分类链接',right_on='url',how='inner')
df_result.to_excel('./4. 输出类目SKU原始数据.xlsx',sheet_name='result',index=False)
print('SKU数 爬取完成!!')

(三) 数据清洗

数据拼接完成后,需要对SKU数字段做一些处理。

  • 爬取后原始格式

    • 格式1:

      • 以“万”为结尾

      • 需要在原始数据上,去除“+”符号,乘以 10000

    • 格式2:

      • 小分类页面不是商品页,而是返回广告页,没有提供商品SKU数

      • 处理成 0

    • 格式3:

      • 正常数据

      • 需要在原始数据上,去除“+”符号

import pandas as pd
import xlwings as xw
import warnings
warnings.filterwarnings('ignore')

df = pd.read_excel('./4. 输出类目SKU原始数据.xlsx',sheet_name='result')

def transform(a,b):
    if a == '万':
        return float(b) * 10000
    elif a == '异常':
        return 0
    else:
        return float(b)

df['基数'] = df['SKU数'].str.findall('[0-9.]').str.join('')
df['单位'] = df['SKU数'].str.findall('[\u4e00-\u9fa5 ;()]').str.join('')
df['转换后SKU数'] = df.apply(lambda x :transform(x['单位'],x['基数']), axis=1)
df = df[['大分类名', '大分类链接', '中分类名', '中分类链接', '小分类名', '小分类链接','转换后SKU数']]
df.to_excel('./6. 输出类目SKU转换后数据.xlsx',sheet_name='result',index=False)

app = xw.App(visible=False,add_book=False)
workbook = app.books.open('./6. 输出类目SKU转换后数据.xlsx')

for i in workbook.sheets:
    value = i.range('A1').expand() # 选择要调整的区域
    value.rows.autofit() # 调整列宽字符宽度
    value.columns.autofit()  # 调整行高字符宽度
    value.api.Font.Name = '微软雅黑' # 设置字体
    value.api.Font.Size = 9 # 设置字号大小(磅数)
    value.api.VerticalAlignment = xw.constants.VAlign.xlVAlignCenter # 设置垂直居中
    value.api.HorizontalAlignment = xw.constants.HAlign.xlHAlignCenter # 设置水平居中
    for cell in value:
        for b in range(7,12):
            cell.api.Borders(b).LineStyle = 1 # 设置单元格边框线型
            cell.api.Borders(b).Weight = 2 # 设置单元格边框粗细
    value = i.range('A1').expand('right')  # 选择要调整的区域
    value.api.Font.Size = 10
    value.api.Font.Bold = True  # 设置为粗体
workbook.save()
workbook.close()
app.quit()

print('数据清洗完成!!')

(四)可视化展现

可视化展示的环节,我这次没有选用之前一直使用的 pyecharts,而是使用了 plotly。

主要原因是 plotly 对于 pandas 的支持非常好,它的高级封装函数的写法非常简洁,使用起来方便,而且也能够支持交互和自定义颜色,集美观与实用于一身,应该会成为我今后的主力可视化工具。

1. 将某平台和我司的类目SKU数占比进行对比

图片

import plotly.io as pio
import plotly.express as px
import plotly.graph_objects as go
import plotly.figure_factory as ff
import pandas as pd
import numpy as np


df1 = pd.read_excel('./6. 输出类目SKU转换后数据.xlsx',sheet_name='result')

df_xx = df1.groupby('映射我司事业部')['转换后SKU数'].sum().reset_index().sort_values(by='转换后SKU数',ascending=False)
df_xx['SKU数占比%'] = ((df_xx['转换后SKU数'] / df_xx['转换后SKU数'].sum()) * 100).round(1) 
df_xx['公司'] = 'xx'
df_xx = df_JD[['公司','映射我司事业部','转换后SKU数','SKU数占比%']]
df_xx.loc[len(df_xx.index)] = ['xx', '商城商品事业部', 0, 0.0]

df2 = pd.read_excel('./【资料】2022年购物公司商品0101-1013.xlsx',sheet_name='Sheet1')
df2 = df2[df2['订购数量']>0]

df_yy = df2.groupby('事业部')['商品编号'].count().reset_index().sort_values(by='商品编号',ascending=False)
df_yy['SKU数占比%'] = ((df_yy['商品编号'] / df_yy['商品编号'].sum()) * 100).round(1) 
df_yy.rename(columns={'事业部':'映射我司事业部', '商品编号':'转换后SKU数'}, inplace = True)
df_yy['公司'] = 'yy'
df_yy = df_yy[['公司','映射我司事业部','转换后SKU数','SKU数占比%']]
df_yy.loc[len(df_yy.index)] = ['yy', 0, 0, 0.0]
df_yy.loc[len(df_yy.index)] = ['yy', '团购', 0, 0.0]

df_concat = pd.concat([df_xx,df_yy])

# SKU类目占比对比(柱状图)
fig = px.bar(df_concat, x='映射我司事业部', y='SKU数占比%',barmode='group',color='公司',text='SKU数占比%')
fig.update_layout(title='事业部SKU占比对比(%)')
fig.update_traces(textposition='outside',textfont_size=16,textfont_color=['#FC5531'])
pio.write_html(fig,'事业部SKU占比对比.html')
pio.write_image(fig,'事业部SKU占比对比.png','png',width=1400,height=800)

2. 某平台类目SKU数量结构

图片

# 树状图
df1['整体'] = '整体'
fig1 = px.treemap(df1, 
                 path=['整体', '大分类名', '中分类名'], 
                 values='转换后SKU数', 
                 title='类目SKU占比树状图',
                 # color='转换后SKU数',
                 # color_continuous_scale='RdBu',
                 # color_continuous_midpoint=df1['转换后SKU数'].mean()
                )
fig1.update_traces(textinfo='label+value',textfont = dict(size = 20))                                                                                 
pio.write_html(fig1,'类目SKU占比树状图.html')
pio.write_image(fig1,'类目SKU占比树状图.png','png',width=1400,height=800)

3. 某平台

图片

#  热力图
bins = [0,1,20000,50000,100000,150000,200000,300000,400000,500000,99999999999]
groups1 = ['0','2万','5万','10万','15万','20万','30万','40万','50万','50万以上']
groups2 = [.1,.2,.3,.4,.5,.6,.7,.8,.9,1.0]
df1['SKU数级别'] = pd.cut(df1['转换后SKU数'],bins,labels=groups1)
df1['SKU数级别'] = df1['SKU数级别'].fillna('0')

data = df1.groupby(['大分类名','SKU数级别'])['转换后SKU数'].sum().reset_index()
data = pd.pivot(data,values='转换后SKU数',index='大分类名',columns='SKU数级别')

data2 = data.apply(lambda x:pd.cut(x,bins,labels=groups2))
data2 = data2.fillna(.1)

data = data.applymap(lambda x:str(round(x / 10000,2)) + ' 万')

data.drop(index='众筹',columns='0',inplace=True)
data2.drop(index='众筹',columns='0',inplace=True)

x = list(data.columns)
y = list(data.index)
z = data2.values.tolist()
z_text = data.fillna('').values.tolist()

#  自定义色卡
# colorscale = [[0.0,'rgb(0,153,102)'],
#               [.1,'rgb(211,207,99)'],
#               [.3,'rgb(255,153,51)'],
#               [.4,'rgb(204,97,51)'],
#               [.5,'rgb(102,0,153)'],
#               [1.0,'rgb(126,0,35)']]

fig2 = ff.create_annotated_heatmap(z,
                                   x=x,
                                   y=y,
                                   annotation_text=z_text,
                                   # colorscale=colorscale
                                   )
fig2.update_layout(title='类目SKU占比热力图')
fig2.update_xaxes(side='top')
pio.write_html(fig2,'类目SKU占比热力图.html')
pio.write_image(fig2,'类目SKU占比热力图.png','png',width=1400,height=800)

三、可提升方向

以上只是实际工作项目中的一部分,接下来还要对自己公司的数据进行分析,不方便给出更详细的说明,但是本文使用的方法是相通的,不管是对自己公司还是外部平台,都可以按照类似的步骤进行处理、分析与展示。

进行项目的过程中还有一些值得提升的地方,

  • plotly.express 尚未支持多子图的呈现,目前只能使用 plotly.graph_objs 来实现,代码较为繁琐

  • plotly 的很多配置项细节需要梳理和掌握,毕竟才真正接触这个库两三天的时间,来日方长

  • 遇到反爬之后,反反爬的成本很高,影响效率,在不花钱的情况下,现在爬虫的 ROI 已经很低,不太值得去做,以我现在的水平有越来越多的网站过不了

  • 遇到海量不同口径的数据(比如类目),有什么样的方法能够快速对齐统一,目前还没有头绪,靠人工肯定不现实,数据清洗是真的让人头大啊

数海随记

喜欢作者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1385412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费分享一套PyQt6图书管理系统(附带完整开发视频教程) Python入门项目实战,果断收藏了~~

大家好,我是python222_小锋老师,最近写了一套PyQt6图书管理系统源码,附带完整开发视频教程,作为Python学习者的入门实战项目,带大家一起入门学习Python技术,感谢大家支持,特来分享下哈。 项目实…

java小游戏——动漫美女拼图

1:继承 1.1 继承概述 首先,我们来说一下,什么是继承: 继承是面向对象三大特征之一(封装,继承和多态) 可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法 也就是说&…

生日视频模板-试试这样制作

视频制作已经成为表达情感、记录生活的重要方式。尤其在生日这样的特殊日子,一份个性化的视频祝福不仅能让人感到温馨,还能成为长久珍藏的回忆。那么,如何快速制作出精美的生日模版视频呢?下面就给大家介绍几种可以制作生日模版的…

论文阅读:Attention is all you need

【最近课堂上Transformer之前的DL基础知识储备差不多了,但学校里一般讲到Transformer课程也接近了尾声;之前参与的一些科研打杂训练了我阅读论文的能力和阅读源码的能力,也让我有能力有兴趣对最最源头的论文一探究竟;我最近也想按…

300块成本从零开始搭建自己的家庭版NAS还可以自动备份,懂点代码有手就行!

前言 300块成本从零开始搭建自己的家庭版NAS,还可以手机上文件照片音乐自动备份,完全实现了自己的网盘效果,可以设置用户权限分配,目录上传、断点续传、并行上传、拖拽文件上传等日常操作。 为什么要搭建NAS? 现在的手…

【数据库】间隙锁Gap Lock

什么是间隙锁 间隙锁(Gap Lock):间隙锁是(RR级别下)一个在索引记录之间的间隙上的锁,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。间隙锁(Gap Lo…

失踪人员信息发布与管理系统:计算机毕设课题的研究与实践 springboot+java+vue+mysql

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

Cesium 模型压平

最近整理了下手上的代码,以下是对模型压平的说明。 原理是使用了customShader来重新设置了模型的着色器,通过修改模型顶点的坐标来实现了压平。 废话不多说,下面上代码: /*** class* description 3dtiles模型压平*/ class Flat…

docker-ce 安装与国内源配置 | Ubuntu 20.04

博客原文 文章目录 让apt可以支持HTTPS将官方Docker库的GPG公钥添加到系统中将Docker库添加到APT里更新包列表为了确保修改生效,让新的安装从Docker库里获取,而不是从Ubuntu自己的库里获取,执行:安装 docker-ce配置 docker 阿里源…

Java多线程并发篇----第十四篇

系列文章目录 文章目录 系列文章目录前言一、ReadWriteLock 读写锁二、共享锁和独占锁三、重量级锁(Mutex Lock)四、轻量级锁前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给…

【图解数据结构】深入剖析时间复杂度与空间复杂度的奥秘

🌈个人主页:聆风吟 🔥系列专栏:图解数据结构、算法模板 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 一. ⛳️算法的定义二. ⛳️算法的特性2.1 🔔输入输出2.2 🔔输入输出2.3 &…

Multimodal Prototypical Networks for Few-shot Learning

tcGAN is provided with an embedding ϕ T \phi_T ϕT​() of the textual description 辅助信息 作者未提供代码

大模型中的显卡优化与分布式训练策略

目录 前言1 大模型训练优化的目标1.1 简单性1.2. 高效性1.3 廉价性 2 显存的组成与利用2.1 参数存储2.2 梯度计算与存储2.3 中间计算结果2.4 优化器信息 3 优化方法3.1 数据并行3.2 模型并行3.3 零冗余优化器(Zero Redundancy Optimizer)3.4 Pipeline并行…

前端数据魔法:解析数据透视功能实现

前言 在信息爆炸的时代,数据扮演着关键的角色。从庞大的数据中提取有用的信息并进行有效地分析,是一项充满挑战的任务。为了应对这个挑战,数据透视表这一工具应运而生。它通过重新排列和组合数据,使得原始数据更易于理解和分析。…

解决鸿蒙APP的内存泄漏

解决鸿蒙(HarmonyOS)应用的内存泄漏问题需要采用一系列的策略和技术。与解决Android内存泄漏类似,以下是一些建议,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1…

增强FAQ搜索引擎:发挥Elasticsearch中KNN的威力

英文原文地址:https://medium.com/nerd-for-tech/enhancing-faq-search-engines-harnessing-the-power-of-knn-in-elasticsearch-76076f670580 增强FAQ搜索引擎:发挥Elasticsearch中KNN的威力 2023 年 10 月 21 日 在一个快速准确的信息检索至关重要的…

收银系统源码,连锁店收银系统源码

智慧新零售系统是一套线下线上一体化的收银系统。致力于给零售门店提供『多样化线下收银』、『ERP进销存』、『o2o小程序商城』、『精细化会员管理』、『丰富营销插件』等一体化行业解决方案! 一、多样化线下收银 1.聚合收款码 ①适用商户:小微门店&am…

在 .NET 中使用可以漫游的 Web 凭据

Windows 凭据管理器是一个内置在 Windows 操作系统中的功能,为用户提供一种安全的方式来存储和管理凭据。本文主要介绍如何在 .NET 中使用可以漫游的 Web 凭据,以及使用中的基本事项。 1. 引言 在前面的文章《试用 Windows Terminal 中的 Terminal Chat…

SwiftUI CoreData Picker

开发多账本功能 CoreData 与 Picker 的使用 上代码: // // TestZhangBenPicker.swift // pandabill // // Created by 朱洪苇 on 2024/1/14. //import SwiftUIstruct TestZhangBenPicker: View {FetchRequest(sortDescriptors: [SortDescriptor(\.cc_at)],anima…

人工智能与六西格玛设计:一场颠覆性的融合之旅

随着科技的飞速发展,人工智能(AI)和六西格玛设计(Six Sigma)已成为当今企业追求卓越的关键工具。当这两大领域相遇,它们将引发一场创新与变革的狂潮。本文将探讨AI与六西格玛设计结合的潜力,以及…