首页 / 资讯中心 / 文章详情

西安房价分析house_crawler:Python爬虫+MATLAB预测实战

西安房价分析house_crawler:Python爬虫+MATLAB预测实战 ★ FEATURED ARTICLE
简介这是一份基于Python和MATLAB混合编程的西安房价分析工具源码面向房地产研究者、从业者及有购房意向的用户也适合想学习爬虫与数据分析的开发者。资源共49个文件包含29个CSV数据文件、7个Python脚本负责房价数据爬取、预处理与展示、6个MATLAB脚本执行多维度分析与预测建模、3张PNG趋势图及说明文档压缩包大小约49.41MB。已有293人学习下载。通过该项目可掌握从网络爬虫采集二手、新房及租赁房源信息到按区域、户型、朝向等维度清洗分析再到利用随机森林、SVR等模型预测房价走势的完整流程并能参考其目录结构与代码组织方式。整体可作为西安区域房价量化研究或Python与MATLAB混合编程实践的实用参考。1. 西安房价分析 house_crawler一套 Python 爬虫加 MATLAB 预测的资源先说我拿到这份资源的第一感觉市面上讲房价爬虫的帖子很多绝大多数爬到 CSV 就收工了分析和预测要自己另起炉灶。这套 house_crawler 源码不一样它把「抓取 → 清洗 → 多维切分 → 建模预测 → 真实值回测」整条链路跑通了而且是用 Python 干爬虫和清洗MATLAB 干统计建模和可视化两边各管一段。它不是什么大工程但刚好能解决一个具体诉求想研究西安新房、二手房、租赁房价格的人不用再手动复制网页数据跑一遍脚本就能拿到按区域、户型、年代、朝向、装修、楼层、中介、开盘时间、销售状态切好的 29 个 CSV以及随机森林、梯度提升、SVR 三个模型的预测结果。适合正在做城市房价分析、想练 Python 爬虫落地或者打算把 MATLAB 统计工具箱接进爬虫工作流的从业者和学习者。2. 模块结构与技术选型爬虫为何用 Python、预测为何交给 MATLAB2.1 Python 与 MATLAB 的分工边界从技术选型看混合编程思路这套源码最有意思的不是爬虫本身而是作者对工具的选择。Python 这边用了 requests 和 BeautifulSoup 这类库加上 pandas 做清洗天然适合处理网页解析和表格整理MATLAB 那边则用了统计和机器学习工具箱随机森林、梯度提升、SVR 都有现成接口通过 fitensemble、fitrsvm 这类函数直接训练连交叉验证和绘图都能一站做完。换句话说纯 Python 加 sklearn 也能走到预测那一步但作者选择让 MATLAB 接管后半程本质是想用统计工具箱里成熟的模型对象和可视化函数省掉自己手写评估代码的时间。这个分工是有边界的不是所有项目都适合这么拆。如果你的数据量到了几十万行、需要频繁迭代特征那 MATLAB 的脚本式工作流反而不如 Python 顺手但这套资源的量级是几个区的二手房和新房几千条记录MATLAB 读 CSV 做训练和画图非常轻快。我实际跑下来觉得这个选型对「单城市、中小样本」的房价分析场景是合理的对想学混合编程的人来说也是一个能直接照搬的骨架。需要注意的是两种语言之间靠 CSV 文件通信Python 写出的文件编码和字段类型会直接影响 MATLAB 读入这一块我在后面章节单独讲。2.2 三层结构拆解原始 CSV、维度 CSV 与预测结果怎么流转把压缩包解压后目录结构大致是这样house_crawler/ ├── new_house/ # 新房 │ ├── new_house_info_crawler.py │ ├── house_info_by_area.csv │ ├── house_info_by_building_type.csv │ ├── house_info_by_decoration_status.csv │ ├── house_info_by_open_time.csv │ └── house_info_by_sale_status.csv ├── old_house/ # 二手房 │ ├── old_house_info_crawler.py │ ├── old_house_info_by_area.csv │ ├── old_house_info_by_face_direction.csv │ ├── old_house_info_by_house_type.csv │ ├── old_house_info_by_build_time.csv │ ├── old_house_info_by_property_agency.csv │ ├── old_house_info_by_decoration_status.csv │ └── old_house_info_by_storey.csv ├── rented_house/ # 租赁房 │ ├── rented_house_info_crawler.py │ ├── rented_house_info_by_rent.csv │ └── rented_house_info_by_house_type.csv ├── house_price_prediction/ # 按区域预测 │ ├── area_house_price_prediction.py │ ├── area_house_price_crawler.py │ ├── house_price_of_different_districts.csv │ ├── 2020_ground_truth.csv │ ├── random_forest_result/ │ ├── gbr_result/ │ └── svr_result/ ├── house_price_prediction_by_factors/ # 多因素预测 │ ├── house_price_prediction_by_factors.py │ ├── house_info_data_final.csv │ ├── data_processing.py │ ├── ground_truth.csv │ ├── models/ │ ├── images/ │ └── prediction_results/ ├── crawler_data/ ├── dicts/ └── readme.txt这份文件清单可以分三层理解。第一层是原始采集层三个爬虫脚本分别落在 new_house、old_house、rented_house 三个目录抓完的数据以 CSV 形式存下来。第二层是维度切分层同一个房源集合按区域、户型、年代、朝向、装修状态、楼层、中介等维度拆成独立的 CSV这是为了后面用 MATLAB 直接读入做分组聚合和绘图不用每次现算。第三层是预测层house_price_prediction 目录管区域均价预测house_price_prediction_by_factors 目录管多因素回归预测ground_truth 和 2020_ground_truth 是真实值基准用来回测模型误差。数据流的走向是三个爬虫脚本生成各自的明细 CSVdata_processing.py 把这些文件合并清洗成 house_info_data_final.csv再喂给两个预测项目里的 MATLAB 脚本。整个流程里 CSV 是中间契约Python 负责生产数据MATLAB 负责消费数据谁都不越界。根目录的 readme.txt 把运行顺序和注意事项写得很实在属于作者自己记给自己看的操作笔记比很多官方文档实用。2.3 环境准备与执行顺序从 python 安装到 matlab 工具箱确认这套资源对环境要求不高。Python 这边我建议直接上 Anaconda 的 Python 3.8 分支如果你还在找 python 安装教程装 Anaconda 是最省事的方式自带 pandas、numpy、scikit-learn省得一个个手动装。爬虫部分需要额外装 requests 和 beautifulsoup4一行命令搞定pip install requests beautifulsoup4 pandas numpy scikit-learn逻辑说明pandas 和 numpy 用于清洗和数值计算scikit-learn 用于在 Python 侧做简单的模型对照实验实际上最终预测交给 MATLAB 的话scikit-learn 只在调试时用得上但装上没坏处。参数说明如果网络源比较慢可以加-i https://pypi.tuna.tsinghua.edu.cn/simple换镜像。MATLAB 那边不需要特定版本关键是确保安装时勾选了 Statistics and Machine Learning Toolbox少了它 fitensemble 和 fitrsvm 都跑不了。网上 MATLAB 下载安装教程很多但大多数人装完才发现工具箱没选全这个坑我建议提前避掉。运行顺序按 readme 的流程走先跑三个爬虫生成原始 CSV再跑 data_processing.py 做合并清洗最后把整理好的 CSV 丢给 MATLAB 脚本做分析和训练。千万别跳过清洗直接跑预测否则模型吃进去的字段还是字符串MATLAB 会直接报错。3. 三个爬虫脚本实战新房、二手房、租赁房的抓取参数与去重细节3.1 新房爬虫列表页翻页、User-Agent 与字段截取new_house_info_crawler.py 负责抓新房数据。新房的典型字段包括楼盘名、所在区域、户型面积、单价、总价、开盘时间、装修状态、销售状态。抓取逻辑一般是先请求列表页再逐条解析房源卡片最后翻页重复操作。常见做法是这样的结构import requests import pandas as pd import time from bs4 import BeautifulSoup def fetch_new_house(base_url, pages10): rows [] headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: base_url } for page in range(1, pages 1): url f{base_url}/pg{page}/ try: resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() except requests.RequestException: time.sleep(2) continue soup BeautifulSoup(resp.text, html.parser) for item in soup.select(.resblock-list .resblock): rows.append({ title: item.select_one(.name).text.strip(), area: item.select_one(.area).text.strip(), price: item.select_one(.price).text.strip(), district: item.select_one(.district).text.strip() }) time.sleep(1.5) return pd.DataFrame(rows)逻辑说明每次请求都带 User-Agent 和 Referer这是应对基础反爬的常见做法很多站点会拦掉没有浏览器标识的请求把解析结果逐条 append 到列表里最后统一转 DataFrame比边抓边写 CSV 更稳。参数说明pages 控制抓取深度想跑全量就调大想验证流程先设 3 页timeout10 是单次请求的上限超过这个时间就放弃本轮time.sleep(1.5) 是频率控制我一般不会低于 1 秒否则容易被封。新房和二手房有一个本质区别新房页面里带有楼盘整体的开盘时间和销售状态这两项是二手房没有的所以 new_house 目录里才会有 house_info_by_open_time.csv 和 house_info_by_sale_status.csv。抓取字段时要把这两个一并解析不然后续按开盘时间分析会缺一块。3.2 二手房爬虫一条记录拆八个维度 CSVold_house_info_crawler.py 是三个爬虫里信息量最大的因为它要支撑八个维度的切分。二手房页面上能看到的字段包括小区名、区域、户型、面积、朝向、楼层、建筑年代、装修状况、中介公司、挂牌价。爬虫抓到明细数据后按维度拆分的写法大致是这样import pandas as pd def split_old_house_dimensions(df): df.to_csv(old_house_info_by_area.csv, indexFalse) df.groupby(face_direction).size().to_csv(old_house_info_by_face_direction.csv) df.groupby(house_type).size().to_csv(old_house_info_by_house_type.csv) df.groupby(build_time).size().to_csv(old_house_info_by_build_time.csv) df.groupby(decoration_status).size().to_csv(old_house_info_by_decoration_status.csv) df.groupby(storey).size().to_csv(old_house_info_by_storey.csv) df.groupby(property_agency).size().to_csv(old_house_info_by_property_agency.csv)逻辑说明to_csv 先把明细表全量存一份随后 groupby 加 size 把同一字段的值聚合计数生成每个维度的频次表。这样 MATLAB 读入 by_face_direction.csv 后直接就能画朝向分布图读入 by_build_time.csv 就能看不同年代的房源量。参数说明这里用 size 不用 count是因为 size 会把缺失值也统计进去能直观看出哪些房源的朝向或年代字段没抓到方便回来补数据。拆维度时容易忽略去重。同一套房源在列表页和详情页可能各出现一次如果直接聚合朝向分布和年代分布都会偏大。我一般会在拆分前先按「标题 区域 面积」三列做一次去重保证一条房源只进一次统计。这是老生常谈但不做的话后面所有维度的占比都是错的。3.3 租赁房爬虫租金字符串的归一化与分桶rented_house_info_crawler.py 抓的是租赁房源逻辑和新房二手房类似但租金字段的清洗更麻烦。网页上租金常写成「3500元/月」或「9500元/月」爬下来是字符串直接进 CSV 会让 MATLAB 没法计算。常见做法是先抽数值import pandas as pd def normalize_rent(raw): if pd.isna(raw): return None clean str(raw).replace(元/月, ).replace(,, ).strip() try: return float(clean) except ValueError: return None逻辑说明先把「元/月」和千分位逗号去掉再用 float 转数值转失败说明原始字符串格式异常返回空值留给后面的清洗流程处理。参数说明replace 的顺序是先长后短避免「元/月」被误伤如果有的页面单位是「元/天」要额外加一层判断不能直接套用。租赁数据拆了两个维度按租金分桶的 rented_house_info_by_rent.csv 和按户型分布的 rented_house_info_by_house_type.csv。分桶的常见做法是把租金切成「0-2000、2000-4000、4000-6000、6000以上」这种区间方便看租赁市场的供给结构。翻页抓取时租赁房源的重复率比二手房更高我一般会额外记录房源的发布时间字段用「发布时间 小区名 户型 租金」做复合去重比单靠标题可靠得多。4. 从 29 个 CSV 看数据设计清洗逻辑、维度切分与可视化输入4.1 原始层、维度层、预测层三层文件的设计意图这套资源里 CSV 数量多但归类后思路很清晰。原始层存的是爬虫直接产出的明细数据比如每个旧房源的完整信息维度层是把原始数据按业务维度聚合后的统计表比如 old_house_info_by_area.csv 是分区域的房源量和均价new_house_info_by_open_time.csv 是分开盘时间的楼盘分布预测层则是把清洗后的明细数据合并成 house_info_data_final.csv再派生出一张 ground_truth.csv 作为真实值基准。分三层的价值在于每层文件都有明确的消费方。原始层给 data_processing.py 用维度层给 MATLAB 绘图和分析用预测层给模型训练和回测用。如果全部塞进一张大宽表MATLAB 每次做分组聚合都要现场写统计语句不如直接读已经算好的维度 CSV 来得快。这也是我在类似项目里会沿用的一种习惯数据文件跟着消费场景走而不是跟着数据库范式走。各层文件的归属可以参考这个表层级代表文件消费方用途原始层crawler_data、house_info_data_final.csvdata_processing.py合并清洗维度层old_house_info_by_.csv、new_house_info_by_.csvMATLAB 脚本分布统计、绘图预测层house_price_of_different_districts.csv、ground_truth.csv预测脚本模型训练与回测4.2 data_processing.py 的清洗流程去重、类型转换与异常过滤data_processing.py 是整个 Python 侧的收口脚本它把三个爬虫产出的数据合并成一张干净的明细表。数据清洗的步骤通常是去重、类型转换、异常值过滤三件事import pandas as pd def clean_house_data(raw_path): df pd.read_csv(raw_path, encodingutf-8-sig) df df.drop_duplicates(subset[title, district, area]) df[price] pd.to_numeric(df[price], errorscoerce) df df[(df[price] 0) (df[area] 0)] df df.dropna(subset[price, area]) return df逻辑说明读取时指定 utf-8-sig 编码防止中文乱码drop_duplicates 用标题、区域、面积三列做联合去重比单列去重更稳妥to_numeric 配合 errorscoerce 能把「1.2万」「暂无报价」这类字符串转成数值或空值方便后续过滤。参数说明subset 的字段名要和爬虫输出的列名严格一致不一致时会报 KeyError价格和面积大于 0 的条件是为了过滤掉「价格待定」和「车位面积」这类异常记录。清洗时有一个容易漏掉的点面积字段里可能混着「89平」「89㎡」「89平米」三种写法如果直接 to_numeric 会得到一堆空值。我一般会在转换前先做一层正则替换把「平」「㎡」「平米」统一删掉再转数值这一步看起来小实际能避免丢掉大量有效样本。4.3 聚合统计怎么读年代、朝向、装修对房价的贡献维度层 CSV 的价值不在于数据本身而在于可以直接回答业务问题。比如读 old_house_info_by_build_time.csv 可以看出西安二手房市场中 2010 年后的次新房占比读 old_house_info_by_face_direction.csv 可以看南北通透房源的供给量读 old_house_info_by_decoration_status.csv 可以计算毛坯和精装的价差。每个维度文件都是一次 groupby 的产物拿 Excel 打开就能理解不需要额外写查询。用 MATLAB 读这些维度 CSV 做可视化的常见做法是readtable 读入后直接 bar 或 pie 出图三张 PNG 图表的原始数据就是从这里来的。需要注意MATLAB 读 CSV 时默认会把中文字段名识别成变量名如果字段名重复会自动改名所以 Python 侧写维度 CSV 时表头一定要保持英文或拼音别用中文否则读进来容易报错。4.4 三张 PNG 图表的生成MATLAB 读 CSV 做可视化的常见做法预测目录下的 images 文件夹里放着三张 PNG 图表它们是从多因素预测脚本运行结果里生成的通常包括特征重要性图、预测值与真实值对比散点图、误差分布直方图。这些图表在 MATLAB 里生成的方式大致是读入 prediction_results 里的 CSV再用 scatter 画真实值与预测值对比用 histogram 画残差分布。读入时用 readtable 指定 VariableNamingRule 为 preserve能避免中文字段被改写。图表生成的顺序要放在模型评估之后因为散点图需要用到预测值和真实值两列数据。5. 房价预测与常见排查区域均价、多因素建模和 RF/SVR/GBR 的坑5.1 区域均价预测从 CSV 聚合到模型对比house_price_prediction 目录下的 area_house_price_prediction.py 解决的是「西安哪个区最贵、未来走势如何」这类问题。它先读取 house_price_of_different_districts.csv 和区域爬虫抓到的明细按行政区聚合出均价序列再用三种模型分别做回归预测。模型参数常见的设定是这样的from sklearn.ensemble import RandomForestRegressor from sklearn.ensemble import GradientBoostingRegressor from sklearn.svm import SVR models { rf: RandomForestRegressor(n_estimators200, random_state42), gbr: GradientBoostingRegressor(n_estimators150, learning_rate0.05), svr: SVR(kernelrbf, C1.0, epsilon0.1) }逻辑说明random_forest_result、gbr_result、svr_result 三个目录对应三种模型的输出方便对比哪一类模型在区域均价预测上更稳。参数说明n_estimators 控制树的数量200 属于中等规模数据量小的话可以降到 100learning_rate0.05 是梯度提升的收缩步长越小越不容易过拟合但训练更慢SVR 的 C 是惩罚系数C 越大越严格拟合训练集epsilon 控制回归的误差管宽度这两个参数需要按数据量级微调。区域均价预测容易踩的坑是训练集和验证集的时间错位。资源里专门放了一个 2020_ground_truth.csv说明作者用 2020 年的真实均价来验证模型预测而不是简单的随机切分训练测试集。这个设计是对的房价预测的业务目标是预测未来训练集必须在时间上早于验证集否则评估出的精度没有实际参考意义。5.2 多因素预测特征分桶与 one-hot 的落地写法house_price_prediction_by_factors.py 是更完整的多因素模型特征包括面积、户型、朝向、建筑年代、装修状态、楼层、区域、物业类型等。这类字段大部分是类别型不能直接丢给回归模型需要先做数值化处理。特征工程的写法大致是df[area_bucket] pd.cut(df[area], bins[0, 60, 90, 120, 144, 999], labels[small, mid_small, mid, large, xl]) df pd.get_dummies(df, columns[district, house_type, face_direction])逻辑说明pd.cut 把面积切成五个区间把连续变量离散化帮助模型捕捉「小户型溢价」和「改善型溢价」这种非线性关系get_dummies 对区域、户型、朝向做 one-hot把类别文本变成 0/1 数值列。参数说明bins 的边界要根据西安的实际面积分布调整60 平以下算小户型、90-120 是主力、144 以上算改善columns 里列出的字段必须是 DataFrame 里真实存在的列名。这套特征处理思路和你见过的 python 量化交易策略代码里的因子处理是一回事原始字段先分桶或 one-hot模型吃的永远是数值矩阵而不是字符串或单位混杂的原始值。这里特别要注意面积字段爬虫抓出来的可能是「建面 89 平」这种带前缀的文本处理顺序必须先清洗再分桶否则 pd.cut 直接报错。5.3 模型结果落盘与评估口径多因素预测脚本训练完模型后会有两个输出动作把模型对象保存到 models 目录把预测结果写入 prediction_results 目录。模型保存常见做法是用 pickle 或 joblib 把训练好的对象序列化这样下次预测不用重新训练。结果落盘时至少要包含三列真实值、预测值、残差这样后续评估和图谱生成都方便。评估口径上区域均价预测和多因素预测略有不同。区域均价预测主要看每个行政区的均价偏差多因素预测主要看单套房源的误差分布。我在读这份资源时特别确认了一下 ground_truth.csv 的用法它是按区域或按房源记录存放真实成交价和模型预测值做差后就能算出 MAE、RMSE。RMSE 比 MAE 对异常值更敏感如果某个区域出现一次极大误差RMSE 会被明显拉高这时候就要回去查是不是该区的数据量太少或字段缺失严重。5.4 常见问题排查四个高频翻车场景根据我拆这份源码的实际经验最容易翻车的集中在环境、清洗和模型对齐三个环节这里有四条排查记录供参考。反爬拦截导致抓取结果为空。现象爬虫运行不报错但解析出的列表始终是空的CSV 里只有表头没有数据。原因目标站点对无标识请求做了拦截返回的页面里没有房源卡片或者返回了验证码页。解决检查请求头里的 User-Agent 和 Referer 是否完整再检查响应体里是否包含目标选择器对应的 HTML 结构我在项目里碰到过选择器类名变更的情况用 BeautifulSoup 调试时先把 resp.text 打印出来对着找。价格单位不一致导致预测结果整体偏大或偏小。现象清洗完的均价明显偏离真实行情区域均价预测误差巨大。原因新房页面可能标「总价 180 万/套」二手房标「单价 1.8 万/平」租赁房标「3500 元/月」这三类价格单位完全不同如果混进同一个训练集模型会被单位带偏。解决在清洗阶段就按用途分表区域均价预测只用单价总价只作为展示字段不参与建模。同一房源重复抓取导致维度统计翻倍。现象朝向分布里南北朝向占比异常高明显不符合真实市场结构。原因列表页和详情页各抓了一次或者翻页时有重叠。解决清洗时用「标题 区域 面积」做联合去重如果还有漏网的加一个发布时间的字段进去重条件能进一步降低重复率。MATLAB 读入 CSV 后中文乱码或数据类型全变成 cell。现象readtable 读出来的中文字段名变成乱码数值列被识别成文本。原因Python 侧保存 CSV 时用了默认编码MATLAB 按系统编码读取失败。解决Python 写 CSV 时统一指定 encodingutf-8-sigMATLAB 读入时用 detectImportOptions 手动指定 VariableNamingRule数据量大或字段多的时候可以写一个通用的读表函数避免每个脚本里重复设置。6. 用 ground_truth 回测预测结果一份验证脚本把区域误差打回原形6.1 把预测值与真实值对齐统一行政区名是关键资源里有两个 ground_truth 文件一个是总体的 ground_truth.csv一个是 2020 年的 2020_ground_truth.csv后者专门用于区域均价预测的回测。回测的第一步不是算误差而是对齐行政区名称。爬虫抓到的区域字段可能是「雁塔」「雁塔区」两种写法并存预测结果里可能写的是「雁塔」真实值里写的是「雁塔区」直接 merge 就会丢行。我一般会先做一个字典映射把简称统一成标准名称再对齐。import pandas as pd district_map { 雁塔: 雁塔区, 碑林: 碑林区, 未央: 未央区, 长安: 长安区 } pred pd.read_csv(prediction_results/pred.csv) true pd.read_csv(2020_ground_truth.csv) pred[district] pred[district].map(district_map).fillna(pred[district]) merged pred.merge(true, ondistrict, suffixes(_pred, _true)) merged[error] merged[price_pred] - merged[price_true] merged[abs_error] merged[error].abs()逻辑说明map 加 fillna 的组合先做标准化标准化失败的保留原值避免整行被误删merge 用行政区名做连接键两边列名冲突时用 suffixes 区分error 列是带方向性的误差abs_error 用于算平均绝对误差。参数说明district_map 里的键值对可以先从爬虫数据里用 value_counts 扫一遍把所有别名都找出来再补全只写几个示例肯定不够。6.2 误差分布异常时的第一反应对齐之后算出 RMSE 和 MAE这只是第一步更重要的是看误差分布在哪。我习惯把 abs_error 降序排列看误差最大的前几条落在哪个区域再回到原始 CSV 里确认那个区域的样本量和字段完整度。如果某个区误差明显大于其他区常见的原因就两个样本量太少模型没学到该区的价格规律或者该区数据里混入了几套「豪宅」或「车位」记录单价被极端值拉高模型预测值跟不上。处理方法是把极端样本单独拎出来看而不是直接删掉。如果确认是车位或商业产权混进了住宅数据删掉没问题如果确实是高端楼盘保留会让 RMSE 变大但预测结果更真实。这个取舍没有标准答案取决于你想让模型反映「普通住宅」还是「全市场」。做验证时我会顺手把 merged 表输出成一份带误差标注的新 CSV和原始预测结果放在一起。这样下次改特征或调参时可以直接对比同一份 ground_truth 下的误差变化而不是靠记忆判断改完有没有变好。从那以后我每次跑新城市的房价数据都强制走一遍「爬取 → 清洗 → 维度切分 → 训练 → ground_truth 回测」的完整流程先看误差分布再谈模型结论而不是训练完直接出一个预测数字就收工。这个习惯帮我挡掉了至少三次因为单位没对齐导致的荒谬结果希望也帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站