ARTICLE DETAIL

资讯详情

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

Python数据分析实战:京东手机数据采集清洗可视化全流程

Python数据分析实战:京东手机数据采集清洗可视化全流程 想用Python做数据分析练手很多人第一反应就是拿电商平台开刀。京东手机这个品类是我个人认为最适合新手当作分析系统的切入场景SKU足够多、价格梯度清晰、品牌格局稳定、评价数据也丰富一套流程跑下来基本能把Python数据分析的常见环节全过一遍。这篇文章我就把整个系统的落地过程拆开讲清楚从数据怎么来、怎么洗到怎么算、怎么画再到过程中踩过的坑全部按实际开发顺序摊开说适合有一定Python基础、想完整走一遍数据分析项目的人参考。1. 项目概述与目标拆解1.1 为什么选择京东手机这个场景做数据分析系统最难的不是写代码是选数据源。选错了数据源后面所有环节都会卡住。京东手机这个场景有三个优势几乎是为练手量身定做的。第一数据维度完整。手机商品的详情页、评价区、价格曲线、品牌专营店的店铺信息加在一起能覆盖“价格、销量、口碑、品牌”这四个最典型的分析维度不会出现想分析评价却没有评价数据这种尴尬。第二品类结构稳定。手机不像服装那样款式极度过剩也不是生鲜那种价格剧烈波动的品类。它的品牌梯队、价位带分布都比较清晰做出来分析结论有可解释性不会算出一堆“不知道为什么”的数字。第三数据量适中。单页30个商品、几十页左右的样本量对个人电脑的内存和处理速度非常友好不需要上分布式计算一台普通笔记本就能跑完整个流程。说白了这个项目练的不只是爬虫而是把“采集—清洗—分析—可视化”这个完整链路打通的能力。这套方法论学会之后换成别的平台、别的品类只需要改字段映射和分析维度大体框架直接复用。1.2 系统整体架构与模块划分整个系统我按职责拆成四层层与层之间通过CSV文件或DataFrame传递数据不搞复杂的消息队列避免过度设计。采集层负责从京东商品列表页、详情页获取商品名称、价格、店铺、评价数、好评率等原始字段。这一层是最容易出问题的也是耗时最长的部分。清洗层负责把采集到的脏数据整理成规范格式包括字段去重、缺失值处理、价格字符串转数值、品牌信息提取等。分析层负责核心指标计算包括价格区间分布、销量区间分布、品牌集中度、评价口碑对比等。展示层负责把分析结果制作成图表和简易报告。优先使用Pyecharts生成HTML页面方便直接打开查看。开发环境方面我使用的是Python 3.9版本IDE用的PyCharm。如果电脑上还没装好Python建议先完成基础环境配置再跑下面的代码避免把时间浪费在环境问题上。这个分层设计的核心原则是“每层只干一件事”。比如采集层不需要关心数据怎么分析展示层不需要关心数据从哪来这样任何一个环节出问题都能快速定位。2. 数据采集层拿到第一手手机销售数据2.1 采集方案的选型思路拿到需求之后先别急着写代码。我当时对比了三套方案直接调京东官方API、用Selenium渲染浏览器、用Requests模拟HTTP请求。官方API这条路最正规但京东开放平台对个人开发者门槛比较高需要企业资质个人项目基本走不通。Selenium方案能完美绕过大多数反爬限制因为它的行为就是真实用户在操作浏览器但缺点是速度慢、资源占用高抓几十页数据要跑很久。Requests方案速度最快、代码最简洁但需要手动处理UAUser-Agent伪造、Cookie、请求频率限制等反爬策略。最终的选型是用Requests作为主力配合一套可控的请求头伪装和延时策略。这样既保证了速度又不会因为请求过快触发风控。这种方案适合中小体量的采集任务也是目前个人数据分析项目里最常见的做法。2.2 核心采集流程与关键代码采集流程大体上分三步。第一步先访问商品列表页拿到当前页面的商品ID列表第二步根据商品ID访问详情页提取完整字段第三步翻页循环直到采集到预设的商品数量。核心代码如下import requests import time import random from bs4 import BeautifulSoup import pandas as pd HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Referer: https://www.jd.com/ } def fetch_page(page_num): url fhttps://search.jd.com/Search?keyword手机page{page_num * 2 - 1} resp requests.get(url, headersHEADERS, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, lxml) items [] for li in soup.select(.gl-item): try: item_id li.get(data-sku) name li.select_one(.p-name a em).text.strip() price li.select_one(.p-price i).text.strip() comment li.select_one(.p-commit a).text.strip() shop li.select_one(.p-shop a).text.strip() if li.select_one(.p-shop a) else items.append([item_id, name, price, comment, shop]) except Exception as e: print(f解析商品失败: {e}) continue return items def collect_data(page_start, page_end): all_data [] for page in range(page_start, page_end 1): print(f正在采集第 {page} 页...) page_data fetch_page(page) all_data.extend(page_data) time.sleep(random.uniform(1.5, 3.5)) df pd.DataFrame(all_data, columns[商品ID, 名称, 价格, 评价数, 店铺]) df.to_csv(jd_phone_raw.csv, indexFalse, encodingutf-8-sig) print(f采集完成共 {len(df)} 条记录)这段代码里有几个细节值得说明。首先京东搜索页的翻页规则是页码参数为一个奇数序列需要在请求时做换算。其次每个商品项的结构都包在.gl-item这个CSS类下面所以直接select这个类就能遍历到全部商品。采集频率控制是最关键的一环。我在每页之间加了一个随机延时范围在1.5秒到3.5秒之间这样做的目的是让请求间隔没有规律降低被识别为爬虫的概率。实测下来连续采集20页左右比较安全如果一次性拉太猛很容易触发滑块验证。2.3 反爬应对与合规边界京东的反爬策略在各大平台里属于中等偏上的水平新手最容易踩的坑是请求头不完整。只带UA不带Referer或者UA是Python默认的python-requests/2.31.0基本上一眼就会被识别。遇到滑块验证时我个人的处理原则是“先冷却再扩容”。冷却指的是停止当前采集任务等待10到15分钟再进行同时降低后续采集频率。扩容指的是增加IP代理池但我认为个人学习项目完全没必要上付费代理控制频率、尊重目标网站就是最合理的方案。这里必须提醒一点任何采集行为都要在合规边界内进行。个人学习用途的爬虫数据量控制在合理范围内不要并发抢数据不要恶意请求不要将采集结果用于商业目的。做数据分析系统的价值在于掌握分析方法和流程而不是破坏平台秩序。3. 数据清洗与预处理垃圾进垃圾出3.1 数据字段设计采集下来的原始字段只有5个商品ID、名称、价格、评价数、店铺。要支撑后续分析字段数量明显不够需要做二次加工。我在清洗层增加了一个字段派生步骤。原始名称字段是一长串文本比如“Apple iPhone 15 (A3092) 128GB 蓝色 支持移动联通电信5G”这里面包含了品牌、系列、存储版本、颜色、网络制式多个维度的信息。通过关键词匹配的方式我提取了品牌和存储容量这两个最核心的派生字段。import re import pandas as pd def clean_price(price_str): match re.search(r[\d.], str(price_str)) return float(match.group()) if match else None def parse_brand(name): brand_list [Apple, 华为, 小米, OPPO, vivo, 荣耀, 三星, 一加, 魅族, realme] for brand in brand_list: if brand.lower() in str(name).lower(): return brand return 其他 def parse_storage(name): match re.search(r(\d)GB, str(name)) return (int(match.group(1)) GB) if match else None df pd.read_csv(jd_phone_raw.csv) df[价格数值] df[价格].apply(clean_price) df[品牌] df[名称].apply(parse_brand) df[存储] df[名称].apply(parse_storage) df df.dropna(subset[价格数值])3.2 清洗规则的取舍逻辑清洗环节最重要的任务有两个去掉无效数据纠正异常数据。评价数这个字段有坑。有的商品评价数是“1.2万”这种带量的表达有的直接是“0条”还有极少数会是“暂无评价”。统一转换成数字时要单独写一个解析函数处理“万”这个单位。另一个典型问题是价格字段偶尔会有区间价格比如“2999.00”和“4199.00”并存原因是一个SKU下有多个版本列表页显示的是最低价。我当时的处理策略是取区间下限作为分析用价格因为大多数人关注的其实是起售价格。数据清洗这层看似技术含量低实际上影响整个分析结论的可靠性。我自己真实的体会是这几行清洗代码花了半天时间调试而写分析代码只用了不到半小时。建议做数据分析的朋友把清洗规则当成独立模块来维护随着采样的数据量增大很多没预见到的脏数据会出现集中管理清洗逻辑能节省大量返工时间。4. 销售数据分析核心逻辑4.1 价格分层与销量分布价格是手机销售最敏感的因素所以我第一件事就是构建价格区间分布。不需要复杂的数学公式直接对价格做分箱统计就能直接反映一个平台商品供给结构是否健康。分箱的操作我用的是pandas的cut方法价格区间按照主流价位带划分1000元以下、1000-2000元、2000-3000元、3000-5000元、5000元以上五档。关于“销量”列表页没有直接暴露销量数字只有评价数。所以这里需要用评价数作为销量代理变量这个替换逻辑需要在结论里明说不能直接把评价数当销量来汇报。评价数越高通常代表历史销量越大这是一个行业里常用的近似处理。import pandas as pd import numpy as np df[价格区间] pd.cut(df[价格数值], bins[0, 1000, 2000, 3000, 5000, float(inf)], labels[千元以下, 1000-2000, 2000-3000, 3000-5000, 5000以上]) price_dist df.groupby(价格区间, observedFalse).size().reset_index(name商品数量) price_dist[占比] price_dist[商品数量] / price_dist[商品数量].sum() * 100 print(price_dist)跑完这个分组统计能很直观看出京东手机商品供给集中在哪个价位带。我这次的数据里2000-3000元区间的商品数量占比最高符合目前国内手机市场走量机型扎堆的真实状态。这种结论不需要复杂的模型一张分组表就能带来有效洞察。4.2 品牌竞争格局与集中度分析品牌分析是手机类目最有看点的地方。分析的维度有两个品牌在售商品数和品牌平均价格。在售商品数反映品牌的SKU丰富度平均价格反映品牌主打价位带。把这两个指标放在一起看能快速画出一张品牌竞争位势图。brand_stats df.groupby(品牌).agg( 商品数(商品ID, count), 平均价格(价格数值, mean), 总评价数(评价数数值, sum) ).sort_values(总评价数, ascendingFalse) brand_stats[价格位置] np.where(brand_stats[平均价格] 4000, 高端线, np.where(brand_stats[平均价格] 2000, 中高端线, 性价比线)) print(brand_stats.head(10))为了衡量品牌集中度我还计算了CR4也就是排名前四品牌的评价总数占总评价数的比例。CR4超过70%的数据表现说明这个市场的头部效应非常明显留给小品牌的份额已经很小。这个指标对于理解市场竞争结构非常直观。我自己在做这个分析的时候最大的收获不是得出哪个品牌强而是理解了“平均价格”这个指标会被低价机型带偏。用中位数来代替平均数会更稳健尤其在手机这种一个品牌横跨从千元机到万元机的场景里平均值容易被极端值拉高中位数更能代表品牌的主流价格定位。所以如果数据量允许建议同时看均值和分位数。4.3 评价数据的情感与口碑洞察光看商品数量和价格还不够评价数据里藏着更有价值的信息。通过评价数和好评率能构建一个简单的口碑矩阵。好评率高、评价数多的商品是明星机型好评率低、评价数多的商品是问题机型消费者避雷主要看这类好评率中等、评价数少的商品则是边缘型号关注度有限。df[好评率数值] df[商品好评率].str.replace(%, ).astype(float) / 100 hot_models df[df[评价数数值] df[评价数数值].quantile(0.9)] problem_models hot_models[hot_models[好评率数值] 0.95].sort_values(好评率数值) print(重点追踪的问题机型:) print(problem_models[[名称, 价格数值, 评价数数值, 好评率数值]].head(20))这个分析维度的价值在于它把“卖得多”和“口碑差”这两个矛盾特征放在一起暴露出来是普通单品观察看不到的视角。整个分析系统的核心意义就在于此把零散的商品信息汇总成结构性判断把直觉用数据验证。5. 可视化展示与分析报告生成5.1 可视化选型为什么我用PyechartsPython做可视化的常见选项有Matplotlib、Seaborn、Plotly和Pyecharts。Matplotlib是经典工具适合出版物级别的静态图表但是交互性太弱Plotly交互性强但是配置繁琐Pyecharts是我在这个项目里的主力因为它生成的图表是HTML格式可以直接在浏览器打开页面自带缩放、悬停提示等交互功能而且中文显示和主题配色都比较符合日常业务汇报场景。用Pyecharts生成了三个核心图表价格区间的饼图、品牌评价数对比的横向柱状图、品牌价格分布的箱线图。from pyecharts import options as opts from pyecharts.charts import Pie, Bar, Boxplot # 价格区间饼图 pie ( Pie() .add(, [list(z) for z in zip(price_dist[价格区间].astype(str), price_dist[商品数量])]) .set_global_opts(title_optsopts.TitleOpts(title京东手机价格区间分布)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {d}%)) ) pie.render(price_pie.html)柱状图那里需要注意一个细节品牌名称有的很长横向柱状图比纵向更适合展示长名称避免文字重叠。箱线图则用于展示品牌价格中位数和离散程度我这里是先手动分组再传给组件生成。Pyecharts版本之间有较大的API差异从0.x升到1.x之后接口风格变成了链式调用。网上不少教程还在用旧版写法实际跑起来会报一堆错。建议直接装最新稳定版pip install pyecharts装完之后用pyecharts.__version__确认一下版本再决定代码写法。5.2 一键生成报告的思路图表是零散的报告才是系统的输出形式。我写了一个简单的HTML模板将统计表和图表嵌入到一个页面中按“市场规模概览 → 价格分布 → 品牌格局 → 口碑追踪 → 结论建议”的顺序排布生成一个完整的数据分析报告文件。这个做法的好处是项目成果可交付、可分享领导者或者客户不需要跑代码打开HTML就能浏览分析结论。所有图表和表格都以静态页面形式嵌入不用起服务不用装环境Windows自带浏览器直接打开即可。6. 常见问题与排查技巧实录6.1 列表页结构与参数变动京东的搜索列表页改版不频繁但偶尔会遇到商品结构变动导致之前写好的CSS选择器失效。有一次跑采集发现.p-price i这个选择器选不到任何元素页面上所有商品都解析失败。排查过程比较曲折最终确认是页面改版后价格元素的位置变了价格被放到了.p-price下的另外一个子节点。这类问题没有一劳永逸的解法只能靠编写选择器时增加健壮性。我通常是在解析之前先对整页做个打印检查确认节点结构再写选择器。如果真的遇到变化用XPath写成相对路径比CSS选择器容错性更高。6.2 CSV文件中文乱码的根源与处理采集的数据里有大量中文如果直接用to_csv保存Excel打开会显示乱码因为Python默认编码是UTF-8而Windows的Excel默认用GBK读取CSV文件。解决办法是我在代码里已经体现的保存时指定encodingutf-8-sig这个编码会在文件开头加一个BOM头Excel就能正确识别UTF-8了。另外读取CSV文件时同样要明确编码参数。我见过很多同学写出pd.read_csv(data.csv)然后报错的情况多数都是因为没加encoding参数文件编码和系统默认编码不一致导致的。6.3 指标计算偏差问题分析阶段最容易犯的错误是拿原始数据直接用不做口径统一。比如有的价格字段是含“”符号的字符串有的评价数字段是“2.3万”这些不经过清洗直接汇总最后的统计结果一定是错的。我在项目里特别设置了“数据质量检查”步骤每次分析前先执行一个简单的df.info()和df.describe()确认数据量、缺失值情况、数值类型无误后再进入核心计算。这套校验流程看起来机械实际能拦截绝大部分低级错误。6.4 内存与性能优化单个数据集只有几千条记录性能压力通常不大。但一旦把评价数文本字段保留在DataFrame里再反复做字符串匹配速度就会明显下降。我的优化方法是在清洗阶段就把用不到的原始长文本字段直接删掉只保留派生后的数值字段。这样后面所有分组、聚合操作都走纯数值计算速度会快很多。还有一个小技巧是给groupby加上observedFalse参数避免分箱产生的空类别参与计算这在统计占比时会减少很多干扰。7. 项目复盘与个人经验这套系统从零到一跑通前后大概花了两天时间。写代码本身并不难难的是数据采集阶段的耐心和数据分析阶段的思考深度。我自己的一个体会是数据分析项目的价值不在于代码量多庞大而在于每个环节能不能给出一个可信的结论。爬虫拉了数据、清洗逻辑处理了脏数据、分析模块输出了分组统计、可视化呈现了结论这四个环节环环相扣任何一步偷懒都会让最终结果失去说服力。给后续想复现这个项目的人一个建议不要一上来就追求大而全不要试图把所有品牌、所有价位、所有评价都抓下来。先把20页数据跑通、画出3-5张核心图表、得出几个能说清楚的结论这套流程的价值就已经实现了。数据量可以在后续逐步扩大业务流程先稳定才是最重要的。如果你打算往这个方向继续深入可以尝试把这个系统扩展成定时采集任务用Python的schedule库每天固定时间抓一次数据再把历史数据存进SQLite这样就能做价格走势分析和涨跌监控。那种“几天后价格降了多少”的分析就是在这个基础上延伸出来的。
返回列表