做毕设的时候,指导老师给了几个方向让我选,我最后挑了“基于大数据挖掘分析的北京二手房数据分析”。这个题目看起来挺常规的,但真正动手之后才发现,从数据采集、清洗、建模到做成一个能演示的系统,里面的坑比想象中多得多。回头整理这份项目分享,就是想把我走通这条路的过程、踩过的坑、以及那些“如果早知道就好了”的经验写下来,给后面选类似题目的同学一些参考。
这个毕设能做的事其实是三块:第一,把北京二手房市场的真实挂牌数据抓下来,形成一个能支撑分析的数据集;第二,用数据挖掘和机器学习的方法找出影响房价的核心因素,并建模预测价格;第三,把分析结果做成可视化的展示系统,让结论一目了然。它适合对数据分析、爬虫、机器学习感兴趣,并且希望毕设成果既有技术深度又能落地展示的同学来参考。
1. 选题与数据源准备:为什么选北京二手房,以及数据从哪来
1.1 毕设选题的三个判断标准
我选方向时有三个硬性标准:数据可得、技术能落地、成果可展示。北京二手房刚好全部满足。
数据可得这一点很关键。很多毕设题目听起来高大上,但数据获取就是一道坎。有的需要和机构合作、有的涉及隐私、有的根本没有公开数据源。而二手房挂牌数据在房产信息平台上都是公开的,每套房子的价格、面积、户型、朝向、楼层、建筑年代这些字段全都摆在那里,只要想办法抓下来就能用。
技术能落地指的是,这个题目能串联起一整条大数据分析的流程:网络爬虫采集、数据清洗与预处理、特征工程、统计分析、机器学习建模、可视化展示。每一个环节都是课程里学过的知识的应用,但组合在一起又比单个课程设计复杂得多,刚好符合毕设的体量要求。
成果可展示就更重要了。毕设答辩时,老师的注意力是有限的,与其讲一大堆抽象的技术原理,不如打开一个系统,让老师自己点一点、看一看。一个能交互查询、能看图、能预测房价的Web系统,比十页PPT都更有说服力。这是我在答辩准备阶段最深切的体会。
1.2 数据源选型对比与取舍
确定要做北京二手房之后,接下来面临的问题就是数据源选哪个平台。我对比了几个主流的房产信息网站,各自的页面结构、反爬强度和数据完整度差别很大。
| 对比维度 | 平台A | 平台B | 平台C |
|---|---|---|---|
| 页面结构 | 列表页+详情页,字段规整 | 列表页信息量大,详情页较薄 | 页面跳转多,数据分散 |
| 反爬强度 | 中等,有访问频率限制 | 较弱,页面相对容易解析 | 较强,经常出现验证码 |
| 数据完整度 | 高,总价/单价/户型/朝向等齐全 | 中,部分房源缺少建筑年代 | 高,但信息嵌套复杂 |
| 数据更新时间 | 每天更新 | 更新不稳定 | 实时更新 |
我最后选了平台A作为主力数据源,理由有三条:字段结构最规整、数据完整度最高、反爬强度适中。字段规整意味着解析页面时不需要处理大量特例,这对后期数据清洗的省力程度影响巨大。反爬强度适中意味着只要我控制好请求频率,就可以稳定地把数据抓下来,不需要和对方做太多攻防对抗。
1.3 字段规划:想清楚再动手
数据采集之前,我先把需要的字段列了一个清单,这是整个项目里最值得花时间的规划工作之一。
基础字段包括:房源编号、小区名称、所在区域、户型(室厅卫)、面积、朝向、楼层、建筑年代、总价、单价。这些字段直接决定了后续能做什么分析。比如要分析“房龄对房价的影响”,就必须有建筑年代;要分析“户型对总价的影响”,就必须有室、厅、卫的数量;要分析“不同环线位置的价格差异”,就需要坐标信息来定位。
我还加了两个关键字段:经纬度坐标和挂牌时间。经纬度是后面做地图可视化和环线分析的基础,没有坐标就只能按文字描述的区域来做粗略统计,精度会差很多。挂牌时间则可以用来观察房源从挂出到成交或下架的动态变化,这个后面会细说。
字段规划看似简单,实际上特别容易遗漏。我第一版爬虫只抓了页面上的文字信息,后来发现要做地铁距离分析时,没有坐标数据,只能重新补爬,白白浪费了两天时间。所以建议所有做类似项目的同学,动笔写爬虫之前先把分析场景列出来,反向推导需要哪些字段。
2. 爬虫采集链路:从页面解析到数据落库的完整设计
2.1 爬虫的整体架构与设计思路
二手房的爬虫,和一般的小型爬虫有一个明显区别:数据量不是问题,但页面结构复杂、字段多、请求量大。
平台A的房源信息分布在两个层级:列表页和一二级详情页。列表页能看到基础信息,但许多关键字段要进入详情页才能取到。如果对每套房都单独发一次详情页请求,整个采集过程大概需要发几万次请求,这个量级单线程跑会很慢,但并发太高又容易触发反爬。
我的方案是两步走:第一步用列表页抓取房源编号和基础信息,第二步根据编号再去请求详情页补全剩余字段。这样的好处是,即使某次请求失败,我手里已经有了一批编号,可以断点续爬而不是从头再来。
代码层面的设计用了三个核心组件:请求调度器、页面解析器、数据存储器。请求调度器控制并发数和请求频率,页面解析器负责从HTML中提取字段,数据存储器负责去重和落库。三个组件之间的逻辑用队列解耦,后面想调整并发数或者换解析规则,改单块代码就行,不需要牵一发动全身。
2.2 页面解析的核心难点与处理
页面解析是爬虫环节里最需要耐心的地方。二手房的页面信息虽然看起来规整,但仔细看会发现问题很多。
比如楼层字段,平台上的写法五花八门:“低楼层/共6层”“地上22层”“高楼层”……要建模型的时候,我需要的不是这种描述,而是一个可以分层的类别或者数值。所以我专门写了一个楼层解析函数,先把“共N层”和“所在楼层”拆出来,再按照行业习惯映射成层数数值,最后按低、中、高、顶层分桶。
朝向字段就更乱了。有“南”“南北”“东”“东南”,还有带英文的、带斜杠的。我先把所有可能的写法整理成一个映射表,再统一替换成规范的朝向编码。这个工作看起来不起眼,但它直接影响后面“朝向对房价影响”的分析质量。
面积字段也有坑。某些房源会写“建筑面积”和“套内面积”两个数字,有的则只写其中一个。如果不做统一,后面算单价就会混入两种口径的数值,导致统计失真。我的处理方式是优先取建筑面积,缺失时用套内面积乘以一个经验系数补全,并把数据来源标记清楚。
2.3 请求频率控制与异常处理策略
一个稳定的采集过程,关键在于节奏控制。我设置的策略是:每次请求后随机等待一段时间,并将请求间隔控制在每秒几次的范围,避开高峰时段,比如晚上集中抓取。请求头则采用了多组UA轮换,模拟不同浏览器的访问行为,降低被识别为同一客户端的概率。
运行过程中最常遇到的状态码是503和403。我的处理方式是设计带指数退避的重试机制:第一次失败等几十秒重试,再次失败等的时间翻倍,连续失败多次就跳过并记录日志。整个采集跑下来,大概有不到百分之几的请求需要重试,这个比例是完全可以接受的。
还要强调一下合规意识。爬虫采集仅限于公开数据,且用于个人学习和毕设展示,采集频率控制在不影响目标网站正常服务的范围内。如果读者打算自己做类似的采集项目,建议同样把频率设置得保守一些,不要给目标网站带来压力。
2.4 断点续爬与数据落库设计
数据存储我选了MySQL,原因很简单:二手房数据是结构化的,字段之间有明确的逻辑关系,用关系型数据库最自然。另外,后面做Web系统的时候,后端直接连MySQL查询也很顺手。
我建了两张表:房源信息表(字段包括房源编号、小区名称、区域、户型、面积等)和采集状态表(记录每个列表页URL的爬取状态和最后更新时间)。每次启动爬虫时,先查询采集状态表,跳过已完成的页面URL,只处理未采集和采集失败的。这样即使运行中途因为网络问题中断,重新启动也能接着之前的进度继续。
详情页的抓取结果是异步写入的,因为列表页和详情页属于两个批次的请求。这里需要注意房源编号的唯一性约束,否则重复抓取时会出现大量冲突记录。我在建表时给房源编号加了唯一索引,写入时用INSERT IGNORE或ON DUPLICATE KEY UPDATE,从根上解决重复问题。
3. 数据清洗与特征工程:把脏数据整理成能建模的样子
3.1 数据清洗的四个关键动作
数据抓下来以后,第一件事就是做质量检查。我统计了原始数据的规模、完整率和异常情况,发现的问题比预期多得多。
第一个问题是重复记录。同一个房源可能因为平台页面刷新多次,导致抓取到多份记录。我的做法是用房源编号做去重,保留最新一次抓取的状态。
第二个问题是缺失值。有些房源缺少建筑年代,有些缺少朝向,有些缺少楼层信息。处理策略是分类对待:对分析影响很大的字段,比如面积和总价,如果缺失就直接删除该条记录;对影响较小的字段,比如装修情况,则填充为“未知”类别,保留记录做其他分析时仍然可用。
第三个问题是明显异常值。比如面积只有几平方米、总价离谱低、单价远超市场正常区间的记录。我设定了一套过滤规则:面积小于一定阈值、单价超出正常范围的数据一律剔除。这类数据通常是信息填写错误或者车位、储藏室之类非住宅房源混进来的,留着只会干扰模型。
第四个问题是逻辑校验。比如一套两居室记录写着总面积只有不到30平方米,这在逻辑上就不可能成立。我用面积和户型的关系做了一组交叉验证规则,把这类冲突记录筛出来人工检查或直接去掉。
3.2 衍生特征:从原始字段里挖出更多信息
清洗完数据之后,我加了一批衍生特征,这些新字段是后续建模分析的主要输入。
单价是最基础的衍生变量,等于总价除以面积,但这样做出来的单价分布很容易受极端面积干扰。所以我做了两个版本的单价:原始单价和过滤极端值后的单价,建模时用过滤后的版本。
房龄(当前年份减去建筑年代)也是重要特征。不过房龄和房价之间不一定是线性关系,有些老房子因为地段好反而很贵,所以我会在建模时观察它的分布形态,必要时做分箱处理。
环线位置是一个对北京二手房特别重要的特征。我在数据中保留了经纬度坐标,然后根据坐标判断每套房源所在的环线区间,把这个区间作为类别特征加入分析。比如把房源划分为二环内、二环到三环、三环到四环、四环到五环、五环外这几个区间。
距最近地铁站的距离是另一个解释力很强的特征。我通过调用地图开放平台的接口,用房源坐标和站点坐标计算直线距离,再把直线距离乘以一个修正系数来估算实际通行距离。这个特征在后续模型中的重要度排名非常靠前。
3.3 文本字段处理:让模型能理解自然语言信息
小区名称、位置描述这些字段是文本形式,不能直接进模型,需要做一些处理。我的方案是把小区名称和区域描述拼接起来,用分词工具切分成词,再做词频统计,形成一套简单的房源关键词体系。
这个做法有两个用途:一是在统计分析阶段,可以用来观察哪些词出现频率高、价格有什么趋势;二是在可视化阶段,可以做一个词云,让用户直观地看到描述北京房源时最常用的词汇。它不直接参与模型的数值预测,但对提升项目的展示效果帮助很大。
3.4 清洗后的数据质量评估
清洗完成后,我专门做了数据质量评估。最终保留的有效记录数量比原始数据少了接近两成,但这个损失是值得的,留下的数据在字段完整率和一致性上都有了明显提升。
我验证了几个关键指标:面积分布是否符合北京二手房市场的常识、单价分布是否符合各区差异、户型分布的占比是否合理。还抽样对比了平台上部分房源的原始页面,确认清洗后的字段值与页面展示一致,这些验证工作在答辩时是很有力的支撑材料。
4. 挖掘分析与建模:从统计描述到房价预测模型
4.1 北京二手房市场的统计画像
拿到干净的数据之后,我做的第一层分析是描述性统计,简单说就是回答“北京二手房市场整体长什么样”这个问题。
价格分布上,总价呈现明显的长尾形态,大部分房源集中在中间价段,但头部有相当数量的高价房源把整体均价拉高。这时候看均值就容易被极端值带偏,我会同时报告中位数和分位数,让读者对市场的真实水平有更准确的感知。
单价方面,不同环线的差异非常明显。越靠近核心区,单价越高,而且离散程度也越大。这说明核心区内部差异大,既有普通住宅,也有高端楼盘,价格跨度很远;而外部区域离散度相对小,价格区间比较集中。
户型和面积的分析也很有意思。两居室和紧凑型三居室是市场绝对主力,挂牌量占比最高。面积和总价之间的关系不是简单的线性增长,而是存在分段特征,在面积进入某个区间后,总价会有一个明显的跳跃,这背后其实是产品定位的差异。
4.2 相关性分析与特征筛选
建模之前,我先做了特征之间的相关性分析。一个容易忽略的问题是特征之间的多重共线性。比如总面积和室、厅数量天然高度相关,如果同时扔进模型,不仅会让部分模型的解释变差,还可能干扰特征重要度的判断。
我的做法是:先计算所有特征与目标变量的相关系数,同时检查特征之间的相关性矩阵,把相关性过高的一组特征中保留解释力更强的那个,其余的放到候选池里备用。经过这一步,进入模型的初始特征集就精炼了很多。
这里还要特别强调一下,相关系数只能捕捉线性关系。后面建模时我发现,某几个特征虽然相关系数不高,但在树模型中的重要度却很高,说明它们与房价之间存在非线性关系。所以特征筛选不能只靠相关系数,最终还是要用模型本身的重要度来验证。
4.3 房价预测模型:从线性基准到树模型对比
我选择的目标变量是单价而不是总价,因为单价比总价更容易跨房源比较,而且直接对总价建模时,面积带来的影响会主导预测结果,反而掩盖其他因素的作用。目标是单价,输入特征是面积、户型、环线、房龄、距地铁距离、朝向、楼层等。
建模过程分成三步走。第一步是做一个线性回归作为基准模型,它的优点是结果直观,可以很方便地看每个特征的系数和方向。不过线性回归对非线性关系和特征交互的捕捉能力有限,所以它的R方表现只能算及格线。
第二步使用随机森林。树模型天然能处理非线性关系,也能捕捉特征之间的交互效应。随机森林跑下来,效果比线性回归有明显提升。但随机森林的调参空间比较大,树的深度、数量、叶子节点最小样本数都会影响结果,需要结合交叉验证来选。
第三步是梯度提升树。这是效果最好的方案,对特征分布也不敏感,在训练集和验证集上的表现都比较稳定。我最终选了梯度提升模型作为主力预测模型,用交叉验证挑选了学习率、树数量和最大深度等参数。
模型评估我用了三个指标:RMSE(均方根误差)、MAE(平均绝对误差)、R方。RMSE对较大误差更敏感,MAE则能反映平均偏离程度,两个一起看可以知道预测误差是否集中在个体上。最终模型的R方表现超出我的预期,说明这套特征体系对北京二手房单价的解释力是够的。
4.4 模型解释性分析:找出影响房价的关键因素
预测准确只是一个方面,对于毕设来说更重要的是“解释”。我用SHAP值分析了每个特征对预测结果的影响方向和大小。
结果和常识基本一致:位置类特征(环线、距地铁距离)和面积类特征(总面积、户型)对房价的影响最大,房龄的影响中等,朝向和楼层的边际效应相对较小但依然显著。
更细看的话,环线的影响是阶梯式的,跨过一环线区间,单价的预测值会有跳变,这说明位置对房价的作用不是平滑的线性关系,而是带有明显的地段属性。距地铁距离的影响则是连续的,距离越近,预测单价越高。房龄方面,次新房存在溢价,但房龄超过一定年限之后,折旧效应对价格的影响就趋于平缓。
这些分析结论我直接用在了可视化系统里,每个特征都配了对应的图表和解释文本,答辩时讲起来非常顺畅。
5. 可视化系统设计:让分析结果能被看见、能被交互
5.1 系统架构与前后端技术选型
毕设系统我采用了前后端分离的架构。后端用Python的轻量级Web框架提供数据接口,前端用开源图表库和地图库做展示,两者通过JSON格式的数据交互。
选择这种架构的原因是部署简单、开发效率高,而且可视化效果丰富。整个系统在本地就能跑起来,答辩时打开浏览器演示即可,不需要依赖复杂的集群环境。
系统包含四个核心页面:总览看板、地图分布页、价格分析页、预测工具页。
5.2 地图可视化:房源分布一眼看清
地图分布页是整个系统视觉上最出彩的部分。我把每套房源按经纬度标注在地图上,用不同颜色表示不同的单价区间,同时支持按环线和区域筛选。
这个页面解决了一个数据分析中很难用文字表述的问题:北京二手房的价格空间分布特征。从地图上可以很直观地看到,核心区域的价格热力高度集中,往外扩散时价格梯度变化剧烈,而在主要交通沿线和大型居住区存在明显的价格洼地。
除了点位图,我还加了热力图模式,用颜色渲染房源密度和价格水平的叠加效果。交互上支持点击点位查看房源详情,支持框选某一区域查看该区域的价格统计。
5.3 价格分析页与预测工具页的设计
价格分析页把统计分析阶段的所有图表集中展示,包括价格分布直方图、价格随环线变化的箱线图、面积与单价的关系散点图、特征的词云图等。每一个图表都配有简单的解读文字,让不懂数据分析的人也能看懂图表表达的含义。
预测工具页则是一个小型交互应用:用户选择区域、环线、户型、面积、建筑年代等参数,后端调用训练好的模型返回预测单价,同时给出该预测值在数据集中的分位位置,让用户知道这个预测在市场上处于什么水平。
这个小工具在答辩现场演示效果特别好。老师只要动动鼠标调几个参数,系统立刻给出响应,而且预测结果和市场常识基本吻合,大家马上就能理解模型的预测能力。
5.4 展示效果优化的几个细节
可视化展示的效果往往差在细节上。我做了几个小优化:统一的配色方案,冷色到暖色过渡来表示价格从低到高;交互反馈的延迟控制在几百毫秒以内;所有图表都带数据更新时间标注,体现数据的时效性。
还有一个细节是图表的下钻能力。比如价格分布图点某一个柱子,可以继续查看这个价格区间内房源的环线构成、户型构成等信息。这种多级下钻让整个系统不完全是一堆静态图表的拼凑,而是一个可以探索的工具,观感上会专业很多。
6. 毕设踩坑记录:如果重做一次,我会提前避开的事
6.1 数据源的隐藏问题与人工校验
这个项目最大的坑还是数据质量。平台展示给用户的数据和真实情况之间,存在一些不容易察觉的偏差。比如有的平台会把挂牌价的“万”取整显示,导致总价和单价严格计算时对不上;有的房源面积包含赠送的地下室;有的平台会把车位和住宅放在同一个列表里。这些如果不做人工校验,根本不会发现。
我的解决办法是:每次完成主要清洗步骤后,抽一批记录打开原始页面核对。虽然这个过程很费时间,但换来的是整套分析结论的可靠性。在毕设答辩中,当老师问到数据可信度时,我能拿出清洗前后的对比数据和抽样核验记录,这是很有力的回答。
6.2 数据时效性对结论的影响
二手房数据不是静止的。我从某年3月开始采集,5月份再对比时发现部分房源已经下架或调价。这意味着,如果只抓一次数据,分析结论反映的是一个时间截面的市场状态,不一定是当前市场状态。
这个时效性问题可以从两个角度处理:一是分析时明确说明数据的时间窗口,让读者知道结论对应的是哪个时期的市场;二是做时间维度的对比分析,比如分别在两个时间点采集数据,对比价格分布的变化幅度。时间维度的对比让毕设的层次更丰富,也让结论更有说服力。
6.3 模型训练中的偏差处理
模型训练时有一个隐藏偏差差点让我翻车。我没有做数据的时间切分,直接随机划分了训练集和验证集,结果因为数据本身都来自同一时间段,这种划分其实没问题。但如果数据跨越了较长的时间,比如包含了不同月份的数据,就可能把时间趋势混入到学习过程中,导致验证集上的表现高估模型真实泛化能力。
处理这类问题的一个通用原则是:如果数据带有明显时间属性,尽量按时间顺序切分训练集和验证集,而不是随机切分。这样才能模拟模型在真实使用中面对未来数据时的表现。
另一个偏差是目标变量的分布问题。直接预测原始单价时,模型对高价位房源的系统性低估比较明显,因为高价房数量少、模型很难学到它们的规律。后来我对目标变量做了对数变换或者加权训练,在预测效果上获得了显著改善。
6.4 答辩展示的侧重点选择
答辩准备阶段,我重新梳理了整个项目的核心贡献,最终把展示重点放在了“数据全流程处理能力”和“模型可解释性”两个关键词上。
数据全流程处理能力证明的是工程量:从几十万条原始页面数据清洗出干净可用的结构化数据集,这个过程本身就很有说服力。模型可解释性证明的是分析深度:不只是拿一个黑箱模型预测价格,而是能告诉老师哪些因素在起作用、作用的逻辑是什么。
不要试图在答辩里覆盖所有技术细节,那会让重点失焦。我当时的策略是:把采集、清洗、建模的具体实现放在辅助材料和演示环节,在正式讲解中只突出最核心的流程和最有价值的发现。实践证明,这种聚焦的展示方式比罗列技术栈受欢迎得多。
整个项目做下来,我最大的感受是:毕设的价值不在于题目听起来多高级,而在于你能不能把一个看似常规的题目做出完整的闭环。从数据到结论,从模型到系统,每一个环节都会暴露真实世界的问题,解决这些问题的过程才是最大的收获。如果后面有人也要做类似的数据分析类毕设,我希望这份记录能帮你把路走得更顺一点——尤其是爬虫的节奏控制、数据清洗的耐心程度、以及提前规划的字段清单,这些都是我自己做反了之后才明白的事。