
简介基于 Python 的二手房数据分析完整项目面向数据分析与爬虫方向的初学者、课程设计和毕业设计学生提供从数据采集、清洗到可视化展示的一站式参考方案。压缩包共 157 个文件、约 48.05MB其中包含 18 个 Python 源码、18 个 CSV 数据文件、15 个 HTML 页面及配套 JS 脚本另有 Word 文档、PPT 答辩资料、65 张 PNG 截图和少量字体配置文件结构清晰便于按模块阅读。代码注释详细个人手打 98 分项目系统功能完善、界面美观、操作简单下载后简单配置即可运行适合期末大作业、毕业设计直接参考或二次开发。项目数据集覆盖原始数据、清洗后数据等多个版本可通过折线图、柱状图等形式展示二手房价格分布与区域热度经过严格调试可稳定运行同时附有文档说明和 PPT帮助快速理解设计思路目前已有 126 人学习下载。1. 二手房数据分析这个“高分项目”到底在分析什么打开任意一个源码分享平台这类标注“完整源码文档说明PPT资料”的二手房数据分析项目下载量都常年靠前。但大多数人的实际体验是代码能跑通数据也有却讲不清整个项目到底在论证什么。二手房价分析不是算法竞赛它的本质是一条完整的数据流水线——从房源网页把半结构化的挂牌信息抓下来清洗成可计算的表格再通过可视化和回归模型回答“哪些因素在影响房价”这个问题。这个项目适合Python基础语法已过关、正在找数据分析方向课程设计或求职作品的人也适合想看看爬虫、Pandas和统计模型在真实工程里怎么被串起来的开发者。读透它你得到的不是一个脚本集而是一套可以迁移到任何结构化数据分析任务的完整方法。2. 数据采集先定字段再写爬虫二手房源数据怎么落到本地2.1 为什么源码里最先出现的不是爬虫而是建表脚本拿到这类项目源码很多人第一个动作是找爬虫主文件翻完目录却发现排在最前面的是一个 schema.sql 或 fields.py。这个顺序是有道理的二手房源的原始信息是典型的半结构化数据一个房源页面里“总价”可能带货币单位“户型”写的是“3室2厅1卫”“朝向”偶尔整条缺失。如果不在采集之前把字段约定清楚后面每做一次分析都要回头补数据这比爬虫本身更耗时间。我一般会把房源字段分成三类。标识字段城市、区县、板块、小区名用于后续分组对比价格字段总价、单价是分析的核心因变量结构字段面积、户型、楼层、朝向、楼龄、装修状况是候选自变量。另外一定要加一个 crawled_at 采集时间字段因为房价对时间敏感不同批次的数据混在一起时没有时间戳就无法解释价格波动。建表的 SQLite 脚本通常长这样CREATE TABLE house_info ( id INTEGER PRIMARY KEY AUTOINCREMENT, city TEXT NOT NULL, district TEXT, block TEXT, community TEXT, layout TEXT, area_raw TEXT, floor TEXT, orientation TEXT, built_year TEXT, total_price REAL, unit_price REAL, crawled_at TEXT DEFAULT (datetime(now, localtime)) );注意 total_price 用 REAL 存储浮点价格unit_price 留到清洗阶段再计算而 area_raw、floor、built_year 这些先统一存成 TEXT。原因很简单原始字段里“89.5平”“共6层”“2008年建”混着各种单位符号文本存储不会在采集阶段丢信息解析延后到清洗环节是最稳妥的做法。字段设计回答的是“这个项目能分析什么”而不是“今天能爬什么”。2.2 python爬虫的关键代码requests 加 BeautifulSoup 抓取列表页采集模块是这个项目里最依赖目标网站结构的部分。基础的 python 爬虫方案固定是 requests 拿 HTMLBeautifulSoup 做解析。一个最小可用的抓取函数如下import requests from bs4 import BeautifulSoup import random import time def fetch_listing(city, page): url fhttps://example.entry.house/{city}/pg{page}/ headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9, } resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.text html fetch_listing(chaoyang, 1) soup BeautifulSoup(html, html.parser) card_nodes soup.select(.house-lst .info) print(本页房源卡片数:, len(card_nodes))headers 里的 User-Agent 是必须的第一层伪装很多服务端只凭这一个字段就会拒绝请求timeout10 避免单页卡死拖垮整个采集任务resp.apparent_encoding 根据页面字节内容推断编码解决中文乱码。select 里的 CSS 选择器一定要对照目标站点的实际 HTML 结构调整这是整个爬虫里唯一必须人工改的部分其他逻辑换个城市名就能复用。拿到卡片节点后再从每个节点里提取标题、链接、总价等字段逐条写入 DataFrame。这个循环代码在真实项目里往往有几十行但核心逻辑就是定位节点、取文本、存字段这三步。解析逻辑建议单独写一个函数 parse_card(node)后续网站改版时只改这个函数不必动采集主流程。2.3 落库选择CSV、SQLite 还是 Excel采集后的数据要落盘三类方式的取舍直接决定后面清洗的效率。落库方式适合场景优点缺点CSV一次性分析、快速验证通用性强Excel 可打开无类型约束重复写入易出错SQLite多批次增量采集字段类型明确支持 SQL 查询需要一点 SQL 基础Excel直接交付给业务方直观、筛选方便写入慢数据量过万行就吃力我一般的做法是采集阶段先写 CSV理由是断点续采简单追加一行就行等全量采完再用 pandas 一次性导入 SQLite后续清洗全部用 SQL 或 pandas 操作。导入一行代码即可import sqlite3 import pandas as pd df pd.read_csv(data/raw/house_list.csv) conn sqlite3.connect(data/house.db) df.to_sql(house_info, conn, if_existsappend, indexFalse)参数说明if_existsappend 表示不清空已有表适合分批次追加indexFalse 避免把 pandas 自带索引写成多余列。到这里原始数据已经在一个可查询的结构里了下一步的清洗才有据可依。2.4 反爬与采集纪律限速、重试和边界二手房网站的列表页通常有基础的反爬策略常见做法是请求频率限制和 User-Agent 检测。对个人学习项目来说做到三步就够每次请求之间随机延时 2 到 5 秒遇到 403 或 5xx 状态码时指数退避重试把请求头补全尤其是 Accept-Language。这里不讨论任何绕过人机验证的方案那既不合规对一个数据分析项目也没有必要——几千条样本完全够用。for page in range(1, 51): try: text fetch_listing(chaoyang, page) # 解析并写入文件的逻辑 except requests.RequestException as exc: print(fpage {page} failed: {exc}) time.sleep(10) continue time.sleep(random.uniform(2, 5))提示采集前先看目标网站的 robots.txt 和版权声明只拿公开列表页的展示信息不碰需要登录或验证码的数据。3. 数据清洗与特征工程把“89平米”“3室2厅”变成可算的字段3.1 缺失值处理先看业务含义再决定丢弃还是填充爬下来的数据大概率不干净。第一件要做的事是统计缺失和重复而不是立刻写填补逻辑。常见的统计代码是这样import pandas as pd df pd.read_sql(SELECT * FROM house_info, conn) print(df.isnull().sum()) print(df.duplicated(subset[community, total_price, area_raw]).sum()) df df.drop_duplicates(subset[community, total_price, area_raw]) df df.dropna(subset[total_price]) df[orientation] df[orientation].fillna(未知)df.isnull().sum() 按列统计空值数量能快速看出哪些字段不可靠drop_duplicates 用小区名加总价加面积三个字段联合去重比只用房源标题更稳dropna 删掉 total_price 为空的记录因为总价是分析核心缺失值无法合理填充orientation 的缺失率如果很高说明该平台很多房源本来就不填朝向缺失本身代表一种状态填“未知”比删行更符合业务事实。判断标准就一条缺失值有没有业务含义。有含义就单独分类没有含义才考虑填充或删除。朝向、装修这类字段属于前者总价、面积属于后者。3.2 文本字段的正则解析从“3室2厅”里拆出结构化数字户型、面积、楼层这类字段在网页里全是字符串不能直接进模型。用正则提取是标准方案Pandas 的 str.extract 配合命名分组可以直接生成新列df[面积] df[area_raw].str.extract(r(\d\.?\d*)平).astype(float) df[室数] df[layout].str.extract(r(\d)室).astype(float) df[厅数] df[layout].str.extract(r(\d)厅).astype(float) df[所在楼层] df[floor].str.extract(r(\d)层).astype(float) df[总楼层] df[floor].str.extract(r共(\d)层).astype(float) df[楼龄] 2025 - df[built_year].astype(int)正则解释(\d\.?\d*)匹配“89”或“89.5”这类带可选小数的数字(\d)室定位到“3室2厅”里的“3”“共(\d)层”提取总楼层因为“低楼层/共6层”这类文本里的数字不止一个。注意 astype(float) 而不是 astype(int)因为任何一行解析失败都会产生 NaNNaN 是浮点类型转 int 会直接报错。所有提取后的新列先保持 float后续建模时再用 fillna 处理残余缺失。解析完一定要检查解析失败比例。如果 layout 里有“3室0厅”“4室2厅2卫”等变体某些样本的厅数可能提取不到这个比例超过 5% 就需要回去看原始文本。3.3 特征构造单价、距地铁距离、楼层系数原始字段清洗完还需要构造几个对房价解释力很强的特征。单价是第一个必须算的df[单价] df[total_price] * 10000 / df[面积]这里的 total_price 单位是万元面积单位是平方米乘以 10000 把总价换成元再除以面积得到每平米的单价。单价在不同区域之间直接对比才有意义总价受面积影响太大不能作为区域差异的比较基准。距地铁站距离从房源描述里很难直接拿到常见做法是用小区名反查地图 API或者用页面里已有的“距地铁站XX米”文本。后者更简单df[距地铁站_m] df[traffic_text].str.extract(r距地铁站(\d)米).astype(float) df[距地铁站_km] df[距地铁站_m] / 1000提取后用公里做单位系数量级对回归模型更友好。楼层系数也是一个高解释力的特征把所在楼层除以总楼层得到一个 0 到 1 的比值用来表达“相对楼层位置”比绝对楼层信息更通用因为 5 层楼里的 4 层和 30 层楼里的 4 层含义完全不同。3.4 数据体检describe 和 value_counts 的快速排查法清洗完不是直接建模先做一次全表体检。以下三行代码是体检标配print(df.describe().T) print(df[district].value_counts().head(10)) print(df[df[单价] 5000].head())describe 输出所有数值列的 count、mean、std、min、四分位数、max重点看 min 和 max 是否合理比如面积出现 1 平米或单价出现 0 元都是明显的脏数据value_counts 看区县的样本分布如果一个区只有十几条后面分组对比要谨慎筛出单价低于 5000 的行逐条检查这种往往是车位、商业公寓或者数据录入错误。提示对脏数据先查一条确认原因再批量处理不要因为一个离群值就把整列替换掉。4. 从描述到推断探索性分析与房价影响因素的量化结论4.1 可视化先行Matplotlib 和 Seaborn 的组合用法建模之前必须先做可视化这一步能暴露数据里 80% 的问题。一个标准的探索性分析会同时看单变量分布和分组差异import matplotlib.pyplot as plt import seaborn as sns fig, axes plt.subplots(1, 2, figsize(14, 5)) sns.histplot(df[单价], bins50, kdeTrue, axaxes[0]) axes[0].set_title(单价分布) sns.boxplot(datadf, xdistrict, y单价, axaxes[1]) axes[1].set_title(各区单价对比) axes[1].tick_params(axisx, rotation45) plt.tight_layout() plt.savefig(output/eda_price.png, dpi150)bins50 让直方图保留足够细节kdeTrue 叠加密度曲线方便看整体形态boxplot 的中位数和箱体可以直接对比区域间价格差距。保存图片时 dpi 设到 150后续放进 PPT 不会模糊。做这一步的意义是先用眼睛确认数据形态再决定用什么模型。4.2 区域差异与单价分布怎么读单价分布通常呈右偏少量豪宅把均值拉高中位数才是代表性水平。箱线图的读法有固定顺序先看箱体位置中位数高的区域整体价格高再看箱体宽度宽度大说明区域内房价分化严重最后看离群点离群点往往对应豪宅或异常数据。如果某个区的箱体上方出现一大片散点查询这些样本的小区名确认是真实豪宅还是采集错误。4.3 用 statsmodels 做多元线性回归核心参数怎么看探索性分析给出方向后用多元线性回归量化每个因素的影响。statsmodels 是更合适的库因为它的输出自带 P 值和置信区间适合做统计推断import statsmodels.api as sm features [面积, 室数, 楼龄, 距地铁站_km, 总楼层] X sm.add_constant(df[features]) model sm.OLS(df[总价], X).fit() print(model.summary())sm.add_constant 给自变量矩阵加一列全 1对应回归方程的截距项OLS 是普通最小二乘估计fit 完成拟合后summary 输出系数、标准误、t 值和 P 值。结果里最需要读懂的参数如下参数含义解读方法const截距所有自变量为 0 时的基础价格本身很少有意义coef系数每增加 1 个单位因变量的变化面积的 coef 表示每多 1 平米总价增加多少Pt系数显著性小于 0.05 认为该特征显著大于 0.05 考虑剔除R-squared模型解释力越接近 1 拟合越好但多变量时要看调整后 R²楼龄的系数通常为负代表折旧效应距地铁站_km 的系数也通常为负且显著说明离地铁越远价格越低。这些系数符号如果和直觉相反优先检查是否存在多重共线性而不是急着改模型。4.4 多重共线性与特征选择的两个坑面积和室数天然高度相关大户型面积必然大放进同一个模型会导致系数不稳定。判断标准是方差膨胀因子 VIFfrom statsmodels.stats.outliers_influence import variance_inflation_factor X sm.add_constant(df[features]) for i, name in enumerate(features): vif variance_inflation_factor(X.values, i) print(f{name}: VIF {vif:.2f})VIF 大于 10 说明该特征与其他特征存在严重共线性一般做法是保留面积、删除室数因为面积是更连续的度量。另一个坑是 R² 很高但系数全部不显著这往往出现在样本量小、特征多的场景处理方法是删减特征保留业务上最核心的三到五个变量。回归模型在这里的目的不是追求最高精度而是得到可解释的结论这个定位决定了特征选择和模型评估的方式都和机器学习竞赛完全不同。5. 源码、文档与 PPT把分析项目交付成“高分作品”5.1 源码目录怎么组织才算工程化这类项目在答辩时被问得最多的问题就是“哪部分是你写的”。如果所有代码堆在两个文件里这个问题很难回答。推荐按执行顺序组织目录用编号让任何人拿到源码都能按序复现house_price_analysis/ ├── README.md ├── requirements.txt ├── data/ │ ├── raw/ # 采集原始 CSV │ └── clean/ # 清洗后数据 ├── scripts/ │ ├── 01_crawl.py │ ├── 02_clean.py │ ├── 03_eda.py │ └── 04_model.py ├── docs/ │ └── 分析报告.md └── slides/ └── 答辩资料.pptxscripts 里的编号就是执行顺序04_model.py 直接读取 03_eda.py 生成的中间结果每一步的输出都有明确去向。requirements.txt 必须锁定关键版本Pandas、statsmodels、requests 缺一不可。5.2 文档说明里必须写清楚的三件事文档说明不是代码注释的合集而是让别人不看代码也能复现流程。三件事必须写环境依赖版本包括 Python 大版本和依赖库版本复现步骤从安装依赖到运行哪个脚本再到产物位置核心结论用两三段话说明分析发现了什么。评分者通常先看文档再看代码文档写得清楚等于给项目加分。5.3 PPT 资料的叙事线问题、数据、结论不是贴代码PPT 是给评审看的故事线不是代码仓库的投影。建议按四段走研究问题与数据来源、清洗与特征工程的难点、可视化发现、回归结论。每页只放一个核心图表或一个核心数字比如“楼龄每增加 1 年总价平均下降 X 万元”这种可验证的结论比满屏代码有说服力得多。演示时被问到数据来源就讲采集流程被问到结论就指向回归系数表所有素材都在源码和文档里现场翻得出来项目才立得住。本文还有配套的精品资源点击获取