资讯动态

Python二手房爬虫与可视化分析:从采集到答辩报告全攻略

发布时间:2026/10/27 1:03:53 来源:尧图企业网站定制
简介一份面向毕业设计及Python爬虫初学者的二手房数据采集与可视化分析项目覆盖从爬虫编写、数据清洗到图表展示与报告输出的完整流程。项目源码已获导师认可并高分通过代码经过真实运行测试功能完整可靠。压缩包共158个文件包括18个Python脚本、18个CSV数据文件、65张图表PNG、15个HTML可视化页面、8个TXT说明文档、8个INI配置文件及答辩PPT等整体约40MB目录结构清晰便于按模块查阅。目前已有1800人学习下载。资源提供ANSI/UTF-8编码及不同记录条数的多版本原始数据同时配有清洗后数据便于对照理解数据预处理过程连同详细使用教程和报告PPT可快速掌握爬虫项目开发思路、可视化展示与答辩汇报要点适合毕业设计选题或实战技能提升参考。1. 二手房爬虫可视化毕业设计先看清这个项目到底在拼什么不少人第一次拿到“基于Python网络爬虫的二手房数据采集及可视化分析”这个题目时以为难点在爬虫怎么写。真跑过一遍你会发现抓数据只是开头清洗、去重、字段解析和图表叙事才是真正耗时的地方。这个项目要解决的是一个数据闭环问题用 Python 网络爬虫把公开二手房页面的挂牌数据采下来落库再交给可视化分析回答“哪个区域贵”“面积和总价是什么关系”这类具体问题。它适合计算机、大数据相关专业的毕业生也适合正在转行数据分析的从业者。你不需要发明算法但必须把采集、存储、清洗、展示四条线全部跑通——这正是这类项目能拿高分毕业设计的原因。2. 采集与分析链路怎么搭最小可跑骨架和四个选型理由2.1 为什么优先选 Requests BeautifulSoup而不是一上来就学 Scrapy做二手房爬虫第一反应往往是去学 Scrapy。Scrapy 确实是工业级框架但毕业设计场景下它有明显的副作用学习曲线陡、项目结构重、调试链路长。你花两周时间搭 Scrapy 的管道、中间件和 Item Loader可能还没跑到数据清洗环节。我一般会先让项目跑起来再谈架构。用 Requests 发 HTTP 请求用 BeautifulSoup 解析 HTML这是 Python 网络爬虫里最直接、最容易单步调试的组合。等代码能爬到 1000 条房源了再去了解 Scrapy 的并发和去重机制也不迟。它的核心原理其实很简单一共四步构造 HTTP 请求带上浏览器身份信息接收服务器返回的 HTML 或 JSON用解析器从响应里抽出目标字段把字段按统一格式写入存储。理解这四步比熟练背诵某个框架的 API 重要得多。爬虫领域有个说法叫“爬虫的原理很简单复杂的永远是页面和反爬”你没有必要在入门阶段就被框架本身绊住。下面是一个典型的毕业设计源码目录结构我建议按这个思路组织你的项目second_hand_house/ ├── crawler/ │ ├── spider.py # 列表页与详情页采集 │ ├── parser.py # 字段解析与清洗 │ └── utils.py # 请求头、延时、重试工具 ├── data/ │ ├── raw/ # 原始 CSV 数据 │ └── processed/ # 清洗后的数据 ├── analysis/ │ ├── clean_data.py # pandas 清洗脚本 │ └── visualize.py # PyECharts 图表生成 └── output/ └── report.html # 可视化报告这个结构的优点是边界清楚爬虫只管采集分析只管画图数据和输出解耦。后面你换目标站点、换图表主题都不需要把脚本推倒重来。2.2 最小爬虫骨架请求头、随机延时与字段解析先写一个最简版本不追求完整功能目标是把一个页面的核心字段抓下来。这里以公开二手房页面的列表项为例子真实项目中你需要按目标站点的 HTML 结构调整选择器。import random import time 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/120.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, Connection: keep-alive, } def fetch_house_list(page_url): 抓取列表页并返回 BeautifulSoup 对象 # timeout 分别设置连接超时与读取超时避免页面卡死拖垮进度 resp requests.get(page_url, headersHEADERS, timeout(5, 10)) resp.raise_for_status() # 状态码非 2xx 时直接抛异常方便排查 resp.encoding resp.apparent_encoding # 防止中文乱码 return BeautifulSoup(resp.text, html.parser) def parse_house_item(item): 从单个房源节点里提取字段字段缺失返回占位符 title item.select_one(.house-title) price item.select_one(.total-price) unit_price item.select_one(.unit-price) area item.select_one(.house-area) return { title: title.get_text(stripTrue) if title else 未知标题, 总价: price.get_text(stripTrue) if price else 暂无标价, 单价: unit_price.get_text(stripTrue) if unit_price else 暂无标价, 面积: area.get_text(stripTrue) if area else 暂无面积, } if __name__ __main__: soup fetch_house_list(https://example.com/ershoufang/pg1/) for item in soup.select(.house-item): print(parse_house_item(item)) # 每次请求之间随机休息 1~2 秒是对目标站点的基本礼貌 time.sleep(random.uniform(1, 2))这段代码的关键不是选择器写法而是三个容易被忽略的参数timeout用元组同时限制连接和读取时长避免某一个慢响应把整个采集流程拖死resp.apparent_encoding用于处理中文字符集比直接用resp.encoding更可靠random.uniform让请求间隔不是固定值避免形成规律的访问特征。运行这段代码时如果拿到空列表先打印resp.status_code和Soup的前 200 个字符判断是页面结构变化还是被反爬拦截而不是盲目改选择器。2.3 数据落库CSV 与 SQLite 两种方案的分工采集到的数据要落盘常见做法是 CSV 和 SQLite 二选一。很多人直接全部塞进 MySQL但对毕业设计来说这是过度设计。下面这张表是实际选择时的判断依据存储方案优点适用场景注意事项CSV可直接用 pandas 读取Excel 也能打开数据量几千条、一次跑完即可必须用utf-8-sig编码否则 Excel 打开中文乱码SQLite支持 SQL 查询、增量去重、字段约束需要多次采集、需要按条件筛选单文件数据库迁移方便无需安装数据库服务MySQL并发写入好数据量几十万条以上需要本地装服务对毕设项目偏重我的习惯是原始数据先写 CSV 留底因为它是审计痕迹后面清洗出问题还能回看原始字段清洗后的数据再进 SQLite因为去重和聚合查询用 SQL 写起来更直观。-- 建表语句房源唯一标识使用 id 字段并用 UNIQUE 约束防止重复采集 CREATE TABLE IF NOT EXISTS house ( id TEXT PRIMARY KEY, title TEXT NOT NULL, total_price REAL NOT NULL, unit_price REAL, area REAL, district TEXT, crawled_at TEXT DEFAULT (datetime(now, localtime)) ); -- 插入时带冲突处理同一房源 ID 再次抓到时直接忽略 INSERT OR IGNORE INTO house (id, title, total_price, unit_price, area, district) VALUES (?, ?, ?, ?, ?, ?);INSERT OR IGNORE是增量采集里最有价值的一句第二次跑爬虫时不用写复杂的查重逻辑重复的房源 ID 天然会被挡在库外。REAL 类型的字段存数值而不是存“800万”“120.5平米”这种带单位的字符串这是为第 4 章的可视化分析铺路。3. 二手房目标站点采集落地接口定位、翻页与反爬节奏3.1 数据藏在哪个位置用浏览器“检查”面板找真实请求很多人拿到一个二手房网站第一件事是去看页面元素然后对着 HTML 写选择器。这没有错但有些页面的 HTML 是后端模板渲染的有些则是前端异步加载的。异步加载的页面里列表数据是通过浏览器里的 XHR 请求拿 JSON 返回的如果你只盯着 HTML 看永远找不到核心数据。实际操作上我一般会打开浏览器的“开发者工具”F12切到“网络”Network面板勾选“Fetch/XHR”过滤项再手动翻一页列表。观察新增的请求逐个查看“响应”标签页如果看到 JSON 里直接有房源标题、总价、单价这些字段那这个站点的数据接口就找到了。这种方式能拿到比 HTML 更干净的结构化数据解析成本低得多。需要注意一个现实问题不是所有站点都方便用这种方式采集。有的接口带签名参数有的 JSON 里字段含义不直观这时候回到 HTML 解析反而更省事。判断标准很简单对比解析 JSON 的代码量和解析 HTML 的代码量谁短用谁。另外动手之前先看一眼目标站点的robots.txt和页面底部的数据声明遵守站点的访问约定这是一个毕业设计项目应该有的基本素养。3.2 翻页采集逻辑从列表页到详情页的URL规律二手房列表几乎都是分页展示的URL 规律通常是pg1、pg2、pg3这样的页码参数。百度热搜里很多人搜“python爬虫”其实是想找到这一步的通用写法翻页逻辑本身不复杂但要注意页码上限和详情页补全两个问题。def crawl_list_pages(base_url, max_page10): 按页码采集列表再进入详情页补全字段。 max_page 不要一次拉满先跑 3 页看数据质量。 all_items [] for page in range(1, max_page 1): page_url base_url.replace({page}, str(page)) soup fetch_house_list(page_url) page_items soup.select(.house-item) if not page_items: print(f第 {page} 页没有解析到房源提前结束) break for item in page_items: info parse_house_item(item) # 列表页可能只给总价和面积详情页才有挂牌时间和户型 detail_url extract_detail_url(item) if detail_url: info.update(parse_detail_page(detail_url)) all_items.append(info) # 每页之间随机休息同时打点日志方便排查进度 print(f第 {page} 页采集完成累计 {len(all_items)} 条) time.sleep(random.uniform(1.5, 3.5)) return all_items这段代码里max_page是第一个该调的参数。我踩过的一个坑是直接把翻页上限设到 100结果爬到第 8 页就被限流。合理的做法是先小步跑通确认数据没有重复、字段没有残缺再逐步提高页码上限。列表页和详情页的关系也需要明确列表页提供的是概览字段详情页才有完整的核心信息。理想情况是每个列表项都进详情页抓一遍但这会让请求量放大 10 到 20 倍所以项目中我通常只在列表页字段不够用时才补详情页。3.3 反爬下的请求节奏随机延时、失败重试与冷却等待反爬是二手房采集绕不开的话题它不是玄学而是服务器在判断“对面是人还是脚本”。判断依据主要是请求频率、请求头完整性和行为规律。一个正常用户不可能每 0.5 秒点一次下一页也不可能连续 20 个小时不休息。def request_with_retry(url, max_retries3): 带失败重试的请求函数遇到限流状态码时递增等待 for attempt in range(max_retries): try: resp requests.get(url, headersHEADERS, timeout(5, 10)) if resp.status_code 429: # 429 表示请求太频繁等待时间随重试次数递增 wait_time 30 * (attempt 1) print(f触发限流等待 {wait_time} 秒后重试) time.sleep(wait_time) continue resp.raise_for_status() return resp except requests.RequestException as e: print(f请求失败{e}重试 {attempt 1}/{max_retries}) time.sleep(5 * (attempt 1)) return None429状态码是服务器在明确告诉你“太快了”。这时候继续硬闯没有意义等待时间要按重试次数递增比如第一次等 30 秒第二次 60 秒。5 秒的连接超时也要保留防止某个请求把整个采集流程卡死在半路。真正的反爬对策是把请求间隔拉长到人的操作节奏列表页之间休息 2 到 4 秒详情页之间休息 1 到 2 秒让一天的采集量控制在几千条以内。这个量级已经足够支撑一套毕业设计的分析完全不需要追求高并发。4. 数据清洗与可视化分析把二手房数据变成答辩结论4.1 清洗规则面积、单价、总价的字符转数值与去重爬下来的数据通常是带单位的字符串比如“800万”“120.5平米”“暂无标价”。这样的数据不能直接画图pandas 读取后第一步就是类型转换。import pandas as pd df pd.read_csv(data/raw/house_raw.csv, encodingutf-8-sig) # 总价去掉“万”字并转成数值暂无标价填 NaN df[total_price] ( df[总价] .str.replace(万, , regexFalse) .astype(float) ) # 面积去掉“平米”单位同时处理空白字符 df[area] ( df[面积] .str.replace(平米, , regexFalse) .str.strip() .astype(float) ) # 按二手房唯一标识去重保留第一次采集到的记录 df_cleaned df.drop_duplicates(subsethouse_id, keepfirst) # 过滤明显异常值总面积小于 5 平米或总价低于 5 万的基本是虚假房源 df_cleaned df_cleaned[ (df_cleaned[area] 5) (df_cleaned[total_price] 5) ]str.replace里的regexFalse是个容易被忽略的参数不加的话“万”字在某些 pandas 版本里会被正则解析引发警告。异常值过滤的条件是根据二手房市场常识总结的真实房源几乎不可能出现 2 平米的住宅这类数据往往是页面维护时的错误占位。清洗后的数据建议单独输出一个processed文件源码包里保留清洗前和清洗后两份数据这样答辩时你可以现场演示“清洗前什么样、清洗后什么样”这是一个很容易打动评委的动作。4.2 可视化图表的选型用 PyECharts 生成可交互 HTML可视化分析部分我强烈建议用 PyECharts 而不是 Matplotlib。两者都能画图但 PyECharts 生成的是 HTML 文件图表支持鼠标悬停查看数值、缩放、平移这些交互能力在答辩现场演示时效果比静态图好很多。你只需要把报告文件拷到答辩电脑上用浏览器打开就可以演示不依赖 Python 环境。from pyecharts.charts import Bar, Scatter from pyecharts import options as opts # 区域均价柱状图横向对比一眼看出哪个区最贵 bar ( Bar() .add_xaxis(district_list) # 区域名称列表 .add_yaxis(均价(元/平米), avg_price_list) .set_global_opts( title_optsopts.TitleOpts(title各区域二手房均价对比), yaxis_optsopts.AxisOpts(name元/平米), ) ) bar.render(output/avg_price_by_district.html) # 面积与总价散点图用点的分布看两者相关性 scatter ( Scatter() .add_xaxis(area_list) .add_yaxis(总价(万), total_price_list) .set_global_opts( title_optsopts.TitleOpts(title面积与总价关系), xaxis_optsopts.AxisOpts(name面积(平米)), yaxis_optsopts.AxisOpts(name总价(万)), ) ) scatter.render(output/scatter_area_price.html)散点图里如果大部分点集中在一个带状区域说明面积是总价的主要驱动因素如果中心城区明显偏离这条带说明地段溢价在起作用。这两个图放在报告 PPT 里能直接支撑“区域差异”和“价格驱动因素”两个分析结论。图表主题、颜色可以调成与你的报告封面一致这个细节会让人感觉整个项目是成套的而不是拼凑的。4.3 图表怎么组织成报告PPT一图一结论的叙事顺序可视化分析做完之后很多人的报告 PPT 变成图表的堆砌。评委看 PPT 时最想知道的是“你从数据里看出了什么”而不是“你用了什么库”。所以每张图表旁边一定要配一句结论且结论只能从图上直接读出来。图表从图中读出的一句话结论可作为答辩回答区域均价柱状图中心城区均价是近郊的 1.6 倍地段是二手房定价的第一因素面积-总价散点图60-90 平米房源集中在中低总价段刚需面积段供应充足总价分布直方图300-500 万是主流成交区间该城市二手房以改善型需求为主涨幅对比折线图近三个月近郊均价涨幅高于中心城区城市扩张带动近郊市场升温这个表里的规律都是典型结论你拿自己的数据跑完后大概率会得到类似但数值不同的版本。组织的顺序建议是先放数据规模的说明再放区域对比然后放面积与总价关系最后放一个趋势性的结论。每一步都从图表展开而不是先写一大段文字再贴图。这也是报告 PPT 里“分析”和“展示”的根本区别图表是证据结论是观点两者要挨在一起。5. 采集与可视化路上的常见坑现象、原因、修复顺序5.1 翻车场景请求头缺了 Referer拿到的是空壳页面现象爬虫返回 200 状态码但解析出来title字段全是空的打印 HTML 发现只有框架标签核心内容不在里面。原因有些站点的服务器会校验请求头里的Referer字段它表示“你是从哪个页面跳转过来的”。脚本直接请求详情页时没有携带这个字段服务器选择性返回空壳模板。解决在请求头里补上Referer: https://example.com/ershoufang/让请求看起来像从列表页跳转进入的。这也解释了为什么请求头要统一封装在一个HEADERS字典里而不是散落在各处因为反爬头字段往往是一整套一起校验的。5.2 翻车场景解析“暂无标价”导致类型转换崩溃现象清洗时astype(float)报ValueError: could not convert string to float程序中断。原因房源页面上有大量“暂无标价”“价格待定”等占位文本。爬虫采集时把字符串原样存了下来清洗阶段直接转数值必然崩溃。解决在清理阶段先把暂无标价、待定、统一替换成None并给总价单价字段指定为可空类型。这里有一个实际经验不要用 0 填充缺失值因为 0 在后面的分析里会被当成真实成交价扭曲平均值。缺失就让它缺失pandas 绘图时会自动跳过空值。5.3 翻车场景翻页爬到第 5 页就收到 429 状态码现象前 4 页数据正常第 5 页开始连续 429之后所有请求都返回 503。程序没有崩溃但后面全是无效数据。原因固定 1 秒的定时请求形成了机器行为特征服务器对单 IP 的请求频率阈值被触发。第六页之后触发了更严格的风控。解决把固定延时改成随机延时间隔范围调大比如 2 到 5 秒之间随机取重试逻辑里遇到 429 至少等待 60 秒。最佳实践是每次启动爬虫前先确认当前网络出口的请求频率已经冷却而不是直接把程序挂在那里跑。5.4 翻车场景CSV 用 Excel 打开字段中文全部乱码现象采集和清洗完全正常print到控制台也正常但用 Excel 打开生成的 CSV 文件后小区名、区域名全成了“锟斤拷”之类的乱码。原因CSV 默认用utf-8编码写入而 Windows 版 Excel 读取时默认按gbk解码两者不匹配。这是数据采集项目里最常见的编码坑之一。解决写入 CSV 时统一使用encodingutf-8-sig。多出来的 BOM 头是 Excel 识别 UTF-8 的标记不会影响 pandas 读取。代码演示如下df.to_csv(data/processed/house_cleaned.csv, indexFalse, encodingutf-8-sig)5.5 排查思路打印状态码和异常堆栈别让程序变成黑匣子现象爬虫跑着跑着停了日志只有一句Process finished with exit code 0既没有输出错误也没有打印数据进度。原因代码里把所有异常都吞进了except块也没有在任何循环节点打印当前页码和数据条数。一旦出错你并不知道它停在哪一步。解决每个请求函数至少返回状态码每个翻页循环打印“当前在第几页、累计多少条”异常捕获时把traceback.format_exc()打出来。这里有一条血泪经验爬虫项目调试成本最高的部分不是写爬虫而是定位“它到底停在哪”。一个可读的日志加上稳健的异常处理比任何花哨的框架都管用。如果页面结构变了直接去对比第一次成功时的响应文本和现在的差异通常是某个 HTML 层级被调整了。6. 让源码项目从“能跑到能答辩”增量采集和自检清单增量采集是这个项目收尾时最值得加的一层能力。简单说就是爬虫第二次运行时不要重复抓取已经入库的房源做法是启动时先把现有house_id集合加载进内存遇到重复 ID 直接跳过。这样每天定时跑一次数据会持续增长你的报告 PPT 里也能多一张“数据随时间累积”的过程说明图。-- 增量采集前取已有 ID 集合交给爬虫脚本去重 SELECT id FROM house;自检清单我建议按这个顺序执行先检查采集脚本能否独立跑通 3 页再检查清洗后 CSV 的行数和字段非空率最后检查两个 HTML 图表能否在浏览器里正常打开。演示顺序上先跑清洗脚本再跑可视化脚本最后打开report.html评委看到的是一个完整的分析流程而不是一张静态截图。整个项目里我摔得最多的不是爬虫本身而是“本地能跑换个电脑跑不起来”的环境问题。后来我习惯了在源码包里附带requirements.txt并在教程里写清楚 Python 安装和依赖导入的步骤这也是这类毕业设计源码包里“使用教程”最该承担的角色。希望这一套从采集到可视化的完整链路能帮你把项目真正做成可以复现、可以演示、经得起追问的作品。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑