Python二手房数据采集与可视化:毕业设计实战指南
2026/9/23 10:41:08 网站建设 项目流程

简介:面向Python课程设计或毕业设计的二手房数据采集与可视化分析项目源码与配套资料包,特别适合高校学生完成期末大作业、毕业设计答辩时参考。项目完整覆盖数据采集、清洗、存储、分析与可视化全流程:通过Requests、BeautifulSoup等网络爬虫库抓取链家、房天下等平台的房价、面积、地理位置等字段,利用Pandas完成重复值去除、缺失值处理和数据格式转换,并将结果写入SQLite、MySQL或MongoDB数据库;随后借助Matplotlib、Seaborn、Plotly等工具,以柱状图、折线图等图表清晰呈现各城市二手房价格分布、面积区间与趋势变化。整包为zip压缩包,大小约48.05MB,核心内容包含可运行的Python源码与全套PPT汇报资料,既能直接用于答辩展示,也便于二次改进扩展。目前已有691人学习下载,适合希望快速搭建同类爬虫与可视化项目的Python学习者。

1. 二手房数据采集与可视化分析:一个毕业设计题目的真实分量

答辩现场,导师随口问一句“房源数据从哪抓的、抓了多少,怎么证明可靠”,不少同学当场卡壳。这个「基于 Python 的二手房数据采集及可视化分析」题目,要的不是写个爬虫,而是把整条链路走通:选数据源、写采集脚本、清洗入库、做可视化分析,用图表回答几个房价相关的实际问题,最后把源码和 PPT 整理成能交付的毕业设计。它适合计算机、大数据、信息管理专业的本科生,也适合想借完整项目练手 Python 数据分析的从业者。技术栈很常规:requests、BeautifulSoup、pandas、Pyecharts 加 Matplotlib,不玩冷门框架。真正拉开分数的是细节——字段设计有没有先行、单位有没有统一、每张图有没有配一句结论。下面按采集、清洗、可视化、避坑、交付的顺序,把每一步的做法、参数和坑位讲透。

2. 数据采集:先定数据源与字段,再写第一版爬虫

采集是整个项目的起点,也是最容易让新手一头扎进去出不来的环节。很多人打开链家首页就开写正则,折腾两天发现页面改了个 class 名字就全废了。正确顺序是:先选数据源,再定字段清单,最后才写采集代码。这一步想清楚了,后面清洗、可视化、答辩都会轻松很多。

2.1 数据源选型:链家、贝壳、安居客、房天下怎么挑

二手房数据平台就那几个,看起来都能抓,实际体验差别很大。按毕业设计里最常见的做法,我列了一张对比表给你参考:

数据源页面形态反爬强度字段完整度适合程度
链家静态 HTML + 分页中等高,含总价/单价/面积/朝向首选
贝壳数据走接口 JSON较强,接口带签名高,数据最规整可选
安居客动态渲染 + 登录墙强,频繁验证码不建议
房天下静态混合字段较乱,重复多备选

我一般建议毕业设计首选链家。原因有三个:第一,列表页是纯静态 HTML,用 requests 加 BeautifulSoup 就能解析,不需要上 Selenium 这种重量级工具;第二,每套房子的总价、单价、面积、朝向、楼层、装修都集中在同一条记录里,字段齐全,不用跨页拼接;第三,URL 分页规律明显,ershoufang/pg2pg3这样逐页加号就行,代码写起来没有玄学。

贝壳的数据质量其实最好,但数据是前端通过接口异步加载的 JSON,带签名参数,直接请求拿不到,得先分析 XHR 再拼接参数,对毕业设计来说复杂度偏高。安居客的反爬比较激进,翻几页就弹验证码,调试时间会被拖得很长,我的血泪经验是别碰。房天下虽然好抓,但列表页经常混入车位、商铺,清洗阶段要多花不少时间。

有一点底线必须守住:只采集公开页面展示的房源信息,控制采集频率,数据仅用于课程设计与学术分析,这是此类项目的通用前提。不要上高并发,不要去绕验证码,用随机延时把请求间隔拉开,既保护目标站点,也保护你自己的进度。

2.2 字段清单先行:先定分析目标,再定抓什么

写爬虫之前,先把分析问题列出来。导师大概率会问“你想通过这批数据回答什么问题”,这个问题列表就是你的字段设计依据。常见的分析目标有五个:

  1. 各行政区域的二手房均价差异;
  2. 面积与总价的相关系数;
  3. 户型结构的分布比例;
  4. 朝向、楼层对单价的影响;
  5. 总价段位的集中区间。

围绕这五个问题,字段清单自然就出来了:小区、区域、板块、户型、面积、朝向、楼层、装修、总价、单价。每个字段都能在后续分析里对应一个用途,没有一个是凑数的。区域和板块一定要单独采集,因为后面可视化要做区域维度的聚合,如果只有小区名,区域信息还得靠地理编码去补,非常麻烦。

这里有个过来人的建议:宁可多抓一个字段,也不要少抓。链家列表页附带的“关注人数”“挂牌周期”这类字段,答辩时拿“关注人数和总价的关系”做一个简单分析,比单纯描述价格分布更能体现思考深度。但也不要贪多,字段超过十五个,清洗时每多一列就多一分出错的可能。采集阶段保留页面原始值,不要在爬虫里做“总价除以面积乘一万换算单价”这种加工,原始字段进库,加工统一放到清洗阶段做,口径才不会乱。

2.3 最小爬虫:requests + BeautifulSoup 跑通第一页

字段定了就动手。环境很简单,按 Python 安装教程装好 3.8 以上的版本,再pip install requests beautifulsoup4就行。下面这个脚本是采集一个城市多个行政区域列表页的最小实现,重点看解析函数怎么写:

import requests from bs4 import BeautifulSoup HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9", } def parse_list_page(html): """解析链家二手房列表页,返回房源字典列表""" soup = BeautifulSoup(html, "html.parser") items = soup.select(".sellListContent li .info") result = [] for item in items: title_el = item.select_one(".title a") if not title_el: # 页面结构变化时这一行会救你 continue house_info = item.select_one(".houseInfo").get_text(strip=True) pos = item.select_one(".positionInfo") pos_text = pos.get_text(strip=True) if pos else "" tokens = pos_text.split() # 常见格式:小区名 区域 板块 total_el = item.select_one(".totalPrice span") unit_el = item.select_one(".unitPrice span") parts = house_info.split("|") house = { "标题": title_el.get_text(strip=True), "小区": tokens[0] if tokens else "", "区域": tokens[-2] if len(tokens) >= 2 else "", "板块": tokens[-1] if len(tokens) >= 3 else "", "户型": parts[0] if len(parts) > 0 else "", "面积": parts[1].replace("平米", "") if len(parts) > 1 else "", "朝向": parts[2] if len(parts) > 2 else "", "装修": parts[3] if len(parts) > 3 else "", "楼层": parts[4] if len(parts) > 4 else "", "总价": total_el.get_text(strip=True) if total_el else "", "单价": unit_el.get_text(strip=True).replace("元/平", "") if unit_el else "", } result.append(house) return result

这套代码有个细节值得说明:解析任何列表页,先用soup.select定位房源条目容器,再在每条里逐字段取值,而不是正则一把梭。houseInfo的文本用竖线分隔,按split("|")拆开就能拿到户型、面积、朝向、装修、楼层五个子字段,这是链家这类平台的常见结构。所有get_text(strip=True)都是为了同时去掉标签和首尾空白,避免清洗阶段再做一轮字符串处理。

接着是主流程,逐页请求并把结果存盘:

import csv, time, random def save_to_csv(data, path): if not data: return with open(path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=list(data[0].keys())) writer.writeheader() writer.writerows(data) def main(): city = "sh" # 城市拼音,sh 表示上海 regions = ["pudong", "xuhui", "minhang", "putuo", "jingan", "huangpu"] all_data = [] for region in regions: for page in range(1, 6): # 每个区域抓前 5 页 url = f"https://{city}.lianjia.com/ershoufang/{region}/pg{page}/" try: resp = requests.get(url, headers=HEADERS, timeout=10) if resp.status_code != 200: print(f"区域 {region} 第 {page} 页返回 {resp.status_code}") break all_data.extend(parse_list_page(resp.text)) print(f"区域 {region} 第 {page} 页完成,累计 {len(all_data)} 条") except requests.RequestException as e: print(f"请求失败:{e}") time.sleep(random.uniform(1, 3)) # 随机延时,控制频率 save_to_csv(all_data, "house_raw.csv") print(f"共采集 {len(all_data)} 条,已保存到 house_raw.csv") if __name__ == "__main__": main()

这里的参数都是按“够用且不容易被反爬识别”的标准设的:timeout=10防止某个请求卡死拖垮整个任务;time.sleep(random.uniform(1, 3))把请求间隔控制在 1 到 3 秒随机波动,接近人工浏览的节奏;每个区域抓 5 页约 150 条,六个区域合计近千条,对毕业论文的可视化分析完全够用。如果只是演示效果,两个区域各 3 页也能跑通全流程。区域拼音要以链家网页 URL 里的实际字段为准,打开网页点一个区域就能看到。保存文件时用encoding="utf-8-sig",这一步直接决定后续 Excel 打开 CSV 会不会乱码,先留个印象,第 5 章还会专门讲。

跑通第一页是整条链路上最关键的里程碑。第一次运行可能因为选择器失效、请求头被识别等各种原因翻车,遇到问题先打印resp.status_codesoup.title确认拿到的是不是目标页面,再排查解析逻辑,这个习惯能省掉大量调试时间。

3. 数据清洗与存储:把“能抓”变成“能用”

采集完成只是拿到原料。爬下来的数据里一定有重复条目、缺失字段、类型错乱和单位不一致这类暗坑,直接拿去画图会得到一堆不可信的结论。这一章讲清洗与存储,目标是把原始 CSV 变成一个口径清晰、类型正确的 DataFrame,再选一个答辩时讲得清楚的存储方式。

3.1 清洗五连:去重、类型、缺失、异常、文本

我习惯用 pandas 一条流水线处理,顺序固定为去重、类型转换、缺失值、异常值、文本规整。这个顺序有讲究:先干掉重复数据,后面的统计才不受干扰;先转好类型,缺失值和异常值才能用数值运算去判断。

import pandas as pd df = pd.read_csv("house_raw.csv") print(df.shape, df.dtypes) # 先看规模与类型,不要急着洗 # 1) 去重:同一条房源可能被多个区域页重复抓到 df = df.drop_duplicates(subset=["小区", "户型", "面积", "总价"]) # 2) 类型转换:面积、总价、单价全部转数值 df["面积"] = pd.to_numeric(df["面积"], errors="coerce") df["总价"] = pd.to_numeric(df["总价"], errors="coerce") df["单价"] = pd.to_numeric(df["单价"], errors="coerce") # 3) 缺失值:朝向缺失补“未知”,单价缺失用总价/面积回填 df["朝向"] = df["朝向"].fillna("未知") df["单价"] = df["单价"].fillna(df["总价"] / df["面积"] * 10000) # 4) 异常值:面积 20 平以下或 500 平以上,总价低于 10 万,多为录入错误 df = df[(df["面积"] >= 20) & (df["面积"] <= 500)] df = df[(df["总价"] >= 10)] # 5) 文本规整:去掉区域名首尾空格,统一装修文案 df["区域"] = df["区域"].str.strip() df["装修"] = df["装修"].str.replace("精装修", "精装").replace("简装修", "简装") df.to_csv("house_clean.csv", index=False, encoding="utf-8-sig") print(df.describe())

逐个参数说清楚。drop_duplicatessubset我选的是“小区 + 户型 + 面积 + 总价”四个字段组合,这套组合在真实业务里足够标识“同一套房子”,单用小区名会把同一小区不同楼栋的房源误删。pd.to_numericerrors="coerce"是关键,页面上出现“暂无数据”这类文本时,它会把该单元格置成 NaN,而不是让整列转换直接报错,这是数据采集项目里的标准防御写法。单价缺失回填时注意单位:总价单位是万元,单价单位是元/平方米,所以回填公式里要乘 10000。

异常值的边界不是拍脑袋定的,而是依据房源真实分布:在售住宅面积几乎没有低于 20 平的,大于 500 平的多为别墅,样本少且单价分布特殊,会影响整体均值。答辩时老师问“边界为什么这么设”,你答“先看 describe 的分布,再结合城市实际情况定阈值”,这就是加分的逻辑。清洗完务必跑一遍df.describe(),如果“均价”在几万这个量级内浮动,说明方向和单位都对了。

3.2 存储选型:CSV 够用,SQLite 更好答辩

清洗后的数据存哪里,取决于数据量和答辩策略。三千条以内,CSV 完全够用,pandas 直接读,Excel 也能打开检查,零学习成本。但如果论文里要写“数据库设计”这一章,或者想演示 SQL 查询,就用 SQLite 落库,一条代码就完成迁移:

import sqlite3 import pandas as pd df = pd.read_csv("house_clean.csv") conn = sqlite3.connect("house.db") df.to_sql("house", conn, if_exists="replace", index=False) conn.execute("CREATE INDEX idx_region ON house(区域);") conn.execute("CREATE INDEX idx_price ON house(总价);") conn.commit() conn.close()

df.to_sqlif_exists="replace"表示每次运行都重建表,适合重复清洗的场景;index=False保证不把 DataFrame 的索引写进去变成多余的一列。建两个索引是给答辩准备的谈资:区域索引加速分组聚合,总价索引加速区间查询,这样“数据库设计”章节有实际内容,而不是干巴巴写一句“建了索引”。

SQLite 的好处是单文件、跨平台、零配置,交材料时把house.db一起放进数据目录,老师用任意 SQLite 工具都能打开复核。相比 MySQL,不用装服务、不用配账号,毕业设计的体量完全不需要引入更重的组件。

3.3 清洗后自检:先信数字,再信图

数据洗干净不等于数据可信。画图之前,我的习惯是做三道自检,每题都不超过三行代码:

# 自检 1:全表统计信息是否有明显不合理值 print(df.describe()) # 自检 2:按区域抽 3 条,人工核对页面 sample = df.groupby("区域").sample(3, random_state=0) print(sample[["小区", "区域", "总价", "面积", "单价"]]) # 自检 3:确认清洗后没有残留重复 print("重复条数:", df.duplicated(subset=["小区", "户型", "面积", "总价"]).sum())

自检 2 值得展开:groupby("区域").sample(3)每个区域随机抽 3 条打印出来,然后打开链家对应区域的列表页,人工核对这三条的总价、面积是否一致。抽样核对能发现一种很隐蔽的错误——解析字段错位。比如某个区域的页面 div 结构略有不同,导致“朝向”被填成了“装修”,describe 和重复检查都发现不了,只有人工看几眼原始页面才能暴露。核对没问题再进可视化,这时候画的每一张图才有底气写进论文。

4. 可视化分析:四张图把房价规律讲到老师点头

数据准备就绪,进入整个项目里视觉产出最高的一步。二手房数据分析的套路已经很成熟,关键不是图的数量,而是每张图能不能回答一个具体问题。这一章讲工具分工、四张必做图的实现,以及从图到结论的“最后一公里”。

4.1 工具分工:Pyecharts 做交互演示、Matplotlib 做论文配图

可视化这一步最常见的坑是工具混乱。Pyecharts 和 Matplotlib 不是二选一,而是分工:Pyecharts 生成交互式 HTML,鼠标悬停能看到数值、支持缩放,答辩演示时打开浏览器现场拖动,效果比静态图好一截;Matplotlib 配 Seaborn 生成静态 PNG,适合插图进论文,排版稳定、打印不糊。毕业设计通常两者都上,README 里写清楚“演示打开 HTML,论文插图用 PNG”。

特别提醒版本问题。Pyecharts 0.5.x 时代的老教程满天飞,而pip install pyecharts现在装的是 1.x,两代 API 差异巨大:新版用add_xaxisadd_yaxis,老版用add()直接传数据,拿老代码粘进来第一行就会报TypeError。安装后先跑一句python -c "import pyecharts; print(pyecharts.__version__)"确认版本,凡是看到教程里.add()加字典传参的用法,直接跳过。这是新手最容易翻车的点。

4.2 四张必做的图:区域均价、面积-总价、户型分布、朝向箱线图

第一张是区域平均单价条形图,回答“哪个区最贵”:

from pyecharts.charts import Bar from pyecharts import options as opts region_mean = df.groupby("区域")["单价"].mean().sort_values() bar = ( Bar() .add_xaxis(region_mean.index.tolist()) .add_yaxis("平均单价(元/㎡)", region_mean.round(0).tolist()) .set_global_opts( title_opts=opts.TitleOpts(title="各区域二手房平均单价"), xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=30)), ) ) bar.render("output/region_price.html")

注意先groupby("区域")["单价"].mean()再画图,统计口径是“区域平均单价”而不是“区域总价求和”,避免面积大的区天然占便宜。x 轴标签旋转 30 度是为了应对区域名偏长的场景,如果标签仍然重叠,把rotate调到 45。画图前先建好 output 目录,代码里用os.makedirs("output", exist_ok=True)最省事。

第二张是面积与总价的散点图,回答“房子越大越贵吗”:

from pyecharts.charts import Scatter s = ( Scatter() .add_xaxis(df["面积"].tolist()) .add_yaxis("总价(万元)", df["总价"].tolist()) .set_global_opts( title_opts=opts.TitleOpts(title="面积与总价散点图"), xaxis_opts=opts.AxisOpts(name="面积(㎡)"), yaxis_opts=opts.AxisOpts(name="总价(万元)"), ) ) s.render("output/area_price.html")

散点图能直观看出正相关,但答辩时最好补一条回归线把相关性量化。常见做法是用numpy.polyfit拟合一次函数,或者在 Matplotlib 里用seaborn.regplot一行出图。这里提前给你一个重点:答辩老师几乎必问“相关系数是多少”,所以df["面积"].corr(df["总价"])这个值要提前算好记在心里,别临场现算。

第三张是户型分布饼图,第四张是朝向箱线图,这一组放一起看户型和朝向对价格的影响:

from pyecharts.charts import Pie huxing_pair = [list(z) for z in df["户型"].value_counts().head(6).items()] pie = ( Pie() .add("", huxing_pair, radius=["40%", "70%"]) .set_global_opts(title_opts=opts.TitleOpts(title="户型分布 Top6")) ) pie.render("output/huxing_pie.html") import seaborn as sns import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False order = ["东", "南", "西", "北"] sns.boxplot(data=df, x="朝向", y="单价", order=order) plt.title("不同朝向的单价分布") plt.tight_layout() plt.savefig("output/chaoxiang_box.png", dpi=200)

huxing_pair那行是 Pyecharts 1.x 的固定写法,value_counts().head(6).items()生成“户型-数量”对列表,取前 6 种避免饼图出现二十个小扇形。Matplotlib 的中文字体配置font.sans-serif必须写在画图之前,否则标题会变成一排方框,这是每个 Python 爬虫可视化项目都会遇到的坑,第 5 章再细说。

4.3 每张图都配一句“所以呢”

图表是证据,不是结论。把图往 PPT 里一贴就完事,是答辩低分的重灾区。我的做法是每张图配一段三行的读图说明:这张图展示了什么、发现了什么规律、规律可能的原因是什么。比如区域均价图读完写“黄浦、静安均价明显高于外环区域,与城市中心区位一致”;面积-总价散点图写“总价与面积呈明显正相关,但 300 平以上房源样本稀疏,结论对外环大户型参考意义有限”。最后这句“样本有限”非常加分,它向老师传递一个信号:你知道自己结论的边界在哪。

如果追求效果,还可以把四张图表组合成一个带选项卡的 HTML 页面,用 Pyecharts 的Tab组件,这就是检索量很高的“python 爬虫可视化界面”的轻量实现,不需要额外学 Flask。Tab 组件三行代码:tab = Tab()tab.add(bar, "区域均价")tab.render("output/all.html"),四张图并到一个文件里,演示时点选项卡切换,观感比逐个打开文件专业得多。

5. 避坑排查:从采集被拦到图表空白的 5 个真实案例

这一章把整个项目里最容易踩、踩了又最浪费时间的五个问题单独拿出来,每条按“现象、原因、解决”三步讲清楚。这些都是我前后带过几个类似项目之后总结出来的高频事故,你在复现时大概率会至少遇到其中两三个。前四个集中在采集和展示环节,最后一个藏在数据口径里,往往到画图那天才暴露。

5.1 请求头缺失导致 301 与 403

现象:代码写好运行后,resp.status_code返回 301 或 403,打印出的页面标题不是链家而是跳转提示页,parse_list_page返回空列表,爬虫跑完零条数据。

原因:requests 默认的User-Agentpython-requests/x.x,平台反爬一眼就能识别这不是浏览器。301 通常是平台把你的请求重定向到验证页或首页,403 则是直接拒绝访问。很多教程只让你设一个 UA,但只设 UA 也有概率被拦,因为平台还会校验 Accept 和 Accept-Language。

解决:从浏览器开发者工具 Network 面板复制完整的请求头,至少包含User-AgentAcceptAccept-Language三个字段,并保持大小写一致。请求间隔设置在 1 秒以上,用random.uniform(1, 3)随机波动。如果连续翻页仍然被拦,说明触发频率限制,停一分钟再继续,而不是硬着头皮加并发。毕业设计的数据量几百条就够,慢就是快。

5.2 选择器全空:页面结构和教程不一样

现象:soup.select(".sellListContent li .info")返回空列表,程序不报错,日志却显示累计 0 条。

原因:链家这类平台会不定期改版,class 命名和 DOM 层级都会调整,网上的教程代码大多基于某个时间点的页面结构,直接照抄很容易失效。另一个容易被忽视的原因是请求被重定向到了登录页或安全验证页,你解析的根本不是列表页。

注意:把选择器提取成文件顶部的常量,改版时只改一处,不要在循环里到处搜 class 名。

解决:定位问题分两步。第一步打印resp.status_codesoup.title,确认拿到的是列表页还是跳转页;第二步在浏览器里右键检查房源卡片的真实结构,重新写选择器。解析每条记录时用if not title_el: continue这样的防御写法兜底,单条结构异常不影响整批数据,这是数据采集项目里的通用习惯。

5.3 Excel 打开 CSV 全乱码

现象:采集完成用 Excel 直接双击打开house_raw.csv,中文全部显示成乱码;用 pandas 读同一份文件却完全正常。

原因:CSV 以 UTF-8 编码写入,不含 BOM 头,Excel 在中文系统下默认按 GBK 解码,于是每个汉字都成了乱码。pandas 自带编码探测所以能正确读出,这容易让人误以为是数据写坏了。

解决:写入时统一用encoding="utf-8-sig",这个编码会在文件头部写一个 BOM 标记,Excel 识别到后自动按 UTF-8 解码。采集脚本和清洗脚本里所有to_csvopen都带上这个参数。另外保存路径最好用纯英文目录,某些中文路径在跨设备拷贝后也可能引发编码问题。

5.4 Pyecharts HTML 空白,Matplotlib 中文变成方框

现象:bar.render("output.html")运行成功没报错,浏览器打开却只有标题没有图表;另一边 Matplotlib 出图后中文标题变成一排方框。

原因:Pyecharts 空白通常有两种,一是生成路径含中文或特殊字符,浏览器以file://方式加载本地资源时路径解析失败;二是图表容器没有正确拿到数据,常见于 pyecharts 版本和教程不匹配。Matplotlib 的方框是字体的锅,默认字体 DejaVu Sans 不含中文字形,标题和坐标轴标签全部显示不了。

解决:Pyecharts 的生成路径固定用英文,图表统一放进output/目录。如果打开仍是空白,用浏览器开发者工具 Console 看报错,十有八九是 JS 资源加载路径问题。Matplotlib 在画图脚本顶部写两行固定配置:plt.rcParams["font.sans-serif"] = ["SimHei"]plt.rcParams["axes.unicode_minus"] = False,前者指定中文字体,后者防止负号显示成方块。如果在 Linux 或无中文字体的环境跑,先安装 Noto Sans CJK 并把字体名写进配置。

5.5 区域均价算出来上亿:单位与类型错乱

现象:数据清洗后画区域均价图,某几个区域均价高达几千万上亿,或者df.describe()里总价均值是正常值的几百倍。

原因:几乎都是类型和单位混了。常见的是“单价”列没有转成数值,astype(float)遇到“暂无数据”报错后改用errors="coerce",但后续聚合时 NaN 参与计算导致结果异常;另一种是总价单位万、单价单位元/平方米,回填公式漏乘 10000,把“万/平方米”当成“元/平方米”用了。

解决:清洗完第一件事跑df.dtypes,确认三列价格都是float64;再用df[df["单价"] < 10000]查一下有没有异常低的值,正常城市均价不会低于一万这个量级,查出来的样本就是单位问题的线索。回填公式写成df["总价"] / df["面积"] * 10000,上一行注释写清楚“总价单位万元,单价单位元/㎡”,防止三天后自己回来看代码又迷糊。数据这行最怕的从来不是算法难,而是口径乱,单位统一这件事值得用一行注释写死。

6. 交付与答辩:源码、PPT 与演示的三项自检技巧

最后一步不是写代码,而是把成果变成老师能快速看懂、愿意给高分的交付物。项目源码和 PPT 文件是毕业设计的门面,我见过数据做得不错、但源码目录乱到老师根本找不到入口的同学,最后分数平平。三项自检技巧如下。

6.1 源码包结构:让老师五分钟看懂

源码包的目录结构就是你的代码脸面。常见的规范结构是这样:

project/ ├── spider/ # 采集脚本 ├── clean/ # 清洗脚本 ├── analysis/ # 可视化脚本 ├── data/ # 原始与清洗后的数据 ├── output/ # 生成的 HTML 与 PNG └── README.md # 运行步骤与依赖说明

README 里用三行写清楚“安装依赖、按顺序运行、产出在哪”,依赖锁进requirements.txt。用 PyCharm 或 VS Code 打开项目根目录,按 README 顺序跑一遍,这就是老师复核你代码的标准路径。

6.2 PPT 一页一图一句话

PPT 的逻辑按“背景与问题、技术路线、数据采集、数据清洗、可视化分析、结论”六部分走,每页最多一张核心图加一句话结论,先给结论再解释过程。把采集到的原始条数、清洗后的有效条数标进图注,严谨性一眼就能看出来。

6.3 演示别现场爬数据

最后一个技巧也是最大的教训:答辩现场演示,永远用离线数据,不要当场跑爬虫。网络波动、反爬触发、页面改版,任何一个环节出问题都会让演示卡壳,而老师不会关心是你的网络不好还是脚本有 bug。提前把采集好的 CSV 和渲染好的 HTML 图表放进项目目录,现场演示的脚本只跑清洗和可视化两部分,保证六十秒内能看到完整产出。如果硬要展示采集过程,用录好的屏,控制在两分钟内。

这套流程走完,你拿到的不仅是一个能答辩的毕业设计,更是一套“先定问题、再采数据、清洗先行、图配结论”的做事方法。我自己早年做第一个数据类项目时,就栽在顺序上——先写了三天爬虫,再回头想问题,结果字段不够又回去重抓。后来养成先列问题和字段清单的习惯,类似项目再没返过工。希望这一篇能帮你避开同样的弯路,少熬几个夜,把精力花在真正加分的地方,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询