二手房数据采集清洗与可视化分析实战
简介本资源是一套完整的二手房数据采集与可视化分析毕业设计项目面向计算机、电子信息工程、数学等专业的本科生适用于课程设计、期末大作业及毕业设计参考。项目基于Python实现全流程闭环从链家等平台爬取原始数据含UTF8/ANSI双编码CSV、清洗去重、特征工程到构建交互式可视化看板HTMLJSECharts及多维度统计分析报告PPTX覆盖数据获取、处理、分析、呈现全环节。压缩包共155个文件含18个核心Python脚本含爬虫、清洗、分析模块、18个CSV数据集含原始、清洗后、采样版等多版本、65张可视化图表PNG、15个HTML前端页面及配套JS、INI配置文件等整体35.13MB结构清晰、模块解耦便于学习调试与功能扩展。目前已有841人学习下载附带完整数据集、可运行源码、分析报告PPT及详细字段说明适合具备Python基础、希望掌握真实业务场景下数据采集与分析实践能力的学生快速上手并完成高质量毕设交付。1. 二手房数据采集与可视化分析一个能跑通、能答辩、能改出新东西的毕业设计闭环你花三天爬了链家、贝壳结果发现反爬策略一升级脚本就崩你用 matplotlib 画了十张图答辩老师皱着眉问“这能看出房价和学区的关系吗”你把清洗后的 CSV 丢进 pandasdf.describe()一跑发现total_price列里混着“面议”“电话详谈”——这不是数据集这是玄学现场。这个资源不是“Python 爬虫入门教程”而是一套已实测跑通、含原始脏数据→清洗逻辑→特征工程→多维可视化→可扩展分析框架的完整毕业设计交付物。它包含真实采集的 20000 条二手房挂牌数据含城市、区域、小区名、户型、面积、单价、总价、朝向、装修、楼层、建成年份、是否满五唯一等 18 字段配套 Python 源码覆盖 requests BeautifulSoup 基础采集、正则清洗、pandas 分箱处理、seaborn plotly 交互图表、以及基于 sklearn 的简单价格预测 baseline。适合计算机、信工、统计类本科生直接复现答辩也足够作为课程设计拓展基础——比如加个 Flask Web 展示页或把price_per_sqm按地铁站 500 米缓冲区做空间聚合。它不承诺“一键生成论文”但保证你从解压.rar开始2 小时内看到第一张热力图跳出来。2. 数据采集与清洗为什么用 UTF-8 而不是 ANSI三个关键清洗动作拆解2.1 采集逻辑requests BeautifulSoup 的最小可行抓取链项目中crawler.py并未使用 Selenium 或 playwright而是基于 requests 构建轻量级采集链。核心在于绕过前端 JS 渲染干扰直接请求真实 API 接口。以某城市链家为例实际请求地址形如# crawler.py 片段 base_url https://bj.lianjia.com/ershoufang/ params { city: bj, page: 1, limit: 30 } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 } response requests.get(base_url, paramsparams, headersheaders, timeout10)注意该代码未硬编码 cookie 或 token依赖目标站未强制校验 Referer 或 X-Requested-With。若遇到 403需在 headers 中补全Referer: https://bj.lianjia.com/ershoufang/。项目中ershoufang-origin-utf8.csv即由此逻辑导出字段顺序与网页 DOM 结构严格对齐避免因页面改版导致列错位。2.2 编码陷阱UTF-8 vs ANSI 的血泪经验你打开ershoufang-origin-ansi.csv会发现中文全是乱码而ershoufang-origin-utf8.csv正常——这不是文件本身问题是采集时响应头未显式声明编码。requests 默认按ISO-8859-1解码当服务器返回Content-Type: text/html; charsetgbk时必须手动指定response.encoding response.apparent_encoding # 自动探测 # 或更稳妥写法 response.encoding gbk if gbk in response.headers.get(content-type, ).lower() else utf-8项目中clean_data.py开头即强制统一读取为 UTF-8import pandas as pd df pd.read_csv(ershoufang-origin-utf8.csv, encodingutf-8, dtypestr) # 全部读为字符串防数字转科学计数法提示dtypestr是关键。否则area列若含“100.0㎡”pandas 会自动转为 float后续str.replace(㎡, )报错。所有字段先保字符串清洗完成再pd.to_numeric(..., errorscoerce)转数值。2.3 清洗三板斧正则提取、分箱归一、缺失值策略清洗不是删空行而是构建可复用的转换管道。clean_data.py中定义了clean_pipeline()函数核心三步价格字段标准化total_price含“320万”“面议”“暂无报价”用正则提取数字并统一为万元单位import re def extract_price(text): if pd.isna(text) or 面议 in str(text) or 暂无 in str(text): return np.nan match re.search(r(\d\.?\d*)[万], str(text)) return float(match.group(1)) if match else np.nan df[total_price_wan] df[total_price].apply(extract_price)面积字段去单位area列含“100.5㎡”“89平”“约75平米”统一提取数字并转 floatdf[area_sqm] df[area].str.extract(r(\d\.?\d*)).astype(float)楼层字段结构化floor列为“高楼层共32层”“低楼层共18层”“中楼层共26层”拆解为floor_level高/中/低和total_floors总层数df[[floor_level, total_floors]] df[floor].str.extract(r(高|中|低)楼层.*?(\d)层) df[total_floors] pd.to_numeric(df[total_floors], errorscoerce)参数说明errorscoerce将无法转数字的值设为 NaN而非报错中断。这是生产级清洗的底线——宁可丢数据不可崩流程。3. 可视化分析从静态图到交互式仪表盘的四层递进3.1 基础分布用 seaborn 绘制带统计信息的直方图eda_basic.py首先验证数据质量。以price_per_sqm每平米单价为例绘制带 KDE 曲线和统计摘要的直方图import seaborn as sns import matplotlib.pyplot as plt plt.figure(figsize(10, 6)) ax sns.histplot(df[price_per_sqm].dropna(), kdeTrue, bins50, colorsteelblue) ax.set_title(二手房单价分布元/㎡, fontsize14) ax.set_xlabel(单价元/㎡) ax.axvline(df[price_per_sqm].median(), colorred, linestyle--, labelf中位数: {df[price_per_sqm].median():.0f}) ax.legend() plt.show()逻辑说明kdeTrue叠加核密度估计曲线比单纯直方图更能反映分布形态axvline标出中位数避免均值被极端高价扭曲。此图直接暴露数据偏态——若右偏严重如出现 20 万/㎡ 天价房需在报告中说明剔除逻辑。3.2 区域对比用 boxplot 揭示房价离散度region_analysis.py对比不同行政区房价差异。关键不是柱状图而是箱线图boxplotplt.figure(figsize(12, 8)) sns.boxplot(datadf, xdistrict, yprice_per_sqm, orderdf[district].value_counts().index[:8]) plt.xticks(rotation45) plt.title(各行政区单价箱线图前8名, fontsize14) plt.ylabel(单价元/㎡) plt.tight_layout() plt.show()为什么选 boxplot箱体高度 IQR四分位距反映价格离散程度须线长度 1.5×IQR超出者为异常值如某区出现 10 万/㎡ 孤立点中位数横线位置直观比较各区“典型价格”。若某区箱体窄且中位数高如海淀说明学区房价格稳定且昂贵若箱体宽如朝阳则刚需与豪宅并存。3.3 多维关联用 scatterplot_matrix 展示变量关系corr_analysis.py不止计算相关系数矩阵更用sns.pairplot可视化关键变量组合# 选取业务强相关字段 key_cols [area_sqm, total_price_wan, price_per_sqm, year_built, floor_level] g sns.pairplot(df[key_cols].dropna(), huefloor_level, paletteSet2, diag_kindhist) g.fig.suptitle(核心变量两两关系图, y1.02) plt.show()参数说明huefloor_level用颜色区分楼层类型diag_kindhist在对角线上显示单变量分布。观察area_sqmvstotal_price_wan散点图若呈明显线性趋势说明总价主要由面积驱动若year_builtvsprice_per_sqm出现 U 型老房与新房贵次新房便宜则暗示“学区老破小”和“改善型次新”双轨行情。3.4 交互式仪表盘用 plotly express 快速生成可筛选图表dashboard.py使用 plotly express 构建交互式看板支持下拉筛选区域、滑块调节面积范围import plotly.express as px fig px.scatter( df, xarea_sqm, yprice_per_sqm, colordistrict, sizetotal_price_wan, hover_data[community, house_type], title面积-单价散点图按区域着色圆圈大小总价 ) fig.update_layout(height600) fig.show()价值点答辩时鼠标悬停即可显示小区名、户型点击图例可隐藏某区数据比静态图更具说服力。部署时只需fig.write_html(dashboard.html)无需 Flask 后端。4. 预测建模与报告落地从 baseline 到答辩话术的实战衔接4.1 价格预测 baseline用 sklearn 实现可解释的线性回归model_price.py不追求复杂模型而是构建可解释、易答辩的 baselinefrom sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_absolute_error, r2_score # 特征工程构造衍生变量 df[age] 2024 - df[year_built] # 房龄 df[is_old] (df[age] 20).astype(int) # 是否老房 X df[[area_sqm, age, is_old, total_floors]] y df[price_per_sqm] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) model LinearRegression() model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) print(fMAE: {mean_absolute_error(y_test, y_pred):.0f} 元/㎡) print(fR²: {r2_score(y_test, y_pred):.3f}) print(特征系数:, dict(zip(X.columns, model.coef_)))答辩话术重点“R²0.62 说明 62% 的单价波动可由面积、房龄、总楼层解释”“面积系数为正3200即每增 1 ㎡单价升 3200 元符合市场认知”“房龄系数为负-180说明房龄每增 1 年单价降 180 元体现折旧效应”。4.2 PPT 报告结构三页讲清技术深度项目附带的report.pptx并非模板堆砌而是紧扣答辩评委关注点设计页码内容要点技术细节P3 数据采集展示crawler.py关键代码截图 请求成功率统计98.2%标注 headers 中 User-Agent 和 Referer 的必要性说明未用 Selenium 降低资源消耗P5 清洗逻辑用流程图展示“原始文本 → 正则提取 → 类型转换 → 异常值标记”四步强调errorscoerce的鲁棒性设计对比清洗前后price_per_sqm缺失率从 12.7% 降至 0.3%P7 可视化结论左图海淀区单价箱线图中位数 8.2 万右图朝阳区散点图面积与总价强相关标注图中异常值坐标如某朝阳楼盘单价 15 万引出“高端改善盘”细分市场提示答辩时切忌念 PPT。指着箱线图说“您看海淀箱体窄、中位数高说明学区房价格共识强而朝阳箱体宽意味着同样面积有 4 万一平的老破小也有 12 万一平的江景豪宅——这正是我们后续可做的聚类分析方向。”4.3 毕业设计加分项两个低成本高价值扩展点不必重写全部代码两个微调即可提升项目深度加入地理信息用geopy根据小区名获取经纬度再用folium绘制热力图from geopy.geocoders import Nominatim geolocator Nominatim(user_agentershoufang) location geolocator.geocode(北京市海淀区中关村南一街1号) print(location.latitude, location.longitude) # 输出39.985, 116.315注意Nominatim 有调用频率限制建议先批量查好存入community_geo.csv避免答辩演示时卡顿。增加时间维度若采集多月数据可计算各小区月度均价变化率用plotly.graph_objects.Scatter绘制趋势线fig.add_trace(go.Scatter(xdf_month[date], ydf_month[avg_price], name中关村某小区))5. 避坑指南五个让答辩老师点头、让调试不崩溃的真实问题5.1 现象pandas.read_csv()报错UnicodeDecodeError: utf-8 codec cant decode byte 0xd0原因文件实际为 GBK 编码但代码强制指定encodingutf-8。常见于ershoufang-origin-ansi.csv。解决先用chardet库探测编码import chardet with open(ershoufang-origin-ansi.csv, rb) as f: rawdata f.read(10000) encoding chardet.detect(rawdata)[encoding] # 输出 GBK df pd.read_csv(ershoufang-origin-ansi.csv, encodingencoding)5.2 现象seaborn.boxplot()报错ValueError: object of too small depth for desired array原因district列含大量空值或特殊字符如“\xa0”不间断空格导致value_counts()返回空 Series。解决清洗时统一处理空格和空值df[district] df[district].str.strip().replace(, np.nan) df df.dropna(subset[district])5.3 现象plotly.express.scatter()生成的 HTML 文件打开空白原因plotly 默认使用 CDN 加载 JS 库内网环境或禁用网络时无法加载。解决离线模式导出fig.write_html(dashboard.html, include_plotlyjscdn) # 改为 include_plotlyjsdirectory # 或更彻底include_plotlyjshttps://cdn.plot.ly/plotly-latest.min.js需联网5.4 现象LinearRegression训练后model.coef_全为 0原因特征X中存在全零列如total_floors有大量 NaNfillna(0)后整列变 0导致矩阵奇异。解决检查特征矩阵秩import numpy as np print(np.linalg.matrix_rank(X_train_scaled)) # 应等于 X_train_scaled.shape[1] # 若不等用 df.dropna() 或删除低方差列5.5 现象答辩演示时geopy地理编码超时或返回 None原因Nominatim 服务限流1 秒/次且中文地址解析准确率低于英文。解决预处理 备用方案# 1. 先用百度地图 API需申请 key批量获取 # 2. 若失败回退到规则匹配df[district].map({海淀:39.985,116.315, ...}) # 3. 最终缺失值用区域中心点填充6. 从那以后我每次开新项目都强制走一遍“三验流程”你拿到这个.rar包解压后看到十几个 CSV 和 Python 文件第一反应可能是“先跑main.py看效果”。但我的血泪经验是任何毕业设计源码在运行前必须完成“三验”——验编码、验路径、验依赖。这不是多此一举而是避免在答辩前夜发现pandas版本冲突导致read_csv行为突变。6.1 验编码用file命令一眼识别文件真实编码Linux/macOSfile -i ershoufang-origin-utf8.csv # 输出ershoufang-origin-utf8.csv: text/plain; charsetutf-8 file -i ershoufang-origin-ansi.csv # 输出ershoufang-origin-ansi.csv: text/plain; charsetiso-8859-1为什么不用 Notepad 查因为 GUI 工具可能缓存旧编码标识。file命令读取文件二进制头结果绝对可信。若charsetunknown立即用iconv -f gbk -t utf-8 input.csv output.csv转换。6.2 验路径用os.path.abspath()打印所有数据路径在clean_data.py开头插入import os print(当前工作目录:, os.getcwd()) print(ershoufang-origin-utf8.csv 绝对路径:, os.path.abspath(ershoufang-origin-utf8.csv))玄学时刻曾有学生把项目从 D 盘移到 E 盘pandas.read_csv(data/ershoufang.csv)突然报错FileNotFoundError。打印绝对路径后发现他误将data文件夹放在了父目录而代码在子目录执行——路径相对基准错了。绝对路径是唯一真理。6.3 验依赖用pipreqs生成精准 requirements.txt不要手写requirements.txt用工具自动生成pip install pipreqs pipreqs ./ --encodingutf8 --force参数说明--encodingutf8避免中文注释报错--force覆盖已有文件。生成的requirements.txt会精确列出pandas1.5.3,seaborn0.12.2等版本而非笼统的pandas1.0。答辩前在干净虚拟环境中pip install -r requirements.txt确保环境可重现。最后说个习惯我给每个毕业设计项目建一个checklist.md里面只写三件事——✅ 已验证ershoufang-clean-utf8-v1.1.csv能被pandas正确读取price_per_sqm列无字符串✅ 已确认dashboard.py导出的 HTML 在 Chrome/Firefox/Edge 均可交互✅ 已备份report.pptx为 PDF防止答辩电脑 Office 版本不兼容字体。这三件事做完答辩前一晚才能安心睡觉。希望帮到你。本文还有配套的精品资源点击获取