Python二手房数据爬取与可视化:从采集到大屏展示的完整实践

发布时间:2026/10/29 18:25:27

Python二手房数据爬取与可视化:从采集到大屏展示的完整实践 简介这套基于Python的二手房数据爬取与可视化分析毕业设计资源包含完整可运行的源码与PPT文档报告适合计算机相关专业学生用于毕业设计、课程设计参考也可作为数据采集与数据分析方向实战练习的系统案例。项目完整覆盖了二手房真实交易数据的爬取采集、清洗整理、持久化存储与可视化展示全流程源码经本地编译调试确保可正常运行曾获导师认可的高分设计项目。资源共156个文件、压缩包约40MB主要包含18个Python程序文件、多份CSV数据表、可视化图表PNG图片、HTML页面与JS交互脚本以及PPT演示文档目录结构清晰便于检索对照。目前已有142人浏览学习。借助该项目可以掌握网页数据抓取、Pandas数据清洗、可视化图表生成等关键技术实现CSV文件同时保留原始数据与清洗后版本便于验证分析过程适合从零复现整个项目并在此基础上扩展功能。1. 基于 Python 的二手房数据爬取与可视化分析这套毕业设计到底能落地成什么样每年毕业季都有大量学生选这个题目但真正能把「爬取」和「可视化」串成一条完整链路的并不多。很多人卡在同一个地方数据抓下来了Excel 里躺着几千行却不知道下一步怎么变成论文里的图表和答辩现场的大屏。本文要讲的就是这条完整链路——用 Python 爬取链家二手房列表页清洗字段后存入 SQLite再用 Flask 提供接口、ECharts 渲染可视化大屏最后把全套代码和 PPT 文档报告的写作框架对应起来。目标读者是正在做毕业设计的学生以及想快速搭建一个数据采集展示 demo 的从业者。如果你只想跑通一个最小示例第 2 章的代码可以直接抄如果你要做增量更新和防封策略第 6 章给出了方案。这套做法不依赖任何商业 API成本几乎为零唯一的硬性要求是电脑能装 Python 3.8 以上环境。2. 目标分析与环境准备先想清楚爬什么、存什么、展示什么2.1 为什么选链家二手房而不是贝壳或安居客毕业论文选题最忌讳的是把大量时间花在对抗反爬机制上。贝壳的数据结构和链家同源但接口签名更复杂安居客的页面是服务端渲染加动态加载解析成本明显偏高。链家的二手房列表页是静态 HTML 加少量 JSON 嵌入Requests 加 BeautifulSoup 就能稳定解析这个特性决定了它是最适合教学和毕业设计的平台。另一个优势是链家的字段非常规整——小区名、户型、面积、朝向、装修、楼层、总价、单价、关注人数都在同一个列表中几乎没有嵌套结构解析代码写起来非常直观。确定平台之后数据的存储方案也要提前定。MySQL 在毕业设计中容易遇到本机没装服务、编码配置出错的问题SQLite 是 Python 标准库自带的零配置文件适合这种单机爬虫项目。如果你的导师要求必须用 MySQL代码里只需要把连接方式换一下SQL 语句的兼容性没有问题逻辑不用动。2.2 开发环境与依赖清单打开终端先确认 Python 版本要大于 3.8否则很多类型注解的写法会用不了。python --version pip install requests beautifulsoup4 pandas flask pyecharts这套依赖组合的逻辑是requests 负责网络请求beautifulsoup4 负责解析 HTMLpandas 做数据清洗和聚合统计flask 提供可视化大屏的后端接口pyecharts 用来快速生成带交互的图表。如果你打算直接用 ECharts 的 JavaScript 文件而不经过 pyecharts 封装flask 依然需要保留前后端分离的展示方式在答辩演示时切换数据更灵活。pip 安装如果出现超时用清华镜像源重试这是国内网络环境下的常规操作。pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests beautifulsoup4 pandas flask pyecharts代码写完调试阶段建议用 VS Code 加 Python 插件断点打在解析函数里逐步看结构比反复 print 省时间。2.3 数据字段设计与存储结构爬虫写代码之前先建表这是很多新手最容易跳过的步骤。链家二手房列表页里能拿到的字段很多但不是每个都值得入库。我建议保留十个核心字段小区名、区域、板块、户型、面积、朝向、装修、楼层、总价、单价、关注人数和挂牌时间。建筑面积和套内面积经常同时出现入库时只保留建筑面积总价单位是万元单价单位是元每平方米后续做可视化计算时统一换算能少走很多弯路。建表语句如下CREATE TABLE houses ( id INTEGER PRIMARY KEY AUTOINCREMENT, community TEXT, district TEXT, area TEXT, layout TEXT, size REAL, orientation TEXT, decoration TEXT, floor TEXT, total_price REAL, unit_price REAL, follow_count INTEGER, listed_date TEXT, crawl_time TEXT );这里把 total_price 和 unit_price 设计成 REAL 而不是 TEXT是为了后续聚合计算时省去类型转换SQL 里直接能写 AVG 和 SUM。3. 爬虫模块从请求头伪装到页面解析的完整代码3.1 请求封装与翻页策略链家的二手房列表最多只展示 100 页每页 30 条也就是说单城市最多能爬到 3000 条左右。这个数据量对毕业设计足够对论文里的回归分析也够用。翻页逻辑非常简单URL 里的 pg 参数就是页码。import requests from bs4 import BeautifulSoup import time import random def get_headers(): return { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.8,en;q0.6, } def fetch_page(page): url https://bj.lianjia.com/ershoufang/pg{}/.format(page) resp requests.get(url, headersget_headers(), timeout10) resp.raise_for_status() resp.encoding utf-8 return resp.text这里固定用北京城市是因为 URL 结构最典型。如果要爬上海把 bj 改成 sh 就行页面解析规则完全一致。headers 里的 User-Agent 建议用真实浏览器的完整字符串不要用 requests 默认的 python-requests 标识那是反爬系统最容易识别的东西。3.2 用 BeautifulSoup 提取列表数据页面拿到之后要做两件事找到每套房子的外层容器然后在容器内部分别提取字段。链家二手房列表项的 CSS 选择器是.sellListContent li .info这是经过多个版本更新后相对稳定的规则。def parse_html(html): soup BeautifulSoup(html, lxml) items [] for li in soup.select(.sellListContent li .info): title li.select_one(.title a) if not title: continue community li.select_one(.communityName) position li.select_one(.positionInfo) house_info li.select_one(.houseInfo) total_price li.select_one(.totalPrice) unit_price li.select_one(.unitPrice) follow li.select_one(.followInfo) items.append({ title: title.get_text(stripTrue) if title else , community: community.get_text(stripTrue).replace(小区, ) if community else , position: position.get_text(stripTrue).replace(/, -) if position else , house_info: house_info.get_text(stripTrue) if house_info else , total_price: total_price.get_text(stripTrue).replace(万, ) if total_price else , unit_price: unit_price.get_text(stripTrue).replace(元/平, ) if unit_price else , follow: follow.get_text(stripTrue).split(人)[0] if follow else , }) return items这段代码的逻辑是按层级逐层提取每取一个字段都做了空值判断。.houseInfo返回的原始字符串是「2室1厅 | 88.45平米 | 南 北 | 精装 | 低楼层(共6层) | 2005年建」后面清洗阶段再拆分成独立字段。3.3 完整爬取与异常处理翻页循环里一定要控制节奏每页至少间隔三到五秒否则触发反爬的概率会大幅上升。def crawl(): all_data [] for page in range(1, 101): try: html fetch_page(page) items parse_html(html) if not items: break all_data.extend(items) print(f第{page}页获取{len(items)}条累计{len(all_data)}条) time.sleep(random.uniform(3, 5)) except requests.exceptions.Timeout: print(f第{page}页超时暂停10秒重试) time.sleep(10) continue except Exception as e: print(f第{page}页失败: {e}) continue return all_data当解析结果为空时直接 break这一行是保命逻辑。如果你被封了 IP链家会返回一个验证页面此时解析出的 items 是空的直接跳出循环比反复重试节省时间。随机延时用 random.uniform 而不是固定 sleep能避免明显的机器访问特征。3.4 把原始数据拆成结构化字段爬下来的数据还在一堆未分割的字符串里第 2 步建好的表还不能直接插入需要先做一层清洗。这块用 pandas 处理比手写正则更直观。import pandas as pd import re def clean_data(items): df pd.DataFrame(items) df[layout] df[house_info].str.extract(r(\d室\d厅)) df[size] df[house_info].str.extract(r([\d.])平米).astype(float) df[orientation] df[house_info].str.extract(r平米\|\s*([^|]?)\s*\|) df[decoration] df[house_info].str.extract(r\|\s*([^|]?)\s*\|\s*[高低中]) df[total_price] pd.to_numeric(df[total_price], errorscoerce) df[unit_price] pd.to_numeric(df[unit_price], errorscoerce) df[follow] pd.to_numeric(df[follow], errorscoerce) return df正则里的分组提取顺序必须严格对上链家的分隔符格式先按 | 切分再取对应位置会更稳妥。极端情况下某些房源缺少装修信息正则匹配会返回 NaN入库之前必须用 dropna 处理。4. 数据清洗与坐标联动让可视化图表不说错话4.1 处理缺失值和异常值爬虫采集的数据直接入库会带脏数据比如总价为 0、面积小于 5 平米、单价超过每平米 20 万这些异常值会让统计图表的平均值严重失真。清洗策略分两步删除必要字段为空的记录然后对数值字段做上下界截断。def clean_for_visualization(df): df df.dropna(subset[layout, size, total_price, unit_price]) df df[(df[size] 10) (df[size] 300)] df df[(df[total_price] 10) (df[total_price] 5000)] df df[(df[unit_price] 5000) (df[unit_price] 200000)] return df上下界的取值来自北京二手房市场的基本认知面积低于 10 平米的住宅几乎只有地下室或者极端小户型总价低于 10 万的房子在链家体系里不是正常住宅。如果你爬取的是三四线城市lower bound 要自己根据当地情况调整用统一阈值反而会误删真实数据。4.2 经纬度联查让散点图落到地图上可视化分析如果只做柱状图和折线图是不需要经纬度的。但要做基于地图的散点分布图就必须把行政区划名称转成经纬度。常见做法是利用高德的 API百度地图的坐标系在高德地图上会有偏移选择哪个平台取决于你最终展示用的底图。我自己一般用高德的 API 接口来拿经纬度import requests AMAP_KEY 你的高德开发者Key def get_coordinate(address): url https://restapi.amap.com/v3/geocode/geo params {address: address, key: AMAP_KEY} resp requests.get(url, paramsparams, timeout5) data resp.json() if data[geocodes]: return data[geocodes][0][location] return None高德地理编码接口每天有免费额度个人开发完全够用。如果你嫌申请 Key 麻烦也有一个笨办法把链家数据里「区域」这一列去重得到大概十几个区名手动维护一张区名到经纬度的映射表。这样虽然精度只到行政区级别但画出来的散点图在地图上依然有区分度而且完全离线可用。4.3 去重规则与增量更新爬虫重复运行时会产生重复数据最简单的去重规则是组合字段去重——小区名、户型、面积、总价这四个字段完全一致就视为同一条房源。def deduplicate(df): return df.drop_duplicates(subset[community, layout, size, total_price])增量更新的逻辑深刻影响着后续数据的使用。如果你是每天定时爬一次应该用「挂牌时间 小区名 户型」做联合判断把新出现的组合插入已存在的跳过。毕业设计如果只做一次性分析那直接全量重爬更省事不需要考虑增量。5. 可视化大屏设计Flask 接口与 ECharts 图表的组合方案5.1 大屏需要哪几张图从论文答辩倒推图表选型做可视化之前先把你要回答的问题列出来北京哪个区的二手房均价最高面积段和总价的关系是什么户型分布是否遵循二八定律这些问题对应图表类型分别是地图散点图、价格区间柱状图、户型占比饼图、面积总价散点图。再加一个表格展示降价最快的小区一套可视化大屏的雏形就有了。不需要一上来就追求大屏特效答辩评委看重的是图表和结论之间的逻辑关系。5.2 Flask 后端把 SQLite 数据变成 JSON 接口Flask 在这里的角色是数据服务层前端图表通过 Ajax 调用接口拿数据。from flask import Flask, jsonify import sqlite3 app Flask(__name__) def query_db(sql): conn sqlite3.connect(houses.db) cur conn.cursor() cur.execute(sql) rows cur.fetchall() conn.close() return rows app.route(/api/district_price) def district_price(): rows query_db(SELECT district, AVG(unit_price) FROM houses GROUP BY district ORDER BY AVG(unit_price) DESC) data [{district: r[0], avg_price: round(r[1], 2)} for r in rows] return jsonify(data) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)关键点在于每次请求都新建数据库连接避免多线程下的连接冲突。SQL 里的 AVG 函数直接做了聚合前端拿到就已经是处理好的数据减少图表侧的重复计算。如果数据量超过一万条可以在 district 字段上建索引查询速度会快很多。5.3 前端图表实现ECharts 的核心配置参数大屏页面用 HTML 加 ECharts 直接写不用 pyecharts 生成原因是答辩现场调整参数更快。核心配置里xAxis 和 yAxis 的 type 设置是最容易出错的——category 和 value 的区别决定了折线图能不能画出来。!DOCTYPE html html head meta charsetutf-8 script srchttps://cdn.jsdelivr.net/npm/echarts5/dist/echarts.min.js/script /head body div idchart stylewidth: 900px; height: 500px;/div script fetch(/api/district_price) .then(response response.json()) .then(data { var chart echarts.init(document.getElementById(chart)); chart.setOption({ title: { text: 北京各区二手房均价元/平 }, tooltip: { trigger: axis }, xAxis: { type: category, data: data.map(d d.district) }, yAxis: { type: value, name: 均价 }, series: [{ type: bar, data: data.map(d d.avg_price), itemStyle: { color: #2f89cf } }] }); }); /script /body /htmlECharts 的 CDN 链接建议下载到本地因为答辩现场的教室网络可能无法访问外部 CDN。另外 xAxis 上如果区域名超过七个字记得加 axisLabel 的 rotate 参数转 45 度否则标签会互相遮挡。地图散点图用 type: scatter 加 coordinateSystem: geo需要单独加载 geo 组件的 JSON 数据这在答辩前要提前确认最终效果。6. 常见问题排查与避坑指南6.1 爬到一半突然全是空白页现象前面几十页数据正常突然某页返回的 HTML 里找不到任何列表项。原因大概率是 IP 被临时限制了链家返回了一个安全验证页面而不是正常的房源列表。解决办法是停掉脚本等 15 到 30 分钟再继续或者切换网络环境。不要在同一 IP 下用多线程并发爬否则封禁时间会更长。如果连续多次触发就要检查请求头里的 User-Agent 是否被识别以及 Cookie 是否缺失。我的做法是去浏览器复制完整的 Cookie 字符串放进 headers这样做完能明显降低触发概率。6.2 解析出来的面积是空值现象size 列大量 NaN但页面上显示有面积数据。原因通常有两个一是正则写得太严格链家某些房源的信息格式多了个空格或者换行符二是 fallback 的逻辑没写某些特殊户型会在面积后面带「㎡」而不是「平米」。解决办法是先把原始字符串打印出来对比差异再调整正则。size字段里可能混入「暂无数据」正则匹配会直接失败提取之前先做一步过滤。这一步放在 pandas 的 str.extract 之前用正则去掉所有非数字和小数点字符。6.3 Flask 接口返回中文乱码现象Ajax 请求返回的数据里中文变成\u5c0f\u533a这种转义字符。直接看浏览器控制台会觉得是乱码实际上 JSON 里这就是正常的中文编码形式前端 JSON.parse 后会自动还原。真正需要检查的是 Flask 的 response 是否设置了正确的 Content-Type加上下面这行配置app.config[JSON_AS_ASCII] False加完配置后重新启动 Flask再刷新页面看接口返回值中文就会以明文显示。6.4 图表数据对不上 SQL 查询结果排查时先打开浏览器的 Network 面板直接看接口返回的 JSON 和 SQLite 里查出来的是否一致。多数情况是 SQL 里的 GROUP BY 和筛选条件写得不对比如按均价排序却忘了排除总价为 0 的记录。把清洗后的 DataFrame 先导出成 Excel 检查一遍再决定问题出在数据层还是展示层能少走不少弯路。7. 进阶技巧从单机爬虫到增量更新与自动化报告7.1 用 Scrapy 改造成增量爬虫Requests 版本适合演示和教学但如果你想把爬取能力部署成每天自动运行的任务Scrapy 的调度器和去重机制更适合生产场景。Scrapy 的 Item Pipeline 里可以做去重判断Request 的 dont_filter 参数控制是否跳过重复 URL。class DuplicatesPipeline: def __init__(self): self.seen set() def process_item(self, item, spider): key (item[community], item[layout], item[total_price]) if key in self.seen: raise DropItem(Duplicate) self.seen.add(key) return item这个 Pipeline 的逻辑和上面 pandas 去重的思路一致只是换成了爬虫框架内置的机制。不加此行会让 Scrapy 默认按 URL 去重但链家翻页 URL 每次都不重复去重就失效了。7.2 定时任务与数据自动更新Windows 下用计划任务Linux 下用 crontab每天凌晨两点跑一次更新脚本。0 2 * * * cd /home/user/house_spider /usr/bin/python3 crawl.py log.txt 21脚本内部做一个「只追加新数据」的逻辑先读数据库里已有的挂牌时间最大值只爬取晚于这个时间的房源。用这种方式积累三个月数据以后你能做的分析维度就完全不一样了——可以用「降价次数」和「挂牌天数」做字段关联分析观察到手房源的周期规律这比单纯拿一天的截面数据写论文深入得多。7.3 结语我的习惯是在每个图表旁边都留一个「数据更新时间」的文本标签这样答辩被问到「数据是不是实时」时能直接指给对方看。另一个教训是爬虫代码里一定要写 try-except 包裹的日志输出爬完第一天可能还正常第二天早上起来发现全失败没日志就只能从头猜。写这套系统时先把日志框架搭好后面所有调试时间都省回来了。希望这篇笔记能帮你把毕业设计的时间主要花在数据分析和图表设计上真正想清楚怎么讲出一个完整的故事。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/28 15:52:04

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

简介:这是一份基于Python的二手房数据爬取与可视化分析毕业设计源码及PPT文档报告,经导师指导并获评98分,适合计算机相关专业学生用于毕业设计、课程设计或数据分析项目实践。项目围绕二手房信息采集与分析展开,包含爬虫抓取、数据…

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/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
免费获取方案
☎咨询二维码 ☎ ↑