二手房数据爬取与可视化:Python毕设实战指南

发布时间:2026/10/29 18:26:09

二手房数据爬取与可视化:Python毕设实战指南 简介这是一份基于Python的二手房数据爬取与可视化分析毕业设计源码及PPT文档报告经导师指导并获评98分适合计算机相关专业学生用于毕业设计、课程设计或数据分析项目实践。项目围绕二手房信息采集与分析展开包含爬虫抓取、数据清洗、数据存储到可视化展示的完整链路能够帮助读者掌握Requests/Scrapy等常用爬虫工具与Pandas数据处理方法并借助ECharts等前端组件生成直观分析图表。压缩包为ZIP格式共156个文件总体积约40MB其中包含18个Python脚本、18个CSV数据文件、15个HTML页面、11个JS文件、8个TXT说明文本以及PPT报告与大量PNG可视化截图脚本用于抓取与清洗逻辑CSV便于查看原始及处理后的数据HTML/JS对应可视化界面图片和PPT则可直接用于报告展示。目前已有142人学习浏览。资源内附有多版本CSV数据及配置信息可对照理解不同清洗阶段的结果差异源码均经过本地运行调试具备较高的可参考性与可复用性适合二次开发与答辩讲解。1. 基于 Python 的二手房爬取与可视化一套能撑起答辩的毕设骨架很多同学把“二手房数据爬取与可视化分析”选做毕业设计最开始想得很简单会 requests 能拿到网页用 matplotlib 画几张图就完事。实际上手才发现页面反爬、字段缺失、清洗困难、图表不落地每一项都能卡掉一周时间。这篇笔记想给你一条可复现的路径怎么选工具、怎么写爬虫、怎么清洗、怎么出图最后还能对着 PPT 把每一步讲清楚。它适合第一次独立做 Java 之外偏数据方向毕设的同学也适合想快速搭一个完整项目骨架的从业者。你要的不只是“能跑”而是一套能讲出数据链路、能应对答辩追问的工程。2. 二手房爬虫方案选型Requests、Scrapy、Playwright 怎么选数据存哪里2.1 为什么用 Python 而不是现成采集器市面上一键采集工具很多但毕设要求你讲清楚“数据是怎么来的”所以核心采集逻辑必须自己写。Python 在这一步的优势非常明显requests 和 BeautifulSoup 都是轻量库写一个最小爬虫只要二十多行Pandas 处理表格数据几乎不需要额外学习成本matplotlib、pyecharts 能直接生成答辩要用的图片和 HTML。相比 Java 或 CPython 的字符串处理和 CSV、SQLite 交互都更省时间适合把主要精力留给“分析”而不是“工程配置”。常见做法是使用requests BeautifulSoup作为主力而不是一上来就上 Scrapy。Scrapy 性能确实强但它的 Spiders、Middlewares、Item Pipeline 概念会分散新手注意力调试成本也高。只有你的房源数据量超过 5 万条、需要每天增量抓取时Scrapy 的并发场景和Item Pipeline才值得投入。如果只是毕设级别的数据量几千条数据足够支撑分析用同步请求加随机等待反而更好控制不容易把目标网站压出问题。Playwright 或 Selenium 也不是首选。二手房平台的主流列表页和详情页大多还是服务端渲染HTML 里直接有房源字段不需要等待 JavaScript 执行。只有当你发现某个字段必须点击“加载更多”才能出现或者页面用 Vue/React 动态渲染时才需要引入 Playwright。它的优点是自动等待和截图方便缺点是内存占用大、环境依赖多打包到答辩演示机器时容易翻车。2.2 房源字段表先定义数据结构再去写爬虫写爬虫前一定要先想清楚要哪些字段。毕设常见做法是从列表页拿基本信息再进详情页补全挂牌时间、关注人数这类动态字段。表格数据可以设计如下字段名类型示例来源house_idTEXT字符串型主键列表页链接中提取cityTEXT北京列表页链接携带districtTEXT朝阳列表页biz_circleTEXT望京列表页titleTEXT望京新城 2室1厅列表页layoutTEXT2室1厅1卫列表页areaREAL87.3列表页抓取后清洗orientationTEXT南北列表页floor_infoTEXT低楼层/共22层列表页total_priceREAL760列表页单位是万unit_priceREAL87000列表页单位是元/平米build_yearINTEGER2003详情页listing_timeTEXT2024-05-20详情页crawler_timeTEXT2025-06-02 12:00爬虫自动记录存储我一般用 SQLite而不是 MySQL。数据库就一个.db文件答辩时直接拷贝不需要另装服务。你只需要创建一张house表把house_id设为 PRIMARY KEY后续重复跑爬虫可以用INSERT OR REPLACE规避重复数据。提示不要把原始 HTML 存进数据库。先解析成结构化字段再入库遇到解析报错宁可丢掉这条记录也不要存“半成品”。2.3 先做页面结构侦察确认数据在 HTML 里而不是接口里正式写爬虫前先花 10 分钟手动确认页面结构。回到拿到标题后你需要打开目标房产网站的任意一个城市二手房列表页使用浏览器开发者工具在 Elements 面板里搜索一个房源标题。如果标题出现在 HTML 里说明是服务端渲染requests 方案可行如果标题出现在 Network 面板的 XHR 响应里而 HTML 中没有则需要模拟接口请求或上 Playwright。观察列表页 URL 规律也很重要。常见做法是把列表页的翻页参数找出来比如 URL 里会带pg2、page2、l0这类参数。第一次手动翻两页看 URL 变化规律然后再写爬虫。不要上来就对着一个 URL 写死因为不同城市、不同板块的 URL 结构差异很大最好把城市名和页码作为参数传入函数。另外要尊重网站的 robots 协议。毕设里你只抓取少量公开信息但也要控制单次运行的总量比如每页间隔随机 1 到 3 秒总页数不跑满避免给服务器造成压力。这一点答辩时是能主动说出来的加分项。3. 爬虫实现从列表页翻页到房源字段入库跑通最小版本3.1 最小爬虫骨架requests BeautifulSoup 拿下列表页这里我给出一个最小可运行骨架实际页面需要你按自己的目标页面微调 CSS 选择器。我用fxx二手房-第{page}页模拟链接你替换为真实 URL 模板即可。写代码时强烈建议把请求头单独定义成一个变量尤其是User-Agent这样被排查时至少看起来是正常浏览器。import requests from bs4 import BeautifulSoup import time import random headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.5, } def fetch_list_page(city, page): # 实际项目里把 city 和 page 拼到目标 URL 中 url fhttps://example.com/{city}/ershoufang/pg{page}/ resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() resp.encoding resp.apparent_encoding return resp.text html fetch_list_page(beijing, 1) soup BeautifulSoup(html, html.parser) # 先用一个宽松的选择器抓所有房源卡片再用循环提取 items soup.select(li[class*house]) print(len(items))这段代码逻辑很直白fetch_list_page负责请求raise_for_status()会在遇到 40x/50x 时主动抛异常避免你拿到的是一份错误页面还继续解析。resp.encoding resp.apparent_encoding是解决中文乱码的关键比如某些服务器返回的 Content-Type 没有明显 charset用apparent_encoding能推断出 GBK 或 UTF-8。items的选择器是占位写法真实页面里你需要在开发者工具里看房源卡片是一个li还是div再决定选择器。打印len(items)是为了确认“解析到数据了”如果数量是 0说明选择器不对。3.2 解析房源字段并用生成器逐条产出拿到items后不要在一个函数里写几百行最好把“列表页 HTML 转字段”单独拆成函数。每个房源卡片里的字段位置可能不同我一般会先把所有文本get_text(stripTrue)取出来再用正则匹配面积和单价因为网页里面积和单价通常混在一段文字里。import re def parse_list_item(item): # 链接里提取唯一 ID a_tag item.select_one(a) if not a_tag: return None href a_tag.get(href, ) house_id re.search(r/\d\.html, href) # 按实际链接调整 if not house_id: return None house_id house_id.group().replace(/, ).replace(.html, ) # 标题和基本字段 title a_tag.get_text( , stripTrue) # 从整段文本中找面积例如87.3平米 text item.get_text( , stripTrue) area_match re.search(r(\d\.?\d*)平米, text) area float(area_match.group(1)) if area_match else None # 总价和单价常见格式为总价760万和单价87000元/平米 price_match re.search(r(\d)万, text) total_price float(price_match.group(1)) if price_match else None unit_match re.search(r单价(\d)元, text) unit_price float(unit_match.group(1)) if unit_match else None return { house_id: house_id, title: title, area: area, total_price: total_price, unit_price: unit_price, raw_text: text, crawler_time: time.strftime(%Y-%m-%d %H:%M:%S), }这里的关键逻辑是通过正则从整段文本中抽取数值。二手房页面的文字排版经常变化但“87.3平米”“760万”“单价87000元”这类文本相对稳定比依赖 CSS 位置更可靠。返回None时主程序应该跳过因为可能是一条广告位或者不完整卡片。house_id从链接里提取是防止后续重复的关键即使标题变了只要链接没变它就是同一条房源。3.3 翻页循环、异常重试和数据入库跑翻页前先想好你要多少数据。常见做法是“每个城市抓前 50 页每页 30 条大约 1500 条”这个量已经能支撑大多数统计分析。翻页循环里必须控制节奏我用随机 sleep 而不是固定 sleep因为固定间隔更容易被识别为机器行为。import sqlite3 import time import random def init_db(): conn sqlite3.connect(house.db) conn.execute( CREATE TABLE IF NOT EXISTS house ( house_id TEXT PRIMARY KEY, city TEXT, title TEXT, area REAL, total_price REAL, unit_price REAL, crawler_time TEXT ) ) return conn def crawl_pages(city, max_page3): conn init_db() cur conn.cursor() for page in range(1, max_page 1): try: html fetch_list_page(city, page) soup BeautifulSoup(html, html.parser) items soup.select(li[class*house]) for item in items: data parse_list_item(item) if data is None: continue cur.execute( INSERT OR REPLACE INTO house VALUES (?,?,?,?,?,?,?), ( data[house_id], city, data[title], data[area], data[total_price], data[unit_price], data[crawler_time] ), ) conn.commit() print(f完成第 {page} 页累计 {cur.rowcount} 条) except Exception as e: print(f第 {page} 页失败: {e}) time.sleep(10) time.sleep(random.uniform(1, 3)) conn.close() if __name__ __main__: crawl_pages(beijing, max_page3)INSERT OR REPLACE是解决重复数据的核心第二次重跑同一页不会产生多条相同house_id。rowcount在 SQLite 里只能反映最近一次操作影响的行数所以不要用rowcount做累计统计想打印总数可以单独SELECT COUNT(*)。异常处理这里先睡 10 秒再继续如果连续失败 5 次最好直接退出避免被封后还硬跑。max_page3是我调试时用的值正式跑你按自己需求调大但不要一次从 1 跑到 200太容易触发反爬。4. 数据清洗与可视化分析Pandas 处理脏字段图表沉淀进 PPT 报告4.1 Pandas 清洗数据解决单位、空值和类型问题数据库里存的数据只能算“原始阶段”直接作图会看到大量垃圾值。比如area字段可能是87.3平米字符串total_price可能是760万unit_price可能是87000元/平米。先把它们全部转为浮点数再处理缺失值。import pandas as pd df pd.read_sql_query(SELECT * FROM house, sqlite3.connect(house.db)) df df.drop_duplicates(subsethouse_id) # 面积转浮点 df[area] df[area].astype(str).str.replace(平米, , regexFalse) df[area] pd.to_numeric(df[area], errorscoerce) # 处理价格为 0 的异常 df df[df[total_price] 0] df df[df[unit_price] 1000] # 单价不可能低于1000元/平米 df df[df[area].between(10, 200)] # 面积过滤极端值 # 新增总价/面积 实际单价方便与页面标注单价对比 df[calc_unit_price] (df[total_price] * 10000) / df[area] df[price_diff_ratio] (df[unit_price] - df[calc_unit_price]) / df[calc_unit_price] print(df.describe())这里的errorscoerce会把无法转换的值变成 NaN随后你可以选择删除或用中位数填充但房价数据最好直接删掉异常行因为分布本身是核心分析对象不能用均值填充破坏它。unit_price 1000和area.between(10, 200)是常见的过滤经验太小的面积可能是车位太大的可能是别墅单位不同会引起后面画图时坐标轴失衡。calc_unit_price是校验字段如果页面标注单价和你算出来的单价相差超过 10%多半是房源本身带地下室或面积口径不同这类数据在分析时可以做标记。4.2 从 Excel/CSV 导出再用多种图表验证分布清洗之后最省事的做法是导出 CSV然后用 Excel 或 pandas 画图。如果你想用 pyecharts 做交互图放进 HTML答辩时演示更炫酷但我建议至少先出一张静态散点图因为静态图能直接反映原始数据分布交互图只是锦上添花。import matplotlib.pyplot as plt # 查看价格分布用直方图 plt.figure(figsize(10, 5)) plt.hist(df[total_price], bins30, color#4C72B0, alpha0.8) plt.xlabel(总价万) plt.ylabel(房源数量) plt.title(二手房总价分布直方图) plt.tight_layout() plt.savefig(reports/total_price_dist.png, dpi200) # 面积与总价的关系散点图 plt.figure(figsize(8, 6)) plt.scatter(df[area], df[total_price], s8, alpha0.5, color#C44E52) plt.xlabel(面积平米) plt.ylabel(总价万) plt.title(面积与总价散点图) plt.tight_layout() plt.savefig(reports/area_price_scatter.png, dpi200)直方图能看出主流价格段比如某个城市可能集中在 300 到 500 万散点图能看出面积和总价是否近似线性关系。保存到reports目录是为了后续直接拖进 PPT 文档报告不用再重新运行。dpi200保证放大之后不模糊答辩投影偏大分辨率太低会看起来不专业。4.3 可视化分析维度商圈、环线和板楼塔楼如何出结论只画两张图还不够毕设里必须有“分析结论”。常见做法是按商圈聚合统计每个商圈的挂牌均价、平均面积、套均总价然后找出“门槛最低的 5 个商圈”和“均价最高的 3 个板块”。还可以把楼层信息拆成“低/中/高”三档分析楼层对单价的影响。group df.groupby(district).agg( avg_price(total_price, mean), avg_unit(unit_price, mean), count(house_id, count), avg_area(area, mean) ).round(2) group group[group[count] 5] # 过滤样本过少区域 print(group.sort_values(avg_unit, ascendingFalse).head(10)) group.to_csv(reports/group_by_district.csv, encodingutf-8-sig)样本量小于 5 的区域统计意义很弱直接剔除能避免“一个高价房源拉高整个区域均价”的误判。输出编码用utf-8-sig这样用 Excel 打开 CSV 时不会中文乱码。分析结论最好落在 PPT 的“数据洞察”页例如“总价 400 万以下房源占比超过一半主要分布在城市南部”“面积超过 120 平米的大户型单价平均低于 80 平米小户型”这些才是可视化分析的价值而不是画了图就完事。5. 爬虫毕设避坑403、字段残缺和翻页漏数据的排查记录5.1 列表页返回 403没有完整请求头被服务器识别为脚本现象爬虫一启动第一页就抛出HTTPError: 403 Client Error但浏览器里打开同样的 URL 是正常的。原因服务器根据请求头里的User-Agent、Accept、Accept-Language等字段判断请求不是普通浏览器。很多人只设置了User-Agent就以为够了但漏掉Referer或Accept-Encoding也会触发校验。解决把浏览器开发者工具里 Network 面板中的请求头完整复制下来至少包含User-Agent、Accept、Accept-Language。如果仍然 403尝试增加Cookie从浏览器当前访问的页面拷贝。另外每次请求间隔至少 1 秒不要在测试时用for循环快速连发 10 次。提示403 排查顺序是“请求头 → Cookies → 频率 → 代理”不要把代理放到第一步。5.2 房源数据大量缺失列表页字段被折叠详情页需要二次请求现象清洗时发现build_year和listing_time两列全是 NaN而浏览器里房源卡片下方明明能看到“2003年建、2人关注”这类信息。原因列表页只展示了部分字段有些字段需要点击进入详情页才会返回。也有可能是字段文字不在卡片容器的get_text结果中而是被折叠到了># analysis.py 的简化入口 import argparse import pandas as pd parser argparse.ArgumentParser() parser.add_argument(--input, defaulthouse.csv) parser.add_argument(--output, defaultreports) args parser.parse_args() df pd.read_csv(args.input) df clean_data(df) generate_charts(df, args.output) print(图表已生成到, args.output)6.2 抽 20 条数据人工核对把误差率写进 PPT答辩时最怕老师问“数据准不准”。常见做法是随机抽取 20 条房源打开原始网页人工比对总价、面积、单价然后统计一致率。如果一致率低于 90%说明清洗逻辑有问题先查单位换算和空值处理。我通常会把一致率直接写进 PPT 的“数据说明”页“人工复核 20 条字段匹配 19 条误差率 5%”。这句话比“我爬了几千条”有用得多。6.3 演示顺序按“数据链路”走不要一上来就炫图答辩演示时很多同学先打开可视化大屏老师说“这是怎么来的”就卡住了。我更建议按数据链路顺序先展示数据库截图和 3 条原始记录再展示清洗前后的表格差异最后展示图表。每张图表下面写一句话结论例如“单价超过 10 万的房源集中在 CBD 周边 5 公里内”。最后留一个专门“局限与改进”页面主动说“当前只爬取了 3 个月内挂牌数据未考虑成交数据的时效性”比被老师追问出来要好得多。我自己每次跑完爬虫都会把sleep调大再跑一遍确认不封 IP再把原始 HTML 文件名命名为“城市-页码-时间”这个习惯帮我省了不少返工时间。希望这篇文章里从选型到避坑的这套路径能让你在毕设教室从容演示不用临时手忙脚乱。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/28 15:47:03

t3code:轻量CLI驱动的跨端验证工具(Web/Electron/iOS)

1. 项目概述:t3code 是什么?它解决的到底是谁的问题?“t3code”这个名称乍看像一个缩写、代号,甚至可能是某次内部命名时随手敲下的组合——但它在当前开发者社区中正快速获得真实回响。结合高频热搜词(t3code、CLI、E…

2026/10/28 15:47:03

SRC漏洞挖掘入门:从基础到实战

1. SRC漏洞挖掘入门:为什么需要学习这项技能在当今数字化时代,企业面临的安全威胁日益增多,SRC(Security Response Center)漏洞挖掘已成为安全从业者的核心技能之一。我最初接触SRC漏洞挖掘时,完全是个门外…

2026/10/28 15:47:03

Python内置HTTP服务器的安全风险与防护措施

1. Python内置HTTP服务器的安全隐患全景Python标准库中的http.server模块是开发者常用的轻量级Web服务器工具,只需一行python -m http.server 8000就能快速启动本地文件服务。这个看似便利的功能背后却隐藏着诸多安全风险——去年某科技公司内网数据泄露事件的根源&…

2026/10/29 18:23:13

AI故障诊断Agent技术解析与工业应用实践

1. 项目概述:为什么需要AI故障诊断Agent?在工业生产和设备运维领域,故障诊断一直是个耗时耗力的工作。传统方式依赖工程师的经验积累和规则库匹配,面对复杂系统时响应速度慢、误判率高。我去年参与的一个风电项目就遇到过这种情况…

2026/10/29 18:23:13

AI如何通过知识图谱与NLP技术革新学术写作

1. 项目概述:AI如何重塑学术写作生态作为一名在学术出版行业摸爬滚打十年的从业者,我见证了无数研究者被论文写作折磨得焦头烂额。直到三年前,当我团队开发的"百考通"系统首次帮助一位语言学教授在48小时内完成核心期刊投稿论文时&…

2026/10/29 18:23:13

AI智能体在工业故障诊断中的架构设计与实践

1. 项目背景与核心价值这个项目的本质是构建一个能够自主分析系统日志、识别异常模式并给出诊断建议的AI智能体。不同于传统规则引擎或统计分析工具,AI故障诊断Agent的核心优势在于其动态学习能力和上下文理解深度。我在工业设备运维领域工作多年,亲眼见…

2026/10/29 18:23:13

DeepSeek R1 安装与部署全攻略:在线入口、API 接入与本地 Ollama 实践

简介:这份PDF文档面向开发者、数据科学家、设计师及内容创作者等希望借助先进自然语言处理能力提升生产力的个人与团队,系统梳理了DeepSeek R1的多种获取路径与实战应用方法。内容覆盖官网与APP、硅基流动、秘塔搜索、Cursor、Groq等在线渠道&#xff0c…

2026/10/29 18:23:13

图解AI应用架构设计:从模型接入到可观测层的六层工程实践

1. 为什么现在聊AI应用架构设计正当时过去一年半,我参与过三个从零到一的AI应用项目,也接手过两个“Demo很惊艳、上线就崩盘”的烂摊子。踩完这些坑之后,我越来越确信一件事:AI应用架构设计和传统后端架构设计,根本就是…

2026/10/29 18:18:12

marketingskills:用户行为响应系统的构建与验证

1. 这不是“学营销”,而是重建你和用户之间的信任通道“marketingskills”这个词,乍一看像英文课作业里的一个生词抄写——字母堆叠、没有空格、不带解释。但如果你在招聘网站上刷过3年以上岗位JD,或者翻过10家中小企业的年度复盘PPT&#xf…

2026/10/29 17:05:52

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/29 17:06:02

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/29 17:06:13

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/29 0:03:42

学生选课系统DFD图绘制指南:边界划分与分层校验实战

简介:文档以顶层与第一层DFD图为核心,完整刻画学生选课系统的逻辑模型,面向软件工程课程设计、系统分析学习及毕设前期建模人群。顶层图界定系统边界,外部实体包括管理员、教师和学生三类角色;第一层图进一步细化用户登…

2026/10/29 0:03:42

JIRA敏捷落地:Scrum角色权限、Sprint与燃尽图避坑指南

简介:围绕“基于JIRA的敏捷开发项目管理”这一主题,面向产品负责人、Scrum Master及研发测试成员,梳理Scrum角色分工、迭代流程与JIRA工具配置的对应关系,适合初、中级团队导入敏捷管理时参考。压缩包共1个docx文件,大…

2026/10/29 0:03:42

SEMI S8人机工程验收实战:从条款拆解到现场实测的完整指南

简介:面向半导体设备设计、制造与运维工程师及安全合规人员,这份资料聚焦SEMI标准体系下的设备安全规范,重点收录SEMI S8-0308《半导体制造设备人体工程学工程安全指南》。内容围绕设备设计原则、错误与事故预防、人体工程学适配三大主线展开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑