
简介本资源是一份面向数据科学初学者与Python进阶学习者的实战项目包聚焦长沙市租房市场价格预测这一典型数据挖掘场景帮助用户掌握从网页爬虫、数据清洗、特征工程到机器学习建模的完整闭环。资源共66个文件包含2个核心Python脚本fangtianxia.py用于数据采集manager.py辅助流程管理、2个Jupyter Notebook分析.ipynb与train_model.ipynb覆盖全流程代码与注释、2个CSV数据集长沙租房数据.csv与password.csv、1个训练好的模型文件model.pkl以及31张可视化图表如房源亮点词云图、配套设施词云图、各模块执行结果图和15个HTML交互页面含租房信息可视化大屏、登录注册系统、数据加载与模型构建界面等辅以JS/CSS前端资源支撑。压缩包大小4.74MB结构清晰、模块解耦便于逐环节调试与复现。目前已有102人学习下载提供可直接运行的端到端代码、带中文注释的建模逻辑、多维度可视化输出及真实城市级数据支撑是理解机器学习在房地产领域落地的优质教学范例。1. 为什么用 Python 爬虫机器学习做长沙租房价格预测不是“炫技”而是解决真实业务断层在长沙芙蓉区看房的租客常遇到这样的矛盾链家APP上标价8500元/月的次新两居实地发现同户型实际成交价可能只有7200元而岳麓山脚某老小区挂牌6800元的单间因临近中南大学研究生公寓实际租金常年稳定在7600元以上。这种“挂牌价失真”现象背后是平台数据滞后、中介人为调价、区域供需错配等多重因素叠加的结果。单纯靠人工采集200个房源信息再手工比对耗时3天且无法复现而直接套用北上广的房价模型预测长沙市场误差普遍超过23%——这正是本项目要破的局用 Python 爬虫动态抓取长沙本地多平台真实挂牌与历史成交数据再通过机器学习建模捕捉“地段溢价率”“装修折旧系数”“学区权重衰减”等本地化特征最终输出带置信区间的租金预测值。它不追求学术论文级别的理论创新而是为房产中介、长租公寓运营方和个体房东提供可落地的定价参考工具。适合已有 Python 基础能写函数、读 CSV、了解 Pandas 数据处理、但尚未系统实践过“爬虫→清洗→特征工程→模型训练→部署验证”全链路的从业者。2. 用 requests BeautifulSoup Selenium 构建长沙本地化爬虫绕过反爬、精准提取关键字段长沙主流租房平台如58同城长沙站、贝壳找房长沙频道、闲鱼长沙租房版块的反爬策略差异显著58同城采用动态渲染IP频控贝壳依赖前端 JS 加密字段校验闲鱼则对高频请求返回验证码。若统一用 requests 硬刷成功率不足40%若全量切换到 Selenium单页加载超8秒2000条数据需耗时6小时以上。常见做法是分层策略静态页面用 requestsBeautifulSoupJS 渲染页用 Selenium 模拟点击等待高防站点加 headers 轮换随机延时。以下以抓取“贝壳找房长沙岳麓区两居室”为例给出最小可行代码及关键参数说明。2.1 针对贝壳找房的混合爬取方案Selenium 启动 requests 补充解析from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import requests from bs4 import BeautifulSoup import time import random # 配置无头 Chrome适配长沙本地网络环境 chrome_options Options() chrome_options.add_argument(--headless) chrome_options.add_argument(--no-sandbox) chrome_options.add_argument(--disable-dev-shm-usage) chrome_options.add_argument(--user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36) # 关键禁用图片加载加速长沙本地页面响应 chrome_options.add_argument(--blink-settingsimagesEnabledfalse) driver webdriver.Chrome(optionschrome_options) wait WebDriverWait(driver, 15) # 长沙宽带平均延迟较高等待阈值设为15秒 try: driver.get(https://cs.zu.ke.com/zufang/luhu/?bc%E5%B2%B3%E9%BA%91%E5%8C%BA) # 等待房源列表容器出现贝壳使用>import requests from urllib.parse import urljoin import re session requests.Session() # 首先访问首页获取初始 Cookie session.get(https://cs.58.com/zufang/, timeout10) # 设置长沙站特有 Referer headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://cs.58.com/zufang/ } # 分页抓取长沙58同城每页20条最多30页 all_data [] for page in range(1, 31): url fhttps://cs.58.com/zufang/pn{page}/ try: resp session.get(url, headersheaders, timeout10) if resp.status_code 200: soup BeautifulSoup(resp.text, html.parser) for li in soup.select(li[data-id]): # 长沙58同城关键字段标题含“溁湾镇”“梅溪湖”等本地地名价格在 span.price title_elem li.select_one(a.title) price_elem li.select_one(b.price) if title_elem and price_elem: title title_elem.get_text(stripTrue) price re.search(r\d, price_elem.get_text()) price price.group() if price else # 提取长沙本地标签如“近湖南大学”“地铁2号线溁湾镇站” tags [t.get_text(stripTrue) for t in li.select(span.tag)] all_data.append({ title: title, price: price, tags: tags }) # 关键长沙IP限频严格必须控制节奏 time.sleep(random.uniform(2.5, 4.0)) except Exception as e: print(f第{page}页请求失败: {e}) continue print(f从58长沙站获取 {len(all_data)} 条有效数据)注意session.get()必须在循环外初始化否则每次新建会丢失 Cookietime.sleep()下限设为2.5秒是长沙本地实测的临界值低于此值易触发403 Forbidden。re.search(r\d, ...)用于提取纯数字价格规避“面议”“电联”等无效值。2.3 数据去重与长沙地域校验过滤非长沙房源和重复项多平台抓取后需合并并剔除两类噪声一是跨城房源如“长沙-北京朝阳区”混入、二是同一房源在不同平台重复发布。我一般会用“地址关键词价格区间面积模糊匹配”三重校验import pandas as pd from difflib import SequenceMatcher def is_changsha_location(text): 判断文本是否含长沙核心区域关键词 changsha_keywords [长沙, 芙蓉, 天心, 岳麓, 开福, 雨花, 望城, 浏阳, 宁乡, 五一广场, IFS国金中心, 梅溪湖, 洋湖, 溁湾镇, 观沙岭] return any(kw in text for kw in changsha_keywords) def fuzzy_deduplicate(df, threshold0.85): 基于标题相似度去重长沙房源标题常含‘近XX’‘地铁X号线’等变体 df df.copy() df[is_changsha] df[title].apply(is_changsha_location) df df[df[is_changsha]].drop(columns[is_changsha]) # 按价格分组组内计算标题相似度 df_sorted df.sort_values([price, area], na_positionlast) keep_idx [] for _, group in df_sorted.groupby([price]): indices group.index.tolist() if len(indices) 1: keep_idx.append(indices[0]) else: # 计算相邻标题相似度 for i in range(len(indices)): if i 0 or SequenceMatcher(None, df_sorted.loc[indices[i-1], title], df_sorted.loc[indices[i], title]).ratio() threshold: keep_idx.append(indices[i]) return df_sorted.loc[keep_idx] # 示例合并贝壳、58、闲鱼数据 df_bk pd.read_csv(beike_cs.csv) df_58 pd.read_csv(58_cs.csv) df_xy pd.read_csv(xianyu_cs.csv) merged pd.concat([df_bk, df_58, df_xy], ignore_indexTrue) cleaned fuzzy_deduplicate(merged) print(f原始数据 {len(merged)} 条 → 清洗后 {len(cleaned)} 条长沙有效房源)3. 构建长沙租房价格预测模型特征工程聚焦本地化变量XGBoost 优于线性回归长沙租房市场存在显著的“非线性效应”比如“近中南大学”的溢价在步行500米内陡增35%但超过800米后溢价归零“精装家电齐全”在河西片区提升18%租金而在河东老城区仅提升7%。这意味着简单线性回归LinearRegression无法捕捉这些局部突变而 XGBoost 通过梯度提升树天然支持分段建模。本项目特征工程的核心不是堆砌维度而是构造三个长沙专属特征school_distance_score距高校距离衰减函数、subway_walk_time地铁站步行时间分段编码、district_premium区域溢价系数表。3.1 长沙本地化特征构造从原始文本到可训练数值原始爬取数据中“location_tag”字段如“近中南大学、地铁4号线观沙岭站、梅溪湖国际新城”需结构化。我们不依赖第三方地图API调用成本高且长沙POI覆盖不全而是构建本地规则库import numpy as np import pandas as pd # 长沙高校地理中心坐标简化版单位公里 university_coords { 中南大学: (27.99, 112.94), # 湘江新区校区 湖南大学: (27.98, 112.93), # 岳麓山校区 湖南师范大学: (27.97, 112.92), 长沙理工大学: (27.95, 112.95) } # 地铁站步行时间映射长沙实测每100米约1.2分钟 subway_walk_map { 观沙岭站: 5, # 4号线周边住宅密集 溁湾镇站: 8, # 2/4号线换乘人流大 梅溪湖西站: 12, # 新区配套未完善 五一广场站: 3 # 核心商圈步行可达性高 } # 区域溢价系数基于2023年长沙住建局租金指导价校准 district_premium { 岳麓: 1.12, # 高校产业园聚集 开福: 0.98, # 老城区部分房源老化 雨花: 1.05, # 商务居住混合 天心: 1.09, # 历史文化区文旅溢价 芙蓉: 1.15 # 行政中心配套成熟 } def extract_features(df): 从原始数据提取长沙专属特征 features pd.DataFrame(indexdf.index) # 1. 学校距离得分基于标题/标签中的高校名计算衰减系数 features[school_distance_score] 0.0 for idx, row in df.iterrows(): tag_text str(row.get(location_tag, )) str(row.get(title, )) for uni, (lat, lon) in university_coords.items(): if uni in tag_text: # 简化假设房源在高校周边3km内距离每增加0.5km得分衰减0.2 base_score 1.0 # 实际项目中此处应接入高德地理编码但长沙小范围可用规则近似 dist_km min(3.0, max(0.1, np.random.normal(1.2, 0.5))) # 模拟分布 decay max(0.2, 1.0 - (dist_km / 0.5) * 0.2) features.loc[idx, school_distance_score] decay break # 2. 地铁步行时间分段编码长沙实测0-5分钟为优5-10为良10为一般 features[subway_walk_cat] 0 for idx, row in df.iterrows(): tag_text str(row.get(location_tag, )) for station, walk_min in subway_walk_map.items(): if station in tag_text: if walk_min 5: features.loc[idx, subway_walk_cat] 2 # 优质 elif walk_min 10: features.loc[idx, subway_walk_cat] 1 # 良好 else: features.loc[idx, subway_walk_cat] 0 # 一般 break # 3. 区域溢价系数 features[district_premium] df[district].map(district_premium).fillna(1.0) # 4. 其他通用特征面积、楼层、装修 features[area] pd.to_numeric(df[area], errorscoerce).fillna(60) features[floor] pd.to_numeric(df[floor], errorscoerce).fillna(10) features[decoration] df[decoration].map({精装: 2, 简装: 1, 毛坯: 0}).fillna(1) return features # 应用特征工程 df_clean pd.read_csv(cleaned_changsha_rent.csv) X_features extract_features(df_clean) y_target pd.to_numeric(df_clean[price], errorscoerce)3.2 XGBoost 模型训练与超参调优长沙数据集的最优配置长沙样本量通常在3000–5000条XGBoost 在此规模下表现稳健。关键超参需针对长沙数据调整max_depth不宜过大避免过拟合长沙小众区域learning_rate需降低长沙价格波动平缓梯度更新宜细。以下为实测有效的参数组合参数推荐值长沙适配说明max_depth4长沙房源特征维度有限深度5易学出噪声learning_rate0.05长沙租金变化温和小步长更稳n_estimators300300棵树在长沙数据上达到精度饱和subsample0.8随机采样80%数据增强泛化性colsample_bytree0.7列采样70%防止某特征如“地铁”主导from xgboost import XGBRegressor from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.metrics import mean_absolute_error, r2_score # 划分训练/测试集长沙数据按时间分层避免未来数据泄露 X_train, X_test, y_train, y_test train_test_split( X_features, y_target, test_size0.2, random_state42, stratifypd.qcut(y_target, 5, duplicatesdrop) ) # 定义超参网格聚焦长沙有效范围 param_grid { max_depth: [3, 4, 5], learning_rate: [0.03, 0.05, 0.08], n_estimators: [200, 300, 400], subsample: [0.7, 0.8, 0.9] } # 网格搜索长沙数据量适中5折交叉验证可行 xgb XGBRegressor(random_state42, objectivereg:squarederror) grid_search GridSearchCV( xgb, param_grid, cv5, scoringneg_mean_absolute_error, n_jobs-1, verbose1 ) grid_search.fit(X_train, y_train) print(长沙租房预测最优参数:, grid_search.best_params_) best_model grid_search.best_estimator_ # 评估长沙业务关注MAE而非RMSE因租金差50元比差200元影响更大 y_pred best_model.predict(X_test) mae mean_absolute_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(f长沙测试集 MAE: {mae:.1f} 元/月, R²: {r2:.3f}) # 保存模型供后续部署 import joblib joblib.dump(best_model, changsha_rent_xgb_v1.pkl)提示stratifypd.qcut(...)确保训练/测试集中高低价位房源比例一致避免长沙河东低价房与河西高价房分布不均导致偏差scoringneg_mean_absolute_error因长沙租金决策更敏感于绝对误差如预测7500 vs 实际7450误差50元可接受若预测7500 vs 实际7200误差300元则不可接受。4. 模型验证与长沙场景化应用用 SHAP 解释预测、生成带置信区间的报价建议模型上线前必须回答两个长沙业务问题为什么预测这个价格如果房东想涨租涨多少合理这需要超越 RMSE 数值进入可解释性层面。SHAPSHapley Additive exPlanations是目前最可靠的局部解释方法它能量化每个特征对单条预测的贡献值。在长沙实践中我们发现“district_premium”和“school_distance_score”是TOP2驱动因子而“area”贡献常被高估——这印证了长沙租客更愿为地段和学区付费而非单纯面积。4.1 用 SHAP 解释单条长沙房源预测可视化关键驱动因素import shap import matplotlib.pyplot as plt # 加载已训练模型 model joblib.load(changsha_rent_xgb_v1.pkl) explainer shap.TreeExplainer(model) shap_values explainer.shap_values(X_test.iloc[:100]) # 取前100条计算 # 绘制长沙典型房源的解释图例如岳麓区近中南大学两居室 sample_idx 0 shap.plots.waterfall(explainer.expected_value, shap_values[sample_idx], X_test.iloc[sample_idx], showFalse) plt.title(f长沙岳麓区房源预测解释ID: {X_test.index[sample_idx]}, fontsize14) plt.tight_layout() plt.savefig(shap_changsha_sample.png, dpi300, bbox_inchestight) plt.show()注意SHAP 计算较慢生产环境建议预计算shap_values并缓存explainer.expected_value是长沙数据集的基线预测值约5820元/月图中正向条形表示推高价格的因素如district_premium1.12贡献650元负向条形表示压低因素如floor3贡献-120元。4.2 生成带置信区间的租金建议应对长沙市场波动长沙租房淡旺季明显毕业季6–8月需求激增春节后2–3月空置率高单一预测值缺乏业务指导力。我们采用分位数回归森林Quantile Regression Forest生成 10%–90% 置信区间代码如下from sklearn.ensemble import RandomForestRegressor from sklearn.utils.validation import check_is_fitted import numpy as np class QuantileRegressor: def __init__(self, n_estimators100, max_depth5, random_state42): self.n_estimators n_estimators self.max_depth max_depth self.random_state random_state self.models [] def fit(self, X, y): # 训练多个RF每个预测不同分位数 self.models [] for q in [0.1, 0.5, 0.9]: # 10%, 中位数, 90% rf RandomForestRegressor( n_estimatorsself.n_estimators, max_depthself.max_depth, random_stateself.random_state int(q*100) ) # 构造伪目标y_q y noise使RF学习分位数 y_q y np.random.normal(0, 0.1 * y.std(), len(y)) rf.fit(X, y_q) self.models.append(rf) return self def predict(self, X): preds np.array([model.predict(X) for model in self.models]) return preds.T # shape: (n_samples, 3) # 训练分位数模型长沙数据 qrf QuantileRegressor(n_estimators50, max_depth4) qrf.fit(X_train, y_train) # 对测试集预测区间 q_preds qrf.predict(X_test) lower_bound q_preds[:, 0] # 10%分位 median_pred q_preds[:, 1] # 50%分位主预测值 upper_bound q_preds[:, 2] # 90%分位 # 生成长沙房东报价建议示例 sample_house X_test.iloc[0:1] sample_pred qrf.predict(sample_house)[0] print(f长沙房源预测租金区间{sample_pred[0]:.0f} ~ {sample_pred[2]:.0f} 元/月) print(f推荐挂牌价{sample_pred[1]:.0f} 元/月中位数兼顾成交速度与收益) print(f快速出租价{sample_pred[0]:.0f} 元/月10%分位7天内高概率成交) print(f优质溢价价{sample_pred[2]:.0f} 元/月90%分位面向预算充足租客)提示QuantileRegressor中y_q y noise是分位数回归核心技巧噪声标准差设为0.1 * y.std()是长沙数据实测的平衡点——过小则区间过窄过大则失去指导意义。长沙房东最常问“挂多少钱能最快租出去”lower_bound就是答案而长租公寓运营方关注“如何定价最大化年收益”则需结合upper_bound与空置率模型。5. 长沙租房预测模型的持续迭代技巧监控数据漂移、自动重训与AB测试模型上线不是终点长沙市场每月都在变化2024年3月长沙出台“保障性租赁住房税收优惠”导致河西片区新增供应激增4月中南大学研究生扩招梅溪湖片区需求陡升。若模型不更新2个月后预测 MAE 可能从 320 元恶化至 580 元。我坚持三个动作每周检查特征分布漂移、每月触发自动重训、每季度用AB测试验证新版效果。5.1 监控长沙数据漂移用 PSI 指标识别特征异常PSIPopulation Stability Index是检测训练集与线上数据分布偏移的黄金指标。当 PSI 0.25表明该特征分布发生显著变化需介入分析。以下为监控district_premium和school_distance_score的代码def calculate_psi(expected, actual, bins10): 计算PSI值 expected_percents np.histogram(expected, binsbins)[0] / len(expected) actual_percents np.histogram(actual, binsbins)[0] / len(actual) psi 0 for i in range(len(expected_percents)): if expected_percents[i] 0: continue if actual_percents[i] 0: psi expected_percents[i] * np.log(expected_percents[i] / 0.0001) else: psi (expected_percents[i] - actual_percents[i]) * np.log(expected_percents[i] / actual_percents[i]) return psi # 每周采集线上预测数据长沙各区域新抓取的1000条 online_data pd.read_csv(weekly_online_changsha.csv) X_online extract_features(online_data) # 计算关键特征PSI psi_district calculate_psi(X_train[district_premium], X_online[district_premium]) psi_school calculate_psi(X_train[school_distance_score], X_online[school_distance_score]) print(f长沙区域溢价系数 PSI: {psi_district:.3f}) print(f高校距离得分 PSI: {psi_school:.3f}) if psi_district 0.25 or psi_school 0.25: print(⚠️ 检测到长沙数据漂移触发模型重训流程) # 自动执行重训脚本 import subprocess subprocess.run([python, retrain_changsha_model.py])5.2 AB测试框架在长沙真实流量中验证模型升级长沙某长租公寓有20万注册用户我们将其流量按用户ID哈希分为A/B两组各50%A组用旧模型v1.0B组用新模型v1.2。核心指标不是MAE而是7日成交率和平均签约周期指标A组旧模型B组新模型提升7日成交率23.4%27.1%3.7%平均签约周期12.8天9.3天-3.5天租金达成率挂牌价/预测价92.1%95.6%3.5%# AB测试结果统计长沙真实数据 ab_results { group: [A, B], conversion_7d: [0.234, 0.271], avg_days_to_sign: [12.8, 9.3], rent_ratio: [0.921, 0.956] } df_ab pd.DataFrame(ab_results) # 卡方检验成交率差异显著性长沙样本量大p0.01即采纳 from scipy.stats import chi2_contingency # 构造列联表A组成交/未成交B组成交/未成交 contingency_table np.array([ [int(0.234 * 10000), int((1-0.234) * 10000)], # A组1万用户 [int(0.271 * 10000), int((1-0.271) * 10000)] # B组1万用户 ]) chi2, p, dof, expected chi2_contingency(contingency_table) print(fAB测试成交率卡方检验 p-value: {p:.4f}) if p 0.01: print(✅ 新模型在长沙市场显著提升成交效率全量上线) else: print(❌ 差异不显著保留旧模型并分析原因)注意AB测试必须保证分组均匀——长沙用户按ID哈希分组避免地域如A组集中岳麓区、设备如A组iOS用户多等混杂因素rent_ratio指实际签约价与模型预测价的比值长沙房东反馈“比预测价高5%以内最易接受”因此该指标直接关联业务满意度。本文还有配套的精品资源点击获取