资讯详情

资讯详情

Python房价预测系统实战:从58同城爬虫到K-means聚类与回归模型

简介这是一份面向计算机专业毕业设计场景的完整论文文档主题为基于Python的房价预测系统适合正在准备毕设选题、撰写论文或需要参考项目实现思路的本科生与指导教师。压缩包内仅含1个doc文件大小约2.55MB即论文正文本身涵盖摘要、绪论、系统设计与测试等完整章节。论文围绕网络爬虫、数据存储与机器学习预测展开详细介绍了使用Python爬取58同城房源信息、借助MySQL管理数据并运用线性回归、决策树、随机森林等算法建立房价预测模型的全过程同时涉及数据可视化与后台管理功能。目前已有373人学习下载读者可从中获取一份结构规范的毕业论文范本参考其章节组织、技术选型与实验验证思路为自身毕设写作与系统开发提供借鉴。1. 从一份本科毕设论文拆出的房价预测系统它到底能跑通什么如果你手头正缺一份能落地的房价预测项目参考或者被导师催着交计算机毕业设计论文这份基于 Python 的房价预测系统论文文档值得先拆开看看。它不是纯理论综述而是一套从数据采集到可视化大屏的完整链路用 Python 爬虫抓取 58 同城网的房源信息清洗后存入 MySQL再通过 K-means 聚类和回归模型做价格趋势分析最后用大屏展示区域、房型、价格分布。适合两类人一是需要快速搭出毕设原型的学生二是想了解房产数据从抓取到建模全流程的开发者。论文里把需求分析、系统设计、数据库表结构、测试用例都写全了相当于一份带文档的源码包说明书。但要注意论文里的代码片段是示意性的真正跑起来还得补不少工程细节下面我按实际复现的顺序拆一遍。2. 环境选型与爬虫链路Python 3.7 MySQL 5.7 怎么配才不翻车2.1 为什么是 Python 3.7 和 MySQL 5.7 这个组合论文里明确写了开发环境Windows 10、Python 3.7、PyCharm、MySQL 5.7。这个组合放在今天看不算新但胜在稳定尤其是 MySQL 5.7 对 SQLAlchemy 和 PyMySQL 的兼容性经过大量项目验证不会出现 MySQL 8.0 那种默认认证插件导致的连接报错。Python 3.7 虽然官方已停止维护但论文里用到的 requests、BeautifulSoup、pandas、sklearn 这些库在这个版本上都有成熟的 wheel 包pip 安装基本不会卡在编译环节。如果你用 Python 3.10 以上注意 sklearn 和 pandas 的版本要跟着升否则会出现 API 不兼容。我一般会建议直接建一个虚拟环境把依赖锁死避免污染全局。# 创建虚拟环境指定 Python 3.7 python -m venv house_env # 激活环境Windows house_env\Scripts\activate # 安装核心依赖版本按论文环境锁定 pip install requests2.28.2 pip install beautifulsoup44.11.2 pip install pandas1.3.5 pip install pymysql1.0.3 pip install sqlalchemy1.4.46 pip install scikit-learn1.0.2 pip install matplotlib3.5.3这段命令的逻辑是先隔离环境再按论文技术栈装库。requests 负责发 HTTP 请求BeautifulSoup 解析 HTMLpandas 做数据清洗pymysql 是 MySQL 驱动sqlalchemy 做 ORM 映射sklearn 跑 K-means 和回归matplotlib 出图。参数上注意 pandas 1.3.5 是支持 Python 3.7 的最后一个稳定版之一再高就要求 Python 3.8。如果你装的时候提示某个包找不到对应版本先去 PyPI 查一下该包的 Python 版本要求别硬装。2.2 聚焦型爬虫的请求策略与解析入口论文里把爬虫定性为聚焦型爬虫只抓房产相关数据不扩散到其他频道。这个定位很关键因为 58 同城的页面结构复杂列表页和详情页的字段分布不同如果一开始就想着全站抓很容易被反爬拦住。实际做法是先从列表页拿到房源链接和基础字段再进详情页补全面积、朝向、楼层等信息。请求头里必须带 User-Agent否则大概率返回 403。另外论文里没提代理池但真实抓取时单 IP 高频请求会被限流常见做法是加时间间隔或者用多个 UA 轮换。import requests from bs4 import BeautifulSoup import time import random # 目标列表页 URL实际使用时替换成你要抓的城市频道 base_url https://bj.58.com/chuzu/ # 请求头伪装UA 池可以多准备几个 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, } def fetch_page(url): 发送请求并返回页面内容失败时重试一次 try: resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() resp.encoding utf-8 return resp.text except requests.RequestException as e: print(f请求失败: {url}, 错误: {e}) time.sleep(random.uniform(2, 5)) return None def parse_list(html): 解析列表页提取房源标题、价格、链接 soup BeautifulSoup(html, html.parser) items [] # 列表项选择器根据实际页面结构调整这里用常见的 class 名示意 for node in soup.select(div.house-list-item): title node.select_one(h2 a) price node.select_one(div.price strong) link node.select_one(h2 a) if title and price and link: items.append({ title: title.get_text(stripTrue), price: price.get_text(stripTrue), detail_url: link.get(href), }) return items if __name__ __main__: html fetch_page(base_url) if html: data parse_list(html) print(f本页抓到 {len(data)} 条房源) for d in data[:3]: print(d) time.sleep(random.uniform(1, 3))这段代码分三层fetch_page 负责网络请求和异常兜底parse_list 负责 DOM 解析主流程控制节奏。参数上 timeout10 是防止某个请求卡死整个脚本random.uniform(1,3) 是模拟人工间隔。注意 select 里的 class 名是示意58 同城的页面 class 会变你得用浏览器开发者工具重新定位。如果返回的 HTML 里找不到目标节点先检查是不是被重定向到了验证页那种情况下需要换请求策略或者降低频率。2.3 数据清洗与 MySQL 入库的字段映射抓到原始数据后不能直接塞数据库价格字段带“元/月”后缀面积带“㎡”这些都要在入库前转成数值类型。论文里提到数据清洗和加工用例实际就是做类型转换、去重、空值过滤。MySQL 表设计上论文没有给出完整 DDL但按常见做法房源表至少要有标题、价格、面积、区域、房型、抓取时间这几个字段。用 SQLAlchemy 做 ORM 映射比裸写 SQL 更好维护也方便后续接 pandas 做分析。from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime from sqlalchemy.orm import declarative_base, sessionmaker from datetime import datetime import re # 数据库连接替换成你的用户名密码和库名 engine create_engine(mysqlpymysql://root:passwordlocalhost:3306/house_db?charsetutf8mb4) Base declarative_base() class House(Base): __tablename__ house_info id Column(Integer, primary_keyTrue, autoincrementTrue) title Column(String(255)) price Column(Float) # 单位元/月 area Column(Float) # 单位平方米 region Column(String(64)) layout Column(String(32)) # 如 2室1厅 crawl_time Column(DateTime, defaultdatetime.now) Base.metadata.create_all(engine) Session sessionmaker(bindengine) def clean_price(raw): 从 3500元/月 提取 3500.0 if not raw: return None match re.search(r(\d(\.\d)?), raw) return float(match.group(1)) if match else None def clean_area(raw): 从 89㎡ 提取 89.0 if not raw: return None match re.search(r(\d(\.\d)?), raw) return float(match.group(1)) if match else None def save_items(items): session Session() for item in items: price clean_price(item.get(price)) area clean_area(item.get(area)) if price is None: continue # 价格缺失的直接丢弃 house House( titleitem.get(title), priceprice, areaarea, regionitem.get(region, ), layoutitem.get(layout, ), ) session.add(house) session.commit() session.close()这里的关键点是 clean_price 和 clean_area 用正则提取数字避免字符串直接入库导致后续聚合报错。save_items 里对价格缺失的记录做了跳过处理这是数据质量的第一道过滤。注意 create_engine 里的 charset 要设成 utf8mb4否则中文标题会乱码。如果你遇到Access denied报错检查 MySQL 用户权限遇到Cant connect to local MySQL server先确认 MySQL 服务是否启动端口是不是默认 3306。3. 从 K-means 聚类到回归预测模型怎么选、参数怎么调3.1 K-means 做区域价格分档的实操论文里提到用 K-means 聚类算法分析房产价格趋势这个思路在毕设里很常见把房源按价格和面积聚成几档观察不同区域的分布。K-means 的坑主要在 K 值选择和特征标准化。如果价格范围是几千到几万面积是几十到几百不做标准化的话价格会主导距离计算面积几乎不起作用。常见做法是用 StandardScaler 先归一化再用肘部法或轮廓系数选 K。import pandas as pd from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler from sqlalchemy import create_engine engine create_engine(mysqlpymysql://root:passwordlocalhost:3306/house_db?charsetutf8mb4) df pd.read_sql(SELECT price, area FROM house_info WHERE price IS NOT NULL AND area IS NOT NULL, engine) # 特征标准化 scaler StandardScaler() X scaler.fit_transform(df[[price, area]]) # 肘部法看 K 值实际跑的时候打印 inertia inertias [] for k in range(2, 9): km KMeans(n_clustersk, random_state42, n_init10) km.fit(X) inertias.append(km.inertia_) print(fK{k}, inertia{km.inertia_:.2f}) # 假设选 K4打标签 km_final KMeans(n_clusters4, random_state42, n_init10) df[cluster] km_final.fit_predict(X) print(df.groupby(cluster)[[price, area]].mean())这段代码先读库再标准化然后循环 K 值看 inertia 下降拐点。n_init10 是让算法多跑几次取最优避免局部最优。random_state 固定后结果可复现。跑完看每个簇的均值和面积均值就能解释成“低价小户型”“高价大户型”这类标签。如果 inertia 曲线没有明显拐点说明数据分布比较均匀可以换 DBSCAN 或者直接按分位数切档。3.2 线性回归与随机森林的对比验证论文摘要里提到了线性回归、决策树、随机森林、神经网络等多种算法但毕设层面最稳的还是线性回归和随机森林。线性回归可解释性强能看出面积、区域对价格的影响方向随机森林拟合能力更强但容易过拟合。我一般会两个都跑用交叉验证比 MAE 和 R²。特征工程上区域要做独热编码房型可以拆成室和厅两个数值字段。from sklearn.model_selection import train_test_split, cross_val_score from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, r2_score from sklearn.preprocessing import OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 假设 df 里还有 region 和 layout 字段 df pd.read_sql(SELECT price, area, region, layout FROM house_info WHERE price IS NOT NULL, engine) df[room] df[layout].str.extract(r(\d)室).astype(float) df[hall] df[layout].str.extract(r(\d)厅).astype(float) df df.dropna(subset[area, room, hall]) X df[[area, room, hall, region]] y df[price] # 区域做独热编码数值字段保持原样 preprocessor ColumnTransformer( transformers[ (region_ohe, OneHotEncoder(handle_unknownignore), [region]), ], remainderpassthrough ) # 线性回归管道 lr_pipe Pipeline([ (prep, preprocessor), (model, LinearRegression()) ]) # 随机森林管道 rf_pipe Pipeline([ (prep, preprocessor), (model, RandomForestRegressor(n_estimators100, random_state42)) ]) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) for name, pipe in [(线性回归, lr_pipe), (随机森林, rf_pipe)]: pipe.fit(X_train, y_train) pred pipe.predict(X_test) print(f{name} MAE: {mean_absolute_error(y_test, pred):.2f}, R2: {r2_score(y_test, pred):.3f})这段代码用 Pipeline 把预处理和模型串起来避免数据泄露。OneHotEncoder 的 handle_unknownignore 是防止测试集出现训练集没见过的区域时报错。n_estimators100 是随机森林的默认起点树太少欠拟合太多训练慢。跑完对比 MAE 和 R²如果随机森林 R² 明显高于线性回归但 MAE 差距不大说明数据里有非线性关系但线性模型已经够用。毕设里两个都放上去答辩时能讲清楚选型理由。3.3 可视化大屏的数据接口设计论文里提到大屏显示房屋名称统计、价格趋势、房型统计、区域统计。这些图表的数据源就是 MySQL 里的聚合查询。前端可以用 ECharts 或 Pyecharts后端用 Flask 暴露几个 JSON 接口。关键是把 SQL 聚合写对比如按区域算均价、按房型算数量。from flask import Flask, jsonify from sqlalchemy import create_engine import pandas as pd app Flask(__name__) engine create_engine(mysqlpymysql://root:passwordlocalhost:3306/house_db?charsetutf8mb4) app.route(/api/region_price) def region_price(): sql SELECT region, AVG(price) AS avg_price, COUNT(*) AS cnt FROM house_info WHERE price IS NOT NULL GROUP BY region ORDER BY avg_price DESC LIMIT 10 df pd.read_sql(sql, engine) return jsonify(df.to_dict(orientrecords)) app.route(/api/layout_count) def layout_count(): sql SELECT layout, COUNT(*) AS cnt FROM house_info WHERE layout IS NOT NULL AND layout ! GROUP BY layout ORDER BY cnt DESC LIMIT 8 df pd.read_sql(sql, engine) return jsonify(df.to_dict(orientrecords)) if __name__ __main__: app.run(debugTrue, port5000)两个接口分别返回区域均价排行和房型数量分布前端拿到 JSON 后直接喂给 ECharts 的柱状图和饼图。debugTrue 只在开发时用部署时要关掉。如果接口返回空数组先检查数据库里有没有数据再检查 SQL 的 WHERE 条件是不是过滤太狠。跨域问题可以在 Flask 里加 CORS 头或者前端用同源部署。4. 避坑与排查爬虫被封、入库乱码、模型过拟合的常见翻车点4.1 请求返回 403 或验证码页面现象脚本跑了几页后突然返回 403或者 HTML 里出现“访问验证”字样。原因58 同城对单 IP 高频请求有风控UA 固定也容易被识别。解决降低请求频率到 3 秒以上准备多个 User-Agent 轮换必要时加 Referer 头。如果已经触发验证换 IP 或等一段时间再跑。别想着硬刚风控策略是动态的。4.2 MySQL 中文乱码或 emoji 报错现象入库后标题显示问号或者插入带 emoji 的房源描述时报Incorrect string value。原因数据库或表的字符集不是 utf8mb4。解决建库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci连接串里加?charsetutf8mb4。已经建好的表可以用ALTER TABLE house_info CONVERT TO CHARACTER SET utf8mb4改。注意 MySQL 5.7 默认字符集是 latin1不改必乱码。4.3 价格字段清洗后大量为空现象入库后发现很多记录的 price 是 NULL但原始页面明明有价格。原因页面上的价格可能带“面议”或者“暂无”正则匹配不到数字。解决在 clean_price 里加判断匹配不到就返回 None入库时跳过。如果“面议”占比很高说明目标城市频道的数据质量差换个频道或者换数据源。别硬填 0会污染后续统计。4.4 随机森林 R² 很高但预测新数据偏差大现象训练集 R² 0.95测试集 R² 0.6新数据预测离谱。原因过拟合树太深或者特征太少。解决限制 max_depth 到 5-10增加 min_samples_leaf 到 5 以上或者换线性回归。另外检查有没有把价格相关的字段误当特征比如“单价”这种字段如果是从价格算出来的放进去就是标签泄露。4.5 Flask 接口返回 500 但日志看不清现象前端请求接口报 500Flask 控制台只显示一行错误。原因pandas 读 SQL 时字段类型不匹配或者数据库连接断了。解决在接口里加 try-except 把异常打印出来检查 SQL 里的字段名和表结构是否一致。连接池用pool_pre_pingTrue防止长时间空闲后连接失效。调试阶段把 debugTrue 打开能看到完整堆栈。5. 论文文档的复用技巧怎么把一份毕设改造成能写进简历的项目这份论文文档最大的价值不是代码本身而是它把需求分析、系统设计、数据库设计、测试用例的框架都搭好了。如果你要拿它做毕设直接照搬结构会被查重但可以换数据源、换模型、换前端框架。比如把 58 同城换成链家或者安居客把 K-means 换成 DBSCAN把 Flask 换成 FastAPI把 ECharts 换成 Pyecharts 直接出图。这样论文的骨架还在但内容全是新的。我一般会建议先跑通最小闭环抓 100 条数据入库跑一个线性回归出一个柱状图。这个闭环跑通后再加功能不然一开始就铺大摊子很容易卡在某个环节放弃。论文里的测试章节可以改成你自己的测试记录比如爬虫成功率、数据清洗前后对比、模型 MAE 变化这些数据比空泛的“功能测试通过”有说服力得多。还有一个技巧是把数据库表结构导出成 SQL 文件连同爬虫脚本、模型训练脚本、Flask 接口一起打包作为附件提交。答辩时老师问“数据怎么存的”你直接打开 SQL 文件给他看字段和索引比口头描述强。论文里的图 4.1 系统框架模型图可以重画成三层架构图标注清楚数据层、业务层、表现层各自用了什么技术这样既符合论文要求又能体现工程思维。从那以后我每次拿到一份毕设论文都先跑通它的最小数据链路再决定要不要深入。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →