ARTICLE DETAIL

资讯详情

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

Python爬虫数据分析可视化实战:从数据清洗到交互式看板

Python爬虫数据分析可视化实战:从数据清洗到交互式看板 Day 36【99天精通Python】综合实战 - 爬虫与数据分析可视化(下) - 让数据“说话”这个系列走到第36天终于进入综合实战最有“成品感”的阶段。上一篇我们把爬虫部分讲完了requests BeautifulSoup 从目标站点把数据抓下来、做了初步清洗拿到了几百甚至上千条原始记录。但原始数据本身没有意义它只是躺在那里的数字和文本。这篇要做的事情就是接续上一篇的成果把爬虫产出的“原材料”经过数据分析的加工、可视化的包装变成真正能辅助决策的信息——这一步也是爬虫、数据分析、可视化三个环节里最出成果、最有成就感的一段。这篇适合已经掌握 Python 基础语法、能写简单爬虫、但对数据分析还停留在“听说过”阶段的读者。我不打算堆砌理论而是直接用上一篇爬下来的真实数据走一遍全流程数据读入、字段清洗、分组聚合、指标计算最后用可视化图表把结论“讲”出来。全程代码都能直接跑你只需要把文件路径换成本机真实路径。做完这一篇你会有一个能写进简历的数据分析小项目也会真正理解什么叫“让数据开口说话”。1. 整体设计从爬虫到可视化中间经历了什么1.1 先盘点上一站留下的“数据家底”上一篇选的案例是爬取某图书电商网站的商品列表页拿到的字段包括书名、价格、评论数、分类、出版社、出版时间这六项。原始数据大概长这样书名 《Python编程从入门到实践》 价格 89.5 评论数 32847 分类 计算机 出版社 人民邮电出版社 出版时间 2020-10把这样的记录存成 CSV 文件之后一共积累了 800 多条。坦率说这个数量级做数据分析偏小但作为教学项目足够完整。它覆盖了“数值型数据”价格、评论数、“类别型数据”分类、出版社和“时间型数据”出版时间三种数据类型刚好能把数据分析最核心的操作手法全部串起来这是挑选案例时我特意设计的。这里必须先明确一个观点数据分析不是从 pandas 开始的而是从“想清楚要回答什么问题”开始的。拿到这 800 条数据我给自己列了四个问题什么类别的书最多各分类的占比如何价格集中在哪个区间高评论量的书是不是更贵出版时间有没有明显的趋势近年什么类目在增长哪些出版社出书最多头部效应明显吗后续所有清洗、聚合、可视化都是围绕这四个问题展开的。我见过太多人一上来就df.describe()跑完也不知道要干嘛。数据只有在你带着问题去看它的时候才有价值这个问题意识越早建立越好。1.2 技术选型为什么是 pandas pyecharts 而不是别的处理数据阶段我用了 pandas这个没什么好争论的Python 数据分析的事实标准就是它DataFrame的向量化操作处理几千行数据几乎是瞬时完成的远快于用 for 循环一个个处理。数据可视化阶段则用了两个库做配合matplotlib 用于快速出静态图排查数据问题pyecharts 用于产出最终交付的交互式图表。选 pyecharts 的原因有两点。第一它是国产库文档对中文支持友好图表默认的样式也更符合国内用户的审美习惯不需要额外配中文字体——这点是 matplotlib 的痛默认字体显示中文全是方块每次都要手动指定SimHei之类新手一踩一个坑。第二pyecharts 输出的是 HTML 文件可以在浏览器里缩放、悬停、筛选这种交互能力对于分析报告来说非常实用你可以把多个图表拼成一个 HTML 看板发给同事别人用浏览器就能查看不用装任何环境。工具选型有个句大实话不要一味追求功能强大要看你的交付场景是什么。自用探索分析选 matplotlib给别人看选 pyecharts 或者直接做可视化大屏。当然也有些人会提 seaborn 或者 Plotly各有优劣但本文项目用 pandas pyecharts 已经绰绰有余。2. 数据清洗决定分析质量下限的隐形工作量2.1 读入数据先摸清“脏乱差”的底细爬虫存下来的 CSV 文件不能指望它干净编码、缺失、格式通通要处理。第一步先把数据读进来看看基本情况import pandas as pd df pd.read_csv(books_data.csv, encodingutf-8) print(df.shape) print(df.dtypes) print(df.head(10))执行之后我注意到几个问题评论数字段读出来是字符串类型因为原始网页里带“条”字价格字段有部分缺失个别记录是 NaN出版时间既有“2020-10”这种标准格式也有“2020年10月”这种中文格式甚至有几位漏了年份只写了月份。这些脏数据如果不去管它后面画图会出现大量报错或者明显错误的结果。处理脏数据有个总原则先清洗再分析但清洗要克制不能把有效信息洗没了。比如某个字段是空值如果记录本身还有价值就不要整行删除而是考虑填充或者保留分析时剔除。2.2 字段类型修正与缺失值处理对评论数字段去“条”字并转成整数用.str.replace()加astype()一把梭df[评论数] df[评论数].str.replace(条, ).str.strip().astype(int)价格列缺失了 23 条我选择的处理方式是用该分类下图书价格的中位数填充。为什么不选平均数因为有少量高端精装书会把平均价格拉得很高用中位数更稳健不容易被极端值带偏。这是处理缺失值的一个经典技巧简单又有效强烈推荐记下来df[价格] df.groupby(分类)[价格].transform(lambda x: x.fillna(x.median()))出版时间的清洗相对麻烦一点因为格式太乱我用 pandas 的to_datetime配合format参数分步解析先把带“年”的字符串统一替换成标准格式再统一转换df[出版时间] df[出版时间].str.replace(年, -).str.replace(月, ) df[出版时间] pd.to_datetime(df[出版时间], errorscoerce)注意errorscoerce这个参数它会把解析不了的日期变成NaTNot a Time而不是直接抛异常终止程序。之后再对NaT的记录做删除或填充我用的是前向填充法同一分类下用上一条有效记录的出版时间填空因为同一系列的书出版时间往往很接近。跑完之后再看一眼df.info()所有字段类型都正常了。清洗部分是整个项目中最花时间的环节我估计占了百分之四十的工作量但它直接决定了后续图表是否可信。这里给新手一个建议清洗的每个操作后都打印一行shape或者head()确认数据量没有出现异常变化养成这个习惯能避开很多隐蔽的数据错误。3. 分析逻辑怎么从表格里“挖”出有价值的结构3.1 分类占比与价格分布的聚合计算数据干净了开始回答第一和第二个问题不同分类的图书数量、价格带分布。这里用到 pandas 两个最核心的操作groupby聚合和pd.cut分箱。先看分类占比一句话搞定cate_count df.groupby(分类)[书名].count().sort_values(ascendingFalse) print(cate_count)结果里计算机类占了约 28%小说类 22%经管类 15%其余分散在历史、心理、艺术等分类。分类之间的差异肉眼可见说明这个网站的上架策略偏重技术和虚构类图书。后面可视化阶段这个结果会做成柱状图一眼就能看出头部类目和长尾类目。再看价格分布这里先把连续的价格切成区间用pd.cut分箱统计每段的图书数量bins [0, 30, 50, 80, 120, 200, 500] labels [30以下, 30-50, 50-80, 80-120, 120-200, 200以上] df[价格带] pd.cut(df[价格], binsbins, labelslabels) price_band df.groupby(价格带, observedTrue)[书名].count()分箱的边界值不是随便拍的我参考了电商平台惯用的价格锚点30 元以下基本属于促销书30到50是大众平装书主流带50到80是精装和工具书区间80以上要么是高定价专业书要么是套装。这样划分之后每个区间都有实际业务含义而不是机械地等距切分。3.2 评论数与价格的交叉验证第三个问题价格和评论量到底有没有关系很多人第一反应是算相关系数但“高评论受欢迎可能便宜”这种直觉未必对。我用分组均值的方式直接看趋势price_comment df.groupby(价格带, observedTrue)[评论数].mean().round(0) print(price_comment)跑出来的结果有点意思30元以下的图书平均评论数是 511230到50元区间是 864450到80元区间是 612380到120元区间是 3100价格越高评论反而越少。这其实是正常的低价书走量、受众广高价专业书虽然单价高但购买人群狭窄评论数天然偏少。所以“评论多便宜好书”这个想法在数据面前站不住脚。这里要多说一句数据分析的价值经常体现在这种“验证直觉”或者“打脸直觉”的时刻。你不需要做出什么高深模型把问题定量描述清楚就已经比绝大多数凭感觉做判断的人强了。分析到这里我建议把每个结论在心里默念一遍它的前提和口径写报告的时候才不会闹笑话。3.3 时间趋势与出版社头部效应最后看出版时间趋势。先把年份提出来再按年份聚合df[出版年份] df[出版时间].dt.year year_trend df.groupby(出版年份)[书名].count()结果比较清晰2018年前后上架量平稳2019年开始明显上升2022年达到峰值后有所回落。这反映的是网站内容运营的节奏也可能是爬取数据的样本偏差——清单页通常只展示近期热门商品老书容易被挤出列表所以年份越近数量越多。做分析时要能分辨出这种“数据采集机制带来的偏差”否则会把采样噪声误当成真实趋势这是进阶分析能力的分水岭。出版社头部效应直接用groupby(出版社).count()排序取前10计算占比pub_count df.groupby(出版社)[书名].count().sort_values(ascendingFalse) top10_ratio pub_count.head(10).sum() / len(df) * 100前10家出版社贡献了全站 31% 的图书行业头部聚集效应非常显著。这个结论对采购、推荐的业务都有参考价值。数据本身不会告诉你“该怎么做”但它能告诉你在哪里用力最有效。4. 可视化实现从图表到数据看板4.1 pyecharts 出图的基本套路分析阶段产出了四个核心结论分类占比、价格带分布、价格评论关系、年度趋势。现在用 pyecharts 把它们画出来。先安装依赖pip install pyechartspyecharts 的用法很统一每种图都是“建对象、加数据、设配置、生成文件”四步。以分类占比柱状图为例from pyecharts.charts import Bar from pyecharts import options as opts bar Bar() bar.add_xaxis(cate_count.index.tolist()) bar.add_yaxis(图书数量, cate_count.values.tolist()) bar.set_global_opts( title_optsopts.TitleOpts(title图书分类数量Top15), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate-45)), ) bar.render(分类数量.html)有两个细节值得注意。第一add_yaxis传的数据必须是 Python 列表不能直接传 pandas 的 Series否则序列的索引会对不上这是我第一次用 pyecharts 时踩过的坑。第二分类名称如果太长X 轴标签会重叠通过rotate-45旋转 45 度解决。4.2 一图一结论宁可少而精不要大杂烩饼图做价格带占比折线图做年度趋势散点图做价格与评论关系。我只挑其中两个特别典型的展开讲。价格带饼图重点是把颜色做得克制并且突出“30-50元是主战场”这个结论from pyecharts.charts import Pie pie Pie() pie.add(, [list(z) for z in zip(price_band.index.astype(str), price_band.values)]) pie.set_global_opts(title_optsopts.TitleOpts(title图书价格带分布)) pie.set_series_opts(label_optsopts.LabelOpts(formatter{b}: {d}%)) pie.render(价格带分布.html){d}是 pyecharts 内置的百分比格式化参数不需要你自己算占比非常方便。画饼图我有个习惯类别超过6个就不要硬画饼图一坨小扇区根本看不清改画柱状图或者“TopN 其他”的模式。散点图用于表达价格和评论数的关系我会先把异常值过滤掉比如评论数大于 30000 的超热门书不筛掉的话所有点会被挤压到左下角图形完全失去辨识度。过滤之后用Scatter画from pyecharts.charts import Scatter df_filtered df[df[评论数] 30000] scatter Scatter() scatter.add_xaxis(df_filtered[价格].tolist()) scatter.add_yaxis(评论量, df_filtered[评论数].tolist()) scatter.set_global_opts( title_optsopts.TitleOpts(title价格与评论量散点图), xaxis_optsopts.AxisOpts(name价格(元)), yaxis_optsopts.AxisOpts(name评论数), ) scatter.render(价格评论散点.html)散点图比较适合观察“是否有相关趋势”从结果看点在 30到80元区间相对密集高价格区明显稀疏跟前面分组统计的结论互相印证。4.3 拼装数据看板把单个图表升级为“可视化大屏”雏形单个 HTML 文件虽然能用但四个文件来回切换很别扭。热词里频繁出现的“可视化大屏”“百度可视化大屏”也说明大家真正想要的是“多图表同框”的呈现形式。pyecharts 提供了组合页面的能力最简单的做法是用Page把多个图表按顺序纵向排列from pyecharts.charts import Page page Page(layoutPage.SimplePageLayout) page.add(bar, pie, line, scatter) page.render(图书数据分析看板.html)打开这个 HTML往下滚动就能依次看到所有图表而且每张图都能悬停看数据明细发给同事或者放进周报里都够用。如果需求是真正的数据大屏比如像百度可视化大屏那种带深色背景、栅格布局、自动轮播的效果pyecharts 也可以实现核心思路是使用Grid或者基于Page(layoutPage.DraggablePageLayout)拖拽排版后保存布局配置。这个玩法可以在完成基础看板之后再探索先把重点放在结论的可视化表达上别一上来就追求大屏酷炫效果分析深度才是核心。5. 常见问题与排查技巧实录做这个项目过程中我确实踩了不少坑下面把这些问题的现象、原因和解决方案整理成一个速查表按真实场景中的概率排序对你复现项目最有参考价值。问题现象原因分析解决方式图表中文乱码matplotlib 图中文字显示为方块系统没有匹配到中文字体matplotlib 指定中文字体如plt.rcParams[font.sans-serif][SimHei]pyecharts 默认无此问题XPath 提取不到文本爬虫取到空列表text()和string()用混单层文本用text()多层嵌套用string()详见下方补充CSV 乱码pandas 读入后中文字符乱文件实际是 GBK 编码用 UTF-8 解析了使用encodinggbk或encodingutf-8-sig按报错灵活切换日期解析失败to_datetime抛出异常时间字段包含中文或缺失先替换中文再转换加errorscoerce容错图表输出空白浏览器打开 HTML 没内容pyecharts 资源正常但数据为空确认add_yaxis传的是 List 而非 Series检查 render 路径评论数字符串groupby 后类型报错数字带“万”“条”等单位先replace去除单位再astype转数值爬虫被封连续请求后被拒绝访问请求频率太高触发反爬随机 User-Agent加time.sleep()延迟控制速率分组结果 NaN聚合后部分组为空分组字段含空值dropna()清理分组键或fillna(未知)其中 XPath 的text()和string()是爬虫阶段最容易被问到的点单独解释一下。text()只能取当前节点下的直接文本节点如果目标文本被包裹在子标签里比如div classpricespan89.5/span/div你用//div[classprice]/text()取到的是空值或“”而不是 89.5。这时候应该用string()//div[classprice]/string()它会递归拼接所有后代文本得到“89.5”。两者适用场景完全不同实战中我至少见过五次因为text()取不到值而卡住的提问。另外在真正写分析脚本时建议把所有处理管线写到一个clean.py里每完成一步保存一个中间文件比如data_cleaned.csv。这样跑可视化时不需要重新执行爬虫和清洗节省大量时间也方便你随时对比清洗前后的差异。我在这个项目里就是从清洗后的 CSV 开始的分析等于把“数据获取”和“数据分析”两个阶段解耦了调试起来思路特别清楚。6. 一些实操中的真切体会回看这整个项目从决定爬哪个网站到上篇完成数据采集再到现在输出可视化看板前后花了不少时间。我最大的体会是数据可视化不是把数字变成图而是把分析结论变成别人能一眼看懂的事实。光会调用Bar()、Pie()远远不够你得想清楚每一张图要回答什么问题、读者是谁、表达的重点在哪里。最后再分享一个小技巧也是我做完这个项目之后总结的分析做完不要急着写代码总结先把结论用一两句话说给一个完全不懂技术的人听如果你能让他听懂“哪个分类卖得多”“什么价位最集中”“近几年趋势怎样”说明你的分析逻辑是闭环的。很多人分析报告写得晦涩不是因为图不够炫而是因为自己都没想清楚结论。先讲清楚话再动手画图数据才能真正“开口说话”。下一步你可以做的扩展方向很多比如把这套流程套用到招聘数据、电商评论、天气历史等任何你感兴趣的公开数据上或者在现有数据上增加更多维度字段做联合分析再进阶一些可以把分析结果接入定时任务每周自动跑一次数据更新看板。这个系列的后续内容里我也会继续深入讲怎么让分析流程自动化、怎么把看板部署到服务器上供团队访问让这个项目逐步走向真正的“生产可用”。
返回列表