简介这份资源是面向高校学生的Python二手房数据分析与可视化系统完整项目源码适用于毕业设计、课程设计及期末大作业等场景对具备Python基础、希望完成数据分析类实战项目的学习者尤为合适。压缩包共156个文件约40MB包含18个py源码文件、15个html页面、11个js脚本以及65张png界面截图、18份csv数据集和若干配置与说明文档覆盖数据清洗、分析建模到前端展示的完整链路。项目已积累497人学习下载说明其参考价值获得一定认可。源码经过严格调试可直接运行读者能据此掌握二手房数据的清洗与多维度分析思路、可视化图表实现方式及系统整体目录组织也可作为二次开发与功能扩展的起点省去从零搭建的时间成本。1. 二手房数据分析可视化系统从一份毕业设计源码里拆出可复用的技术栈二手房数据分析和可视化系统是计算机毕业设计里被选得最多、也最容易被做“水”的一类题目。它表面上是“爬数据 画图”实际要跑通的是数据采集、清洗、存储、统计、可视化五段链路任何一段偷懒答辩时老师一句“数据从哪来、怎么保证真实”就能问穿。我见过太多同学直接拿一份源码改个标题就交结果环境跑不起来、图表全是假数据、数据库里只有十几条手工录入的记录。这篇笔记不讲空话就按一份典型的 Python 二手房数据分析可视化系统源码把环境搭建、数据采集清洗、可视化实现、避坑排查到进阶技巧完整走一遍。适合正在做基于 Python 的毕业设计、想真正把系统跑通而不是只交个压缩包的人。2. 环境搭建与项目结构把源码从压缩包跑到浏览器里拿到一份 Python 二手房数据分析可视化系统源码第一件事不是急着看代码而是先把运行环境对齐。毕业设计翻车最常见的原因就是环境版本不匹配源码作者用 Python 3.8 写的你本地装了 3.12某些库直接报错。下面按我实际跑项目的顺序拆开讲。2.1 Python 版本与依赖库的选型理由二手房数据分析系统通常涉及四类库数据采集用 requests 和 BeautifulSoup数据处理用 pandas 和 numpy可视化用 pyecharts 或 matplotlibWeb 展示用 Flask 或 Django。这四类库对 Python 版本都有隐性要求。pandas 在 2.0 之后对 numpy 版本卡得很死pyecharts 1.x 和 0.5.x 的 API 完全不兼容Flask 2.x 和 3.x 在路由注册上也有差异。我的建议是如果源码没有明确写版本优先用 Python 3.8 到 3.10 这个区间兼容性最好。不要一上来就装最新版Python 3.12 虽然新但很多毕业设计源码里的老库还没适配。安装 Python 时记得勾选“Add Python to PATH”否则后面命令行里敲 python 会提示找不到命令这是新手最常踩的第一个坑。依赖安装不要一个个 pip install先看源码根目录有没有 requirements.txt。有的话直接一条命令批量装# 进入项目根目录后执行-i 指定国内镜像源加速 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这条命令的逻辑是pip 读取 requirements.txt 里每一行声明的库和版本号逐个从镜像源下载安装。参数-i后面跟的是镜像地址国内直连官方源经常超时换清华源能快很多。如果源码没有 requirements.txt那就需要手动装常见的一套是pip install requests beautifulsoup4 pandas numpy pyecharts flask pymysql sqlalchemy装完之后一定要验证不要装完就当成功了# verify_env.py 环境自检脚本 import sys print(Python版本:, sys.version) # 确认版本在3.8-3.10区间 import pandas as pd import numpy as np import requests from bs4 import BeautifulSoup import pyecharts import flask print(pandas:, pd.__version__) print(numpy:, np.__version__) print(pyecharts:, pyecharts.__version__) print(flask:, flask.__version__) print(所有核心库导入成功)这段脚本的作用是逐个导入核心库并打印版本号。如果某个库导入报 ModuleNotFoundError说明没装上如果报版本冲突比如 numpy 和 pandas 不兼容就要回去调整版本。参数说明sys.version打印当前解释器版本__version__是各库自带的版本属性。跑通这个脚本环境这关才算过。2.2 项目目录结构与启动入口定位一份规范的二手房数据分析系统源码目录结构通常长这样目录/文件作用app.py / run.py项目启动入口Flask 应用实例在这里创建spider/数据采集模块存放爬虫脚本data/存放原始数据和清洗后的 CSV/Excelanalysis/数据分析模块pandas 统计逻辑templates/前端 HTML 模板static/CSS、JS、图表资源models.py数据库模型定义requirements.txt依赖清单启动入口一般是 app.py 或 run.py里面会有app.run()或if __name__ __main__:这样的代码。找到它在命令行里执行# 启动 Flask 应用debugTrue 方便看报错 python app.py启动后终端会输出一个本地地址通常是http://127.0.0.1:5000复制到浏览器打开就能看到系统界面。如果启动报端口被占用改一下端口号比如app.run(port5001)。如果报数据库连接失败说明还没配置数据库下一节讲。提示不要直接双击 .py 文件运行那样看不到报错信息。一定要在命令行或终端里跑报错会完整打印出来方便定位。3. 数据采集与清洗二手房数据从哪来、怎么变成能分析的格式可视化系统的图表好不好看是次要的数据真不真才是答辩老师最关心的。这一章讲数据从采集到入库的完整链路也是整个系统里最容易出问题的环节。3.1 二手房数据采集的常见做法与字段设计二手房数据采集常见做法是抓取公开的房产信息页面提取房源标题、总价、单价、面积、户型、楼层、朝向、小区名称、所在区域这些字段。这里要强调一点采集要遵守目标网站的 robots 协议控制请求频率不要给人家服务器造成压力这是基本的工程素养。一个典型的采集脚本结构如下# spider/ershoufang_spider.py import requests from bs4 import BeautifulSoup import pandas as pd import time import random # 请求头模拟浏览器避免被直接拒绝 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 } def parse_page(url): 解析单个列表页返回房源字典列表 resp requests.get(url, headersHEADERS, timeout10) resp.encoding utf-8 # 防止中文乱码 soup BeautifulSoup(resp.text, html.parser) items [] for li in soup.select(.sellListContent li): title li.select_one(.title a) total li.select_one(.totalPrice) unit li.select_one(.unitPrice) info li.select_one(.houseInfo) if not (title and total and unit and info): continue # 字段缺失的跳过避免脏数据 items.append({ title: title.get_text(stripTrue), total_price: total.get_text(stripTrue), unit_price: unit.get_text(stripTrue), house_info: info.get_text(stripTrue), }) return items def crawl(pages5): 翻页采集pages 控制采集页数 all_data [] base https://example.com/ershoufang/pg{} for p in range(1, pages 1): url base.format(p) all_data.extend(parse_page(url)) time.sleep(random.uniform(1, 3)) # 随机间隔降低请求频率 df pd.DataFrame(all_data) df.to_csv(data/raw_ershoufang.csv, indexFalse, encodingutf-8-sig) print(f采集完成共 {len(df)} 条) return df if __name__ __main__: crawl(pages5)逻辑说明parse_page负责解析单个页面用 CSS 选择器定位房源列表项逐个提取字段字段缺失的直接跳过。crawl负责翻页每页之间time.sleep随机停 1 到 3 秒这是控制请求频率的关键不要去掉。参数说明pages控制采集页数毕业设计演示采集 5 到 10 页足够timeout10是请求超时时间防止卡死encodingutf-8-sig写 CSV 时带 BOM 头Excel 打开不乱码。注意采集频率一定要控制time.sleep不能省。采集到的原始数据先存 CSV不要直接入库中间要经过清洗。3.2 数据清洗把“89.5平米”变成能计算的数字原始采集的数据是字符串比如“89.5平米”“320万”“3室2厅”这些没法直接做统计和画图必须清洗成数值型。这一步是很多毕业设计偷懒的地方直接把字符串塞进图表结果横坐标全是乱码。# analysis/clean_data.py import pandas as pd import re def clean(df): 清洗二手房原始数据 # 从 house_info 里拆出户型、面积、朝向等 df[area] df[house_info].str.extract(r(\d\.?\d*)平米).astype(float) df[layout] df[house_info].str.extract(r(\d室\d厅)) df[orientation] df[house_info].str.extract(r([东南西北])) # 总价去掉“万”字转数字 df[total_price_num] df[total_price].str.extract(r(\d\.?\d*)).astype(float) # 单价去掉“元/平米”转数字 df[unit_price_num] df[unit_price].str.extract(r(\d)).astype(float) # 丢弃关键字段为空的行 df df.dropna(subset[area, total_price_num, unit_price_num]) # 过滤异常值面积小于10或大于1000的单价明显离谱的 df df[(df[area] 10) (df[area] 1000)] df df[(df[unit_price_num] 1000) (df[unit_price_num] 200000)] print(f清洗后剩余 {len(df)} 条) return df if __name__ __main__: raw pd.read_csv(data/raw_ershoufang.csv) cleaned clean(raw) cleaned.to_csv(data/clean_ershoufang.csv, indexFalse, encodingutf-8-sig)逻辑说明用正则表达式从字符串里抽数字str.extract配合捕获组提取目标内容astype(float)转成浮点数。dropna丢弃关键字段缺失的行异常值过滤把明显不合理的记录剔除。参数说明面积区间 10 到 1000 平米、单价区间 1000 到 200000 元/平米这两个范围按实际城市调整一线城市单价上限要调高。清洗完的数据存成 clean_ershoufang.csv后续分析和可视化都读这个文件。3.3 数据入库MySQL 建表与 pandas 批量写入如果系统用数据库存储需要先建表再写入。表结构要和清洗后的字段对应-- 二手房房源表 CREATE TABLE house ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), total_price DECIMAL(10,2), unit_price DECIMAL(10,2), area DECIMAL(8,2), layout VARCHAR(50), orientation VARCHAR(20), region VARCHAR(50), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;建表时字符集用 utf8mb4否则中文小区名可能存进去变问号。字段类型上价格和面积用 DECIMAL 而不是 FLOAT避免精度丢失。然后用 pandas 批量写入# analysis/to_mysql.py import pandas as pd from sqlalchemy import create_engine # 数据库连接串替换成自己的账号密码 engine create_engine(mysqlpymysql://root:passwordlocalhost:3306/house_db?charsetutf8mb4) df pd.read_csv(data/clean_ershoufang.csv) # 只写入需要的列和表字段对应 df_out df[[title, total_price_num, unit_price_num, area, layout, orientation]] df_out.columns [title, total_price, unit_price, area, layout, orientation] df_out.to_sql(house, engine, if_existsappend, indexFalse) print(f写入 {len(df_out)} 条记录)逻辑说明create_engine建立数据库连接to_sql把 DataFrame 批量写入指定表。参数说明if_existsappend表示追加写入如果表已存在就往后加改成replace会先删表再重建调试阶段可以用正式演示前记得改回 append。连接串里的charsetutf8mb4不能省否则中文乱码。4. 可视化实现用 pyecharts 把二手房数据画成能讲的图可视化是毕业设计答辩时最直观的部分老师看的就是你的图能不能说明问题。二手房数据分析常见的图有区域均价柱状图、面积分布直方图、户型占比饼图、价格与面积散点图。用 pyecharts 做交互效果好还能导出 HTML 直接嵌到页面里。4.1 区域均价柱状图与户型占比饼图先看区域均价柱状图这是最能体现分析价值的图# analysis/chart_bar.py import pandas as pd from pyecharts.charts import Bar from pyecharts import options as opts df pd.read_csv(data/clean_ershoufang.csv) # 按区域分组算平均单价 grouped df.groupby(region)[unit_price_num].mean().round(0).sort_values(ascendingFalse) bar ( Bar() .add_xaxis(grouped.index.tolist()) .add_yaxis(平均单价(元/平米), grouped.values.tolist()) .set_global_opts( title_optsopts.TitleOpts(title各区域二手房平均单价), xaxis_optsopts.AxisOpts(name区域, axislabel_optsopts.LabelOpts(rotate30)), yaxis_optsopts.AxisOpts(name单价), ) ) bar.render(templates/chart_bar.html) print(柱状图已生成)逻辑说明groupby(region)按区域分组mean()算平均单价sort_values降序排列。参数说明rotate30让横坐标标签倾斜 30 度防止区域名太长挤在一起这就是热搜里“python画图横坐标太密集”的典型解法。render输出 HTML 文件可以直接用 iframe 嵌到 Flask 模板里。户型占比饼图同理# analysis/chart_pie.py import pandas as pd from pyecharts.charts import Pie from pyecharts import options as opts df pd.read_csv(data/clean_ershoufang.csv) layout_count df[layout].value_counts().head(8) # 取前8种户型 pie ( Pie() .add(, [list(z) for z in zip(layout_count.index, layout_count.values)]) .set_global_opts(title_optsopts.TitleOpts(title二手房户型分布)) .set_series_opts(label_optsopts.LabelOpts(formatter{b}: {c}套)) ) pie.render(templates/chart_pie.html)逻辑说明value_counts统计每种户型出现次数head(8)只取前 8 种避免饼图太碎。参数说明formatter{b}: {c}套控制标签显示格式{b}是名称{c}是数值。4.2 价格与面积散点图及 Flask 页面集成散点图用来看价格和面积的关系能发现异常房源# analysis/chart_scatter.py import pandas as pd from pyecharts.charts import Scatter from pyecharts import options as opts df pd.read_csv(data/clean_ershoufang.csv) data df[[area, total_price_num]].values.tolist() scatter ( Scatter() .add_xaxis([d[0] for d in data]) .add_yaxis(总价(万), [d[1] for d in data]) .set_global_opts( title_optsopts.TitleOpts(title面积与总价关系), xaxis_optsopts.AxisOpts(name面积(平米), type_value), yaxis_optsopts.AxisOpts(name总价(万)), ) ) scatter.render(templates/chart_scatter.html)逻辑说明把面积作为 x 轴、总价作为 y 轴每个点是一套房源。参数说明type_value让 x 轴按数值连续分布不加这个参数会变成按类别均分散点图就失真了。生成好的 HTML 图表在 Flask 路由里渲染# app.py 片段 from flask import Flask, render_template app Flask(__name__) app.route(/) def index(): return render_template(index.html) app.route(/chart/bar) def chart_bar(): return render_template(chart_bar.html) if __name__ __main__: app.run(debugTrue, port5000)逻辑说明每个图表一个路由render_template返回对应的 HTML。参数说明debugTrue开发阶段开启改代码自动重启正式部署要关掉避免暴露调试信息。5. 避坑与排查二手房系统跑不起来时先看这几条这一章是我带过几届毕业设计攒下来的血泪经验每条都按“现象 → 原因 → 解决”写遇到问题对号入座。第一条图表页面空白浏览器控制台报 404。现象是打开图表页什么都没有按 F12 看 Network 有一堆红色 404。原因是 pyecharts 生成的 HTML 引用了在线的 JS 资源网络不通或者路径不对。解决方法是把 pyecharts 的资源本地化或者确认 templates 目录下 HTML 文件确实生成了Flask 的模板路径配置正确。第二条中文显示成方块或乱码。现象是图表标题、坐标轴中文全是方框。原因是 matplotlib 默认字体不支持中文或者 CSV 读写编码不一致。解决方法是 matplotlib 里设置plt.rcParams[font.sans-serif] [SimHei]CSV 读写统一用utf-8-sig数据库字符集用 utf8mb4。第三条pandas 读取 CSV 报 UnicodeDecodeError。现象是pd.read_csv直接抛异常。原因是文件编码不是 UTF-8可能是 GBK。解决方法是加参数encodinggbk试一下或者用记事本另存为 UTF-8。采集和清洗阶段统一编码能避免这个问题。第四条数据库连接报 Access denied。现象是启动时连不上 MySQL。原因是账号密码不对或者没给远程访问权限。解决方法是检查连接串里的用户名密码本地开发用 root 加密码确认 MySQL 服务已启动端口 3306 没被占用。第五条采集脚本跑一次就被目标网站拒绝。现象是第一次能采第二次全部返回空或 403。原因是请求频率太高被识别了。解决方法是加大time.sleep间隔加随机 User-Agent控制单次采集页数。这是工程规范问题不是技术难题但很多同学图快把间隔设成 0结果直接翻车。6. 进阶技巧让毕业设计从“能跑”变成“能讲”系统跑通只是及格线答辩要拿高分得让老师看到你的思考。这里分享几个我常用的进阶技巧。第一个是数据校验。在分析模块里加一段统计描述把数据的最大值、最小值、均值、中位数打印出来答辩时能直接说“我的数据经过异常值过滤单价集中在 X 到 Y 之间”比空口说“数据真实”有说服力。# analysis/describe.py import pandas as pd df pd.read_csv(data/clean_ershoufang.csv) print(df[[area, total_price_num, unit_price_num]].describe())第二个是图表联动。pyecharts 支持把多个图放在一个 Page 里或者用 Grid 组合答辩演示时一页展示多张图比来回切页面流畅。第三个是加一个简单的筛选功能。在 Flask 路由里接收区域参数动态过滤数据再画图app.route(/chart/bar/region) def chart_bar_region(region): df pd.read_csv(data/clean_ershoufang.csv) if region ! all: df df[df[region] region] # 后续画图逻辑同上 return render_template(chart_bar.html)这样老师问“能不能只看某个区”你当场就能演示印象分直接拉满。最后一个习惯源码里所有硬编码的路径、账号、密码答辩前统一抽到一个 config.py 里别散落在各个文件。我见过有同学把数据库密码写在爬虫脚本里答辩时投屏被看得一清二楚场面很尴尬。把这些细节收干净系统才像个正经项目而不是一份抄来的作业。希望帮到你。本文还有配套的精品资源点击获取