简介一份基于Python的二手房数据采集与可视化分析毕业设计参考方案面向计算机、电子信息、数学等专业的学生适用于课程设计、期末大作业或毕业设计选题需具备一定编程基础以进行二次开发。资源从数据采集与清洗入手提供了多版本二手房交易CSV数据集原始数据、UTF-8/ANSI编码、v1.1清洗版辅以Python脚本完成抓取、去重、格式转换和统计并通过HTML/JS页面与PNG图表呈现可视化分析结果整体串起数据获取、处理、展示的完整链路。压缩包共155个文件涵盖Python脚本、数据集、可视化页面、配置文件、字体以及答辩PPT等体积约35.13MB目录结构清楚方便按需查阅。目前已有840人学习下载可作为完整毕业设计题目的参考模板帮助快速理解项目架构和开发流程也能为后续扩展功能提供基础。1. 二手房数据采集与可视化这套源码包的真正价值不在PPT一套二手房数据采集与可视化分析的毕业设计手里握有近两万条真实房源原始数据、清洗后的多版本CSV、完整的Python爬虫与可视化源码还有答辩PPT。很多人下载后第一反应是打开PPT改个名字但这样浪费了它最有价值的部分——数据从乱到整、再从整到图的过程恰恰是面试和答辩时最容易被追问的细节。这套资源适合需要快速理解数据采集-清洗-分析-可视化全链路的学生也适合想复现一个完整数据项目的初级工程师。它不是什么高深算法但把工程规范补齐之后完全可以作为一份拿得出手的课程设计或毕业设计。2. 数据集结构与编码陷阱先别急着跑爬虫把这六个CSV读明白很多同学拿到压缩包就去找爬虫代码结果发现报错半天是在CSV编码上。源码包里其实给了两套原始数据和三套清洗后数据命名里的utf8和ansi直接对应着Windows和Linux/macOS环境下的常见编码习惯。我一般建议先把数据读进DataFrame再决定要不要重新采集因为现成的2万条数据往往比你自己爬的更快、更干净。2.1 六个CSV文件分别是什么先摸清家底文件名大体可以分成三组原始数据ershoufang-origin-、扩充数据ershoufang - 20000.csv、清洗后数据ershoufang-clean-。清洗数据又分v1.1版本和两种编码。各文件用途如下表文件作用常见问题ershoufang-origin-utf8.csvUTF-8编码的原始房源数据直接用pandas读取没问题ershoufang-origin-ansi.csvANSI/GBK编码的原始数据Windows下Excel打开正常Python需指定encodingershoufang.csv较早版本的原始数据字段可能与新版不一致ershoufang - 20000.csv扩充到2万条的样本文件名有空格路径处理注意ershoufang-clean-utf8-v1.1.csv清洗后UTF-8版本推荐作为分析和可视化的主数据ershoufang-clean-ansi-v1.1.csv清洗后ANSI版本与上一份内容相同编码不同这里的核心经验是先确认编码再谈分析。推荐直接用clean-utf8版本因为Python生态默认UTF-8省去转换环节也避免在Windows上打开文件时出现乱码。2.2 编码问题Python读取时的两种典型坑如果你是Windows用户用pandas默认参数读取ansi文件会报UnicodeDecodeError。常见做法是显式指定编码import pandas as pd # 读取ANSI编码的CSV注意不能用utf-8 df_ansi pd.read_csv(ershoufang-origin-ansi.csv, encodinggbk) # 读取UTF-8编码的CSV df_utf8 pd.read_csv(ershoufang-clean-utf8-v1.1.csv, encodingutf-8-sig) print(df_utf8.shape) print(df_utf8.head())逻辑说明第一行用encodinggbk是为了兼容Windows中文版导出的ANSI文件GBK是ANSI在简体中文系统下的具体实现。第二行用utf-8-sig而不是utf-8是因为很多CSV文件带BOM头utf-8-sig会自动剥离BOM避免列名变成\ufeff小区。shape输出行列数head预览前5行用于确认字段是否完整。参数说明read_csv最常见的参数除了encoding还有sep。默认分隔符是逗号如果源文件用TAB分隔需要设置sep\t。另外nrows可以控制只读取前N行做快速探查比如pd.read_csv(..., nrows100)避免把2万条全部载入内存。dtype可以指定列类型比如把总价读成float防止大数变成科学计数法。注意清洗后的CSV虽然命名为clean但不同来源的清理程度不同建议读取后先执行2.3节的步骤再进行分析。2.3 清洗字段的常规操作缺失值、重复值、类型转换从文件名看清洗版本已经处理过一轮但拿到手后还是要自己过一遍因为答辩时肯定会问清洗逻辑是什么。常规流程是import pandas as pd import numpy as np df pd.read_csv(ershoufang-clean-utf8-v1.1.csv, encodingutf-8-sig) # 1. 去重 df df.drop_duplicates(subset[小区, 户型, 面积, 总价]) # 2. 处理缺失值面积、总价为空的直接删除其他文本列填空字符串 df df.dropna(subset[面积, 总价]) df df.fillna({朝向: , 装修: , 楼层: }) # 3. 类型转换把面积、总价转成数值类型 df[面积] pd.to_numeric(df[面积], errorscoerce) df[总价] pd.to_numeric(df[总价], errorscoerce) df df.dropna(subset[面积, 总价]) # 4. 新增单价列总价万* 10000 / 面积平方米 df[单价] df[总价] * 10000 / df[面积] print(df.info())逻辑说明drop_duplicates用关键字段判重subset指定依据哪几列这里选了小区、户型、面积、总价四个字段如果四者完全相同就认为是同一条记录。dropna和fillna组合使用面积、总价这类数值关键字段缺失时直接删行因为无法估算朝向、装修、楼层这类文本字段缺失时填空字符串这样后续groupby不会因为None报错。pd.to_numeric里errorscoerce会把无法转换的字符串变成NaN比如暂无数据这种脏值之后再删一次。参数说明to_numeric的errors参数有三个取值ignore返回原列、coerce转NaN、raise抛异常。实战中coerce最常用因为它能快速定位脏数据并且配合dropna二次清理。单价列是重新计算出来的而不是直接取原始数据里的单价因为网上部分房源标注的单价是四舍五入的自己算一遍能验证原数据是否有计算错误。清洗完之后df.info()会告诉你每列的非空数量和类型。如果数据量从2万掉到1万5那说明原始数据的脏数据量不小这部分可以写进报告里作为数据质量分析的依据。到这里分析用的DataFrame就准备好了。3. 数据采集与解析从链接到结构化字段的完整链路数据清洗只是起点如果答辩老师追问这些原始数据怎么来的你就需要能讲清楚采集环节。这套源码里应该有爬虫部分即使没有下面这套基于requests和BeautifulSoup的流程也是行业里最常见的选择。3.1 目标分析与字段映射二手房网站页面结构通常是列表页详情页列表页能看到小区、户型、面积、总价详情页才有朝向、装修、楼层、建造年代。常见的字段映射表如下列表页字段详情页字段存储列名标题小区户型小区、户型总价单价总价、单价面积面积面积位置楼层楼层-朝向/装修/年代朝向、装修、年代经验是列表页先爬到详情页链接再进入详情页抓扩展字段。全量抓详情页会慢但信息密度高毕业设计的数据量一般几千到两万条完全可以承受。3.2 爬虫代码与反爬应对核心采集代码类似下面这样import requests from bs4 import BeautifulSoup import pandas as pd import time headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://xxx.xx.com/, Accept-Language: zh-CN,zh;q0.9 } def fetch_list(page): url fhttps://xxx.xx.com/sale/pg{page}/ resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) items [] for li in soup.select(div.info): title li.select_one(a.title).text.strip() info li.select_one(p) # 实际结构按页面调整 items.append({title: title, info: info.text.strip() if info else }) return items all_data [] for page in range(1, 101): data fetch_list(page) if not data: break all_data.extend(data) time.sleep(2 page % 3) # 动态限速逻辑说明requests.get发送GET请求headers里的User-Agent必须设置很多站点对Python默认UA直接返回403Referer可以带上模拟从首页跳转过来的访问。resp.encoding手动指定编码避免中文乱码。BeautifulSoup中的select使用CSS选择器定位列表项select_one取第一个匹配元素text.strip()去掉首尾空白。参数说明time.sleep(2 page % 3)是动态限速前3页间隔2秒第4页起间隔变成3秒这样请求间隔不固定比固定time.sleep(2)更不容易被识别为机器。timeout10表示10秒无响应就抛异常配合try...except可以跳过失效页面。for page in range(1, 101)是抓前100页每页假设30条能拿3000条数据足够做分析如果目标是2万条需要调整页数并处理分页规则。实战中列表页的选择器div.info和a.title需要根据目标网站调整。在写爬虫之前先用浏览器开发者工具查看一下列表项所在的DOM节点这一步比写代码更重要。3.3 反爬的底线与数据落盘遇到验证码、封IP、滑块等不要硬刚。常见的做法是降低请求频率、在请求头里带上Cookie、用requests.Session()保持会话。Session的好处是会自动保存服务端设置的Cookie模拟同一个浏览器的连续访问。示例import requests session requests.Session() session.headers.update(headers) def fetch_with_retry(url, retries3): for i in range(retries): try: resp session.get(url, timeout10) if resp.status_code 200: return resp except Exception as e: print(f第{i1}次重试: {e}) time.sleep(3) return None参数说明retries3表示失败后最多重试3次session.headers.update(headers)把公共请求头一次性设置好后续请求自动携带。如果网站要求登录还可以在session.cookies里手动添加登录后的Cookie。如果仍然被反爬最稳妥的方案是直接从CSV数据集入手因为这份资源里的ershoufang - 20000.csv已经是现成的采集结果。最后数据落盘记得统一编码pd.DataFrame(all_data).to_csv(ershoufang-new.csv, indexFalse, encodingutf-8-sig)indexFalse避免把行号写进文件encodingutf-8-sig让Excel直接打开不乱码。这里再强调一次落盘编码与读取编码必须一致否则换机器后再读又会踩2.2节的坑。4. 可视化分析把房价数据的分布规律用图表讲清楚数据采集和清洗做完就到了最容易被看见的环节——可视化。毕业设计的答辩现场评委对你的爬虫和清洗可能无感但一张清晰的图能立刻解释你的数据结论。可视化工具我推荐pyecharts因为交互性强、导出方便图表也现代如果环境没装用matplotlib也能完成同样的分析。4.1 区域均价对比柱状图先来看不同区域的二手房均价这是所有二手房价分析里最基础的一个视角。原始数据里通常有位置或区域列可能包含朝阳-望京这类文本。用groupby聚合后画柱状图import pandas as pd from pyecharts.charts import Bar from pyecharts import options as opts df pd.read_csv(ershoufang-clean-utf8-v1.1.csv, encodingutf-8-sig) # 提取前两位作为区域如朝阳-望京 - 朝阳 df[区域] df[位置].str[:2] # 按区域计算平均总价万元 region_price df.groupby(区域)[总价].mean().sort_values(ascendingFalse) bar Bar() bar.add_xaxis(region_price.index.tolist()) bar.add_yaxis(平均总价万元, region_price.round(1).tolist()) bar.set_global_opts( title_optsopts.TitleOpts(title各区域二手房平均总价), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate45)), yaxis_optsopts.AxisOpts(name总价万元) ) bar.render(region_price.html)逻辑说明str[:2]假设位置列以区名-板块命名切前两位就是区域如果你的数据格式不同换成str.split(-)[0]更准确。groupby(区域)[总价].mean()计算每个区域的平均总价sort_values(ascendingFalse)降序排列让高的排前面。bar.render(region_price.html)生成一个可交互的HTML文件双击就能打开。参数说明rotate45是X轴标签旋转45度防止区域名重叠。add_yaxis里的round(1)把数值保留一位小数。pyecharts默认生成HTML路径相对于当前工作目录。如果用matplotlib同样的聚合结果画出来是静态图适合直接嵌入论文。两种工具选一种即可不需要都写。4.2 面积与总价散点图拟合趋势线第二个常见分析是面积与总价的关系。很多二手房数据里面积越大总价越高但不同户型的单价差异很大。用散点图叠加拟合线可以直观看到这个规律import matplotlib.pyplot as plt import numpy as np data df.dropna(subset[面积, 总价]) data data[data[面积] 200] # 去掉面积异常大的值 x data[面积].values y data[总价].values # 线性拟合 k, b np.polyfit(x, y, 1) y_fit k * x b plt.figure(figsize(10, 6)) plt.scatter(x, y, alpha0.3, s10, label房源样本) plt.plot(x, y_fit, r-, labelf拟合线: y{k:.1f}x{b:.1f}) plt.xlabel(面积平方米) plt.ylabel(总价万元) plt.title(面积与总价关系散点图) plt.legend() plt.grid(alpha0.4) plt.show()逻辑说明np.polyfit(x, y, 1)用最小二乘法拟合一次多项式返回斜率k和截距b。alpha0.3让散点半透明避免点密度太高看不清分布s10控制点的大小。data[面积] 200是过滤异常值实际数据里可能有面积很大的别墅不过滤会把坐标轴拉得更长让密集区域挤在一起。参数说明alpha取值范围0到1越大越不透明。plt.grid(alpha0.4)给网格线加透明度让网格不抢散点的视觉权重。figsize(10,6)控制画布宽高这个比例在PPT和论文里都比较合适。如果拟合结果不是线性的可以改成np.polyfit(x, y, 2)做二次拟合看曲线是否更贴近数据。4.3 用透视表交叉验证户型与朝向除了画图还可以做一个交叉表看户型和朝向对单价的影响。用pivot_table生成紧凑的表格再配合热力图展示pivot df.pivot_table( index户型, columns朝向, values单价, aggfuncmean, fill_value0 ) print(pivot.round(1))pivot_table的index是行索引columns是列索引values是要聚合的列aggfunc指定聚合函数为平均值。fill_value0用0填充空值避免热力图出现NaN。输出可能形如朝向南北东西一室一厅45123.039800.042000.038500.0两室一厅52000.047000.049900.045000.0三室一厅61000.055000.058000.053000.0这个表可以直接写进报告比截图更专业。如果想让可视化更完整可以用seaborn的heatmap把透视表渲染成热力图颜色深浅代表单价高低一眼就能看出朝南的户型普遍更贵。5. 答辩加分与工程验证三个让评委觉得你真正做过项目的细节这套资源里既有数据又有PPT但如果只照着PPT讲很容易在提问环节露怯。我建议在答辩前做三件小事它们能让你的项目从跑了别人代码变成自己真的理解了这个项目。5.1 用数据质量报告证明清洗有效在报告里加一页清洗前后对比列出清洗前行数、去重后行数、缺失值占比最高的字段、类型转换修正的错误值数量。用下面脚本生成origin pd.read_csv(ershoufang-origin-utf8.csv, encodingutf-8-sig) clean pd.read_csv(ershoufang-clean-utf8-v1.1.csv, encodingutf-8-sig) print(f原始数据量: {origin.shape[0]}) print(f清洗后数据量: {clean.shape[0]}) print(f删除比例: {(1 - clean.shape[0]/origin.shape[0]):.1%}) print(缺失值统计:\n, clean.isnull().sum())这段代码的意义在于数字比我做了清洗这句话有说服力。评委看到删除比例和缺失值统计自然就会问清洗逻辑对这个问题2.3节的代码已经做好了准备。5.2 验证可视化结论的稳定性一个常见的答辩问题是你这个区域均价差异是真实的还是样本偏差回答思路是把数据随机抽样一半重新画图如果趋势不变则说明结论稳定。可以写一个重采样循环import pandas as pd import numpy as np clean pd.read_csv(ershoufang-clean-utf8-v1.1.csv, encodingutf-8-sig) sample clean.sample(frac0.7, random_statenp.random.RandomState(1)) # 重复4.1的聚合步骤对比排名前5区域是否一致 sample_price sample.groupby(区域)[总价].mean().sort_values(ascendingFalse) print(sample_price.head(5))frac0.7表示抽取70%样本random_state固定随机种子保证结果可复现。如果两次结论一致就说明你的卖点充足。如果排名有变化可以分析差异来自哪些样本这也是一个不错的讨论点。5.3 源码目录重构思路压缩包里的源码可能比较发散为了体现工程能力建议按标准数据项目结构重新组织project/ ├── data/ # 原始与清洗CSV ├── crawler/ # 爬虫代码 ├── analysis/ # 清洗与聚合脚本 ├── dashboard/ # 可视化HTML与代码 └── README.md # 环境依赖与运行说明在README里写明Python版本、依赖库及安装命令pip install pandas requests beautifulsoup4 pyecharts matplotlib本文还有配套的精品资源点击获取