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

一、项目背景

最近又及年底,各类分析与规划报告纷至沓来,于是接到了公司平台类目商品增长方向的分析需求,其中需要结合外部电商平台做对比。我选择了国内某电商平台作为比较对象,通过获取最细层级前台类目下的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]# 类别的URLcategory_url = categorys[0]# 获取 category_url 中 `-` 个数count = category_url.count('-')if category_url.count('xx.com') != 0:# 其他就是本身就是URL, 前面补一个协议头category_url = 'https://' + category_urlelif count == 1:# 如果包含一个 '-' 是二级分类的频道category_url = 'https://channel.xx.com/{}.html'.format(category_url)else:# 如果包含2个 '-' 是三级分类的列表# 1. 把 `-` 替换为 ','category_url = category_url.replace('-', ',')# 2. 生成具体列表的URLcategory_url = 'https://list.xx.com/list.html?cat={}'.format(category_url)return category_name, category_urlresult = pd.DataFrame()
df = dict()
# 遍历分类列表
for category in categorys:# 获取大分类,包含子分类; 注: 第一层的分类都在在0索引上;b_category = category['s'][0]# 获取大分类信息(分类URL,名称)b_category_info =  b_category['n']# 解析大分类信息, 获取大分类名称和URLdf['大分类名'], 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').Tresult = 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).textselector = 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) * 10000elif a == '异常':return 0else: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 = 10value.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 npdf1 = 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.rhkb.cn/news/238290.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java网络爬虫--HttpClient

目录标题 技术介绍有什么优点?怎么在项目中引入? 请求URLEntityUtils 类GET请求带参数的GET请求POST请求 总结 技术介绍 HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、功能丰富的、支持 HTTP 协议的客户端编程工具包。相…

八分钟了解一致性算法 -- Raft算法

前言 #### 分布式一致性在分布式环境中,一致性是指数据在多个副本之间是否能够保持一致的特性。 #### 分布式一致性算法比较常见的一致性算法包括Paxos算法,Raft算法,ZAB算法等• Paxos是Leslie Lamport提出的一种基于消息传递的分布式一致性算法。很多分布式一致性算法都由…

机器学习根据金标准标记数据-九五小庞

根据金标准标记数据是一种在机器学习和数据科学中常见的操作,主要用于评估分类模型的性能。其基本步骤如下: 收集数据:首先需要收集相关领域的原始数据,这些数据通常来自不同的来源和渠道。数据清洗和预处理:在这一步…

什么是Modbus协议?

Modbus协议是一种在工业自动化领域广泛应用的通信协议,它允许不同设备之间进行可靠的数据交换和控制。该协议最初由Modicon公司于1979年创建,旨在提供一种简单而有效的方法,使PLC(可编程逻辑控制器)和其他自动化设备能…

四搭建dockerhub私有仓库

搭建dockerhub私有仓库 很多场景下,我们需使用私有仓库管理Docker镜像。相比Docker Hub,私有仓库有以下优势: 节省带宽,对于私有仓库中已有的镜像,无需从Docker Hub下载,只需从私有仓库中下载即可&#x…

Vue学习笔记五--路由

1、什么是路由 2、VueRouter 2、1VueRouter介绍 2、2使用步骤 2、3路由封装 3、router-link 3.1两个类名 3.2声明式导航传参 4、路由重定向、404 当找不到路由时,跳转配置到404页面 5、路由模式 6、通过代码跳转路由---编程式导航&传参 路由跳转时传参 跳转方式…

【漏洞复现】大华 DSS 数字监控系统 itcBulletin SQL 注入

漏洞描述 大华 DSS存在SQL注入漏洞,攻击者 pota/services/itcBuletin 路由发送特殊构造的数据包,利用报错注入获取数据库敏感信息。攻击者除了可以利用 SQL注入漏词获取数据库中的信息例如,管理员后台密码、站点的用户人人信息)之外,甚至在高权限的情况可向服务器中写入木…

【Python数据可视化】matplotlib之绘制高级图形:散点图、热力图、等值线图、极坐标图

文章传送门 Python 数据可视化matplotlib之绘制常用图形:折线图、柱状图(条形图)、饼图和直方图matplotlib之设置坐标:添加坐标轴名字、设置坐标范围、设置主次刻度、坐标轴文字旋转并标出坐标值matplotlib之增加图形内容&#x…

嘴尚绝卤味:健康卤味风潮来袭,引领卤味市场新变革!

随着生活水平的提高,人们对食品的需求已不再满足于基本的口感和饱腹,健康、营养成为越来越多人关注的焦点。在这种背景下,健康卤味理念应运而生,并迅速在卤味市场引发了一场深刻的变革。 健康卤味理念强调选用优质、健康的食材&am…

SpringBoot3 WebFlux 可观测最佳实践

前言 链路追踪是可观测性软件系统的一个非常好的工具。它使开发人员能够了解应用程序中和应用程序之间不同交互发生的时间、地点和方式。同时让观测复杂的软件系统变得更加容易。 从Spring Boot 3开始,Spring Boot 中用于链路追踪的旧 Spring Cloud Sleuth 解决方…

反射助你无痛使用Semantic Kernel接入离线大模型

本文主要介绍如何使用 llama 的 server 部署离线大模型,并通过反射技术修改 Semantic Kernel 的 OpenAIClient 类,从而实现指定端点的功能。最后也推荐了一些学习 Semantic Kernel 的资料,希望能对你有所帮助。 封面图片: Dalle3 …

docker/华为云cce 部署nacos 2.3.0 集群模式

镜像地址 https://hub.docker.com/r/nacos/nacos-server 版本 nacos/nacos-server:v2.3.0-slim 关键环境变量 使用mysql数据源 变量值备注MODEcluster启用集群模式MYSQL_SERVICE_DB_NAME数据库名MYSQL_SERVICE_USER数据库用户名MYSQL_SERVICE_PASSWORD数据库密码SPRING_D…

WPF 布局

了解 WPF中所有布局如下,我们一一尝试实现,本文档主要以图形化的形式展示每个布局的功能。 布局: Border、 BulletDecorator、 Canvas、 DockPanel、 Expander、 Grid、 GridView、 GridSplitter、 GroupBox、 Panel、 ResizeGrip、 Separat…

API设计:从基础到最佳实践

1*vWvkkgG6uvgmJT8GkId98A.png 在这次深入探讨中,我们将深入了解API设计,从基础知识开始,逐步进阶到定义出色API的最佳实践。 作为开发者,你可能对许多这些概念很熟悉,但我将提供详细的解释,以加深你的理解…

13、Redis高频面试题

1、项目中为什么用Redis 我们项目中之所以选择Redis,主要是因为Redis有下面这些优点: 操作速度快:Redis的数据都保存在内存中,相比于其它硬盘类的存储,速度要快很多数据类型丰富:Redis支持 string&#x…

【题解】—— 每日一道题目栏

2024.1 【题解】—— LeetCode一周小结1 1. 1599. 经营摩天轮的最大利润 2. 466. 统计重复个数 3. 2487. 从链表中移除节点 4. 2397. 被列覆盖的最多行数 5. 1944. 队列中可以看到的人数 6. 2807. 在链表中插入最大公约数 7. 383. 赎金信 【题解】—— LeetCode一周小…

易安联参与制定的《面向云计算的零信任体系》行业标准即将实施

中华人民共和国工业和信息化部公告2023年第38号文件正式发布行业标准:YD/T 4598.2-2023《面向云计算的零信任体系 第2部分:关键能力要求》及YD/T 4598.3-2023《面向云计算的零信任体系 第3部分:安全访问服务边缘能力要求》,并于20…

代码随想录 Leetcode242. 有效的字母异位词

题目&#xff1a; 代码&#xff08;首刷看解析 2024年1月14日&#xff09;&#xff1a; class Solution { public:bool isAnagram(string s, string t) {int hash[26] {0};for(int i 0; i < s.size(); i) {hash[s[i] - a];}for(int i 0; i < t.size(); i) {hash[t[i]…

【零基础入门Python数据分析】Anaconda3 JupyterNotebookseaborn版

目录 一、安装环境 python介绍 anaconda介绍 jupyter notebook介绍 anaconda3 环境安装 解决JuPyter500&#xff1a;Internal Server Error问题-CSDN博客 Jupyter notebook快捷键操作大全 二、Python基础入门 数据类型与变量 数据类型 变量及赋值 布尔类型与逻辑运算…

【2024】OAK智能深度相机校准教程

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是Ash…