ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

京东手机销售数据分析系统:从爬虫采集到可视化看板

京东手机销售数据分析系统:从爬虫采集到可视化看板 1. 从“想看看京东手机行情”到完整分析系统我经历了什么先说结论这个项目不是某个大厂的数据中台也不是什么高深的算法模型它就是一个能用、能跑、能出图的京东手机销售数据分析系统。最初的需求特别朴素——年底想换手机想在京东上看看各个价位段的手机到底谁卖得好、谁的评价最真实、哪些型号是“刷单重灾区”。结果越查越深最后干脆用Python做了一套从采集、清洗、分析到可视化的完整流程。如果你也是刚接触Python数据分析不久或者正在找练手项目这个系统的价值在于它不依赖任何付费数据源完全基于京东网页公开的商品信息、销量标记和评论摘要就能构建一个多维度的市场观察视角。标题里的“销售数据分析”听起来很宏大但实际上拆开就四件事爬数据、洗数据、算指标、画图表。很多人一上来就想着用Scrapy框架搭分布式爬虫或者直接上Hadoop那一套我觉得完全没必要。京东手机品类虽然SKU多但单个类目列表页加详情页的规模也就几万条用requests加pandas就完全能扛住。真正的难点不在数据量而在数据质量——手机商品的参数格式五花八门同一个“8GB256GB”的表述可能出现在标题、参数表、广告词三个地方格式还不一样这部分的处理工作占了整个项目的大头。整套系统我划分成了五个模块数据采集、数据清洗、特征工程、分析引擎、可视化报表。每个模块独立运行中间用数据库对接。模块化的好处是哪一块出了问题可以单独修不用整个推倒重来。下面我把每个模块的完整思路和踩过的坑都展开讲你可以直接照着复现。2. 数据从哪儿来采集方案设计以及必须守住的合规边界采集模块是整个系统的基础数据源头如果出了问题后面所有分析都是空中楼阁。我先说清楚我的抓取思路再说合规问题这一点非常关键。2.1 采集目标与页面结构拆解京东手机销售数据分布在两类页面上列表页和详情页。列表页是搜索“手机”后出现的商品列表一页几十个商品包含商品名称、价格、标签比如“自营”“京东超市”、以及累计评价数——注意京东网页版不直接展示销量数字而是用“累计评价”这个数字替代这在行业内是公认的销量近似指标。详情页则是每个商品单独的主页面包含完整的参数规格、促销信息、品牌官方数据、有时还有商品短视频的标题描述。我的采集策略是先抓列表页拿到商品ID和基础价格再用商品ID拼接详情页URL抓取详细参数和评价摘要。这里有个很实用的细节京东商品ID是纯数字详情页URL是https://item.jd.com/{商品ID}.html列表页URL则是https://search.jd.com/Search?keyword手机encutf-8。我建议你直接用keyword手机这个参数而不是去点分类导航因为分类导航页的页面结构更复杂爬取成本更高。2.2 请求头设置与反爬应对京东的反爬策略这几年升级了好几次。早期只需要伪装User-Agent就能畅通无阻现在基本是一套组合拳User-Agent、Cookie、请求频率、访问行为模式缺一不可。最稳妥的方案是模拟真实用户访问序列。我封装了一个请求类逻辑是这样的每个请求之间随机休眠2到5秒不能固定间隔。维护一个User-Agent池每次请求随机抽取一个。请求头带上完整的Referer、Accept-Language、Accept-Encoding。首次请求先访问首页获取Cookie再带着Cookie访问列表页。用这种方式我实测抓取了大约3000个手机商品的基础信息没有被封IP。如果你是第一次上手还可以用更温和的方式直接下载京东商品列表页的HTML文件到本地然后用BeautifulSoup离线解析完全不碰在线请求。这种方式适合用来跑通后面所有的清洗和分析流程不会有任何合规风险。2.3 合规红线什么能抓什么不能抓这里我必须强调写爬虫和做数据分析一样前提是守住边界。京东的商品名称、价格、公开参数这些信息属于商品公开信息用于个人学习研究是合理的。但有四条红线我建议你绝对不要碰不抓用户手机号、收货地址、订单详情等个人隐私数据。不对京东的服务器发起高频并发请求不做影响正常运营的抓取。不把抓取的数据用于商业转售或大规模商业分析。不绕过登录验证去抓取需要身份认证才能查看的内容。在采集模块的代码里我特意加了限速逻辑和异常退出机制就是为了确保整个过程温和且可控。做技术的底气来自自律不是来自破解能力。2.4 增量采集与数据落库数据落库我选的是SQLite原因很简单零配置、单文件、Python内置支持。手机商品数据不过几万条SQLite的读写性能完全够用没必要上MySQL。建表时我是按“商品维度”和“评价维度”分开设计的。商品主表存商品ID、标题、价格、品牌、店铺类型、累计评价数评价明细表存评价文本摘要、评分、评价时间。两张表通过商品ID关联。这样设计的好处是后面做分析时可以根据需要灵活join不用为了某个分析维度提前定制表结构。增量采集的策略是每天跑一次根据商品ID去重只更新价格和累计评价数有变化的商品。这套机制跑了一个月我手上攒了4个时间切片的手机数据集为后面的趋势分析打下了基础。3. 数据清洗的硬骨头手机参数格式混乱的预处理实战采集到的原始数据不能直接用这是我在这套系统里教训最深的一环。原始字段大约有60多个真正能用的不到一半而且每个字段都有独一无二的“脏法”。我花了大概三天时间做清洗和特征工程后面所有分析模型的准确性都建立在这次清洗的基础上。3.1 价格字段的“文字陷阱”京东的价格显示有一个特点商品标题下方显示的是“京东价”但有时候会叠加促销信息比如“满3000减200”“plus会员价”之类的。但我抓的是静态HTML这些动态信息不会出现在源码里价格字段相对干净。真正脏的是价格范围。比如一个商品有多个SKU存储版本不同价格不同列表页可能会显示“¥3999起”或者“¥4599.00”前者就带了文字尾巴。清洗策略是用正则表达式提取数字部分re.findall(r\d\.?\d*, price_str)如果匹配到多个数字就取最小值作为起售价。这里要注意有些价格带千分位逗号要先去掉逗号再做提取。3.2 内存版本的统一从“8GB128GB”到标准数字手机参数表里最难的字段是内存版本。看一下我收集到的几种写法8GB128GB8128G8G运存128G存储8GB 128GB8GB(运行内存)/128GB(机身存储)如果不加清洗这些会被当成完全不同的类别聚成一堆乱七八糟的分组。我的解决方案是两步走第一步分别提取运行内存和存储内存正则匹配(\d)\s*G把第一个匹配结果当作运行内存第二个当作存储内存。 第二步把GB统一转换为GB数值比如“12G”转成12。这样清洗下来所有手机都能落到“运行内存xGB、存储内存yGB”的标准维度上。这一步做完内存对价格的影响建模才能成立。3.3 品牌字段的自动归类品牌字段不是每个商品都规规矩矩地放在参数表里的很多手机标题里就写了品牌但格式不统一“Apple 苹果 iPhone 15”“小米 Xiaomi 14”“vivo iQOO 12”。单靠标题分词容易出错。我的做法是维护一个品牌关键词映射表包含主流手机品牌及其常见别名然后用str.contains()逐一匹配标题和参数表中的品牌字段。匹配不到的归为“其他”。实测下来这个规则方法的F1值比直接跑一次文本聚类还好而且代码只有不到30行。3.4 评论数据的去重与基础清洗评价摘要文本里有大量重复词、广告词、无意义短评。我做了两个清洗动作长度小于10个字符的评论直接丢弃用difflib.SequenceMatcher的相似度阈值0.9去除高度重复的评论。剩下的有效评论文本用于情感分析。清洗模块的代码结构我建议这样组织def clean_price(raw_price): # 去掉千分位逗号提取首个数字 cleaned re.sub(r,, , str(raw_price)) numbers re.findall(r\d\.?\d*, cleaned) return float(numbers[0]) if numbers else None def clean_memory(raw_ram, raw_storage): # 分别提取运行内存和存储内存 ram_match re.search(r(\d)\s*G, str(raw_ram)) storage_match re.search(r(\d)\s*G, str(raw_storage)) ram int(ram_match.group(1)) if ram_match else None storage int(storage_match.group(1)) if storage_match else None return ram, storage def classify_brand(title, param_brand): # 品牌关键词映射匹配 brand_map { 苹果: [apple, 苹果, iphone], 华为: [huawei, 华为, honor], 小米: [xiaomi, 小米, redmi, 红米], vivo: [vivo, iqoo], oppo: [oppo, 一加, oneplus], 荣耀: [honor, 荣耀] } combined_text f{title} {param_brand}.lower() for brand, keywords in brand_map.items(): for kw in keywords: if kw in combined_text: return brand return 其他4. 分析层价格、配置和评价三个维度能挖出哪些结论数据清洗完以后分析工作就变得顺理成章了。我建了一套“价格-配置-评价”的三角分析模型核心问题是京东手机市场的价格带分布如何配置如何影响定价评价数据能反映哪些真实使用感受4.1 价格带分布与品牌段位图把清洗后的价格字段按区间切分我设定了几档1000元以下、1000-2000元、2000-3000元、3000-5000元、5000元以上。用pd.cut()函数实现然后按品牌交叉统计。让我比较意外的结论是在2000-3000元这个价格区间竞争最激烈覆盖的品牌数最多而在5000元以上区间苹果的份额几乎是一骑绝尘安卓阵营里只有华为的旗舰系列能撑住场面。这种“金字塔结构”不是拍脑袋想出来的是真实数据呈现出来的。交叉表分析用一行代码就能出结果price_band pd.cut(df[price], bins[0, 1000, 2000, 3000, 5000, np.inf], labels[千元以下, 1000-2000, 2000-3000, 3000-5000, 5000以上]) brand_price df.groupby([price_band, brand], observedFalse).size().unstack(fill_value0)4.2 配置与价格的回归关系手机配置里最重要的三个连续变量是运行内存、存储内存和电池容量。把它们作为特征对价格做线性回归能直观看到“每增加1GB运行内存价格大约上涨多少”。这里我遇到一个典型陷阱运行内存和价格不是线性关系低端机8GB和12GB的差价不大但高端机的内存升级溢价明显更高。直接跑普通线性回归残差图会呈现喇叭形。我换成了对数变换把价格取log后再回归效果才合理。如果你想复现可以用statsmodels库它会输出完整的回归摘要包括每个特征的系数、标准误和置信区间比自己手算方便很多import statsmodels.api as sm features df[[ram_gb, storage_gb, battery_mah]].fillna(0) features sm.add_constant(features) model sm.OLS(np.log(df[price]), features).fit() print(model.summary())4.3 评价情感分析与“刷单”识别评价文本的情感分析技术上有很多路线从最简单的基于情感词典到BERT微调。我这套系统的定位是轻量级分析所以用了SnowNLP库——它内置了中文情感倾向分析功能调用.sentiments属性就能得到一个0到1的情感得分。清洗后的评论数据我按商品聚合成平均情感分再和累计评价数做个散点图。这里能看出一个有意思的规律某个商品如果累计评价数特别高、但平均情感分显著低于同类大概率是促销跑量型产品评论里真实使用体验占比低反之评价数中等但情感分高的往往是口碑型产品。如果你觉得SnowNLP的准确率不够可以把每个商品的正负评论抽样出来用关键词匹配做二次校验。我在实践中的经验是对于“流畅”“续航好”“拍照清晰”这类高频正向词直接关键词匹配的准确率反而不低而且可解释性更强。4.4 时间切片与促销节点趋势这可能是整套系统里最有“数据分析感”的部分。因为采集模块是增量跑的我积累了一个月内多个时间点的价格快照。把这些快照合并成宽表后就能看每个商品的降价曲线、涨价曲线以及整类目的价格中位数变化。我对比了双十一前的两周和日常周的价格中位数发现大量商品的“促销价”其实是先涨价再打折的结果真正价格平稳的商品反而没参与所谓的大促。这类结论如果不用时间切片数据根本无法验证。你的数据集里如果暂时没有时间维度可以先跳过这个分析等增量采集积累几周后再回头看效果会很好。5. 可视化层用Pyecharts做出能直接汇报的数据看板分析结果最终要向人表达可视化模块承担这个职责。我选择了Pyecharts理由有三个生成的是HTML文件可以在浏览器里直接交互查看图表类型丰富地图、漏斗、词云都有现成组件输出格式干净方便截图放到汇报ppt里。5.1 第一张图品牌价格带热力图热力图非常适合展示“品牌x价格区间”的交叉关系。横轴是价格带纵轴是品牌方块颜色深浅代表商品数量。用Pyecharts的HeatMap组件实现注意数据要转成[x, y, value]的三元组格式。出这这张图我花了最长的时间在做数据透视表的行列排序上。默认排序是按品牌名拼音没法体现数据层级。我用sort_values()按商品总数从高到低排序再把价格带按从低到高排列视觉效果会直观很多。5.2 第二张图内存配置气泡图气泡图用来同时展示三个维度x轴是存储内存y轴是运行内存气泡大小代表商品数量颜色代表平均价格。Pyecharts的Scatter图表加上visualMap颜色映射就能实现。气泡图最适合回答的问题是“哪个配置段是手机厂商的主力出货区”我在图里清晰地看到12GB256GB和12GB512GB是两个密集区域而16GB1TB这种顶配组合气泡明显偏小。这说明厂商发力的重点和消费者的真实选购行为是吻合的。5.3 第三张图评价情感得分排名条形图条形图用于展示不同品牌在“平均情感得分”上的差异。我给情感得分加了一个参考线0.5大于0.5表示正面评价为主小于0.5表示负面评价居多。用Pyecharts画条形图本身不复杂但要注意一点品牌平均值是聚合后的结果同一品牌内部不同价位段手机的情感差异非常大。我建议你在条形图上叠加每个品牌的得分区间用误差棒或者同时画散点否则会掩盖真实情况。我后来改成了“箱线图均值点”的组合信息量丰富了一个层级。5.4 可视化模块的工程化封装每个图表我都封装成了一个独立的函数输入是清洗后的DataFrame输出是HTML文件路径。这样做既方便调试单个图表也方便后续整合到一个汇总页面里。def render_brand_price_heatmap(df, output_path): # 数据透视 price_band pd.cut(df[price], binsprice_bins, labelsprice_labels) pivot df.groupby([price_band, brand], observedFalse).size().unstack(fill_value0) # 构造三元组数据 heat_data [] for j, brand in enumerate(pivot.columns): for i, band in enumerate(pivot.index): heat_data.append([j, i, int(pivot.loc[band, brand])]) # 初始化图表 c HeatMap() c.add(商品数, pivot.columns.tolist(), pivot.index.astype(str).tolist(), heat_data, label_optsopts.LabelOpts(is_showFalse)) c.set_global_opts( title_optsopts.TitleOpts(title京东手机品牌-价格带热力图), visualmap_optsopts.VisualMapOpts(max_100), ) c.render(output_path)6. 部署与运行整个系统的一键启动思路数据分析系统的价值在于可重复运行而不是一次性脚本。我设计了一键启动入口和模块化配置这样每天新增数据后跑一遍就能更新所有报表。6.1 项目目录结构我的目录组织方式如下jd_phone_analysis/ ├── config.py # 全局配置URL常量、数据库路径、价格区间 ├── collector/ # 采集模块 │ ├── fetcher.py # 请求封装 │ ├── parser.py # HTML解析 │ └── storage.py # 数据落库 ├── cleaner/ # 清洗模块 │ ├── price.py # 价格清洗 │ ├── memory.py # 内存版本清洗 │ └── brand.py # 品牌归类 ├── analyzer/ # 分析模块 │ ├── price_analysis.py │ ├── sentiment.py │ └── regression.py ├── visualizer/ # 可视化模块 │ ├── charts.py │ └── dashboard.py └── main.py # 一键入口每个模块的职责单一依赖关系清晰。main.py里用argparse控制运行阶段支持--stage collect、--stage clean、--stage analyze分别执行。6.2 SQLite数据库的读取调优SQLite虽然轻量但读取时还是有几个性能注意事项。一个典型的坑是商品表中的累计评价数是字符串类型直接排序会按字典序排导致“9999”排在“10000”后面。我的解决方案是在建表时就用INTEGER类型存储数字字段或者在SQL查询里加CAST(comment_count AS INTEGER)。另一个建议是给常用查询字段建索引特别是brand和priceCREATE INDEX idx_brand ON products(brand); CREATE INDEX idx_price ON products(price);加了索引后按品牌筛选的速度能提升一个数量级虽然数据量不大但每次跑分析都等几秒钟积累下来体验差距很大。6.3 main.py的一键运行逻辑一键启动的代码非常朴素def main(): parser argparse.ArgumentParser(description京东手机销售数据分析系统) parser.add_argument(--stage, defaultall, choices[collect, clean, analyze, visualize, all]) args parser.parse_args() if args.stage in (collect, all): run_collect() if args.stage in (clean, all): run_clean() if args.stage in (analyze, all): run_analyze() if args.stage in (visualize, all): run_visualize()如果说有什么经验教训那就是不要把每个阶段都直接“一把梭”跑完。数据量大了以后采集阶段耗时长清洗阶段中途失败要重新跑最好分开执行便于定位问题。7. 复盘这套系统最值钱的地方和最值得改进的部分一个月跑下来这套系统带给我最大的收获不是一箩筐图表而是建立了一种“数据驱动观察”的思维方式。市面上所有关于手机销量的文章很多都是厂商通稿和渠道传闻只有自己从数据里挖出来的结论才经得起追问。7.1 最值钱的三件事第一完整的电商数据采集和分析流程经验。从列表页到详情页再到清洗入库这条链路在任何一个电商分析项目中都能复用不只是京东、不只是手机。第二对“销量数据”这个概念的祛魅。京东的“累计评价数”不等于销量但它是目前公开可得的、相对可信的近似指标。理解了数据代理指标和真实指标的关系对后续做任何商业分析都有帮助。第三配置参数对价格的半对数回归模型。这个模型的解释能力非常强输出结果可以直接用来估算“一台手机的成本天花板”和“品牌溢价空间”这对消费决策是有实际参考价值的。7.2 值得改进的部分当前系统最大的短板是情感分析准确率。SnowNLP的预训练模型偏向通用领域对手机评测文本中的专业词汇如“性能调度”“发热控制”“屏幕边框”理解不够。后续计划引入一个基于手机评测语料微调的轻量文本分类模型或者直接用几家大厂的API做跨平台对比校验。另一个改进方向是时间维度的自动化报告。目前的趋势分析需要手动设定对比周期下一步可以做成定时调度任务每周自动生成一份价格波动简报发送到邮箱。这个功能在商业环境里的价值会更高因为价格趋势一旦形成时间序列就能做简单的预测和异常检测。7.3 给新手的实操建议如果你是第一次做这类完整的数据分析项目我的建议是先别追求完美。就用离线的HTML文件写一百行清洗代码画三张图跑通整个流程。等你理解了“脏数据会在哪个环节出现”“图表到底在表达什么关系”之后再回来补采集模块、增量更新、自动化部署都来得及。我自己也是从最原始的Excel手动统计起步的后来才一步步把每个环节工具化。判断一个数据分析项目是否成功的标准从来不是用了多少技术栈而是它能不能持续地回答你真正的疑问。这套京东手机数据分析系统帮我回答了很多实际问题希望你也能从复现它的过程中找到属于自己的分析视角。
返回列表