1. 项目概述与核心需求拆解
说实话,我第一次看到“python二手房大数据可视化平台”这个选题,脑子里蹦出来的关键词就四个:爬虫、Django、可视化、数据分析。这几乎是毕业设计里最经典的“全家桶”组合。名字听着唬人,拆开看其实就三板斧:用Requests把二手房房源数据抓下来,用Django做后端服务和数据管理,再用图表把分析结果展示到可视化大屏上。对正在选题或者准备复现这套源码的同学来说,真正的价值并不在于“抓了多少数据”,而在于把数据采集、清洗入库、接口开发、图表展示这条完整链路走通。
这个项目适合谁来参考?一类是计算机专业做毕设、需要“有爬虫、有框架、有可视化、有数据分析”多个亮点的同学;另一类是刚学完Python基础,想找个完整项目练手,把Requests、Django ORM、ECharts串起来的开发者。如果你只是想短期跑通一个能演示的项目,这个方案足够;如果你想把项目做成一个真正可用的“数据采集分析平台”,还需要在数据粒度和分析维度上做更多设计。下面我会结合实际操作,把整个项目的设计思路、核心细节和踩坑过程完整拆开讲。
1.1 核心需求拆解
我把标题里隐藏的需求拆成四块:数据采集、数据存储、数据接口、数据展示。数据采集要求能爬取真实房源信息,包括小区名称、区域、户型、面积、总价、单价、朝向、楼层、建筑年代、关注人数等。存储层要求把清洗后的数据结构化,方便后续按区域、户型、价格区间做聚合统计。展示层要求有可视化大屏效果,至少包含地图、柱状图、饼图、折线图、排行列表等常用形态。
还有一个容易被忽略的需求是“可演示性和可复现性”。毕设不是生产环境,老师更关心你能否讲清楚每一步为什么这么做。所以项目里的每个模块都要有明确输出,爬虫要有数据文件,后端要有接口返回的JSON,前端要有图表,这样答辩时才能一环扣一环地讲。很多源码拿到手跑不通,问题就出在模块之间互相依赖,却没有留下中间产物。
1.2 技术选型:为什么是Django + Requests + ECharts
先解释Requests的选择。Requests是Python里最直观的HTTP请求库,代码量少,学习曲线低,适合作为毕业设计的数据采集层。Scrapy虽然功能更强,但分布式、中间件、异步这些概念会让新手在调试上花掉大量时间,反而偏离了“快速出成果”的目标。对于一次性爬取几千条房源数据的场景,Requests加循环加延时已经足够,而且代码容易读懂,答辩时也方便逐行讲清楚。
Django选择的原因在于“全家桶”式开发体验:自带ORM、Admin后台、URL路由、模板引擎和认证系统。你不需要拼装Flask加SQLAlchemy加迁移工具,一个框架就能覆盖后端的大部分需求。尤其是Django Admin,数据采集完直接进后台人工核对,对演示和调试都很方便。另外,Django的ORM在做聚合统计时非常顺手,一行代码就能完成分组、计数、求均值,这些能力正好匹配可视化平台的后端接口需求。
可视化部分我推荐直接用ECharts,而不是用Python的Matplotlib生成静态图片。因为ECharts输出的是网页动态图表,鼠标悬浮、缩放、联动都有交互效果,放到大屏上观感完全不一样。它通过JavaScript读取后端接口数据,把Python数据分析结果和前端图表解耦,代码结构也更清晰。前端页面用普通的HTML、CSS、JavaScript就能搞定,不需要额外引入Vue或React,降低整个项目的上手门槛。
1.3 功能模块全景图
整个系统可以分成四个模块:
| 模块 | 主要职责 | 关键技术点 |
|---|---|---|
| 爬虫模块 | 采集房源原始数据 | Requests、BeautifulSoup/lxml、正则、CSV |
| 数据管理模块 | 清洗、去重、入库 | Django ORM、pandas、自定义清洗函数 |
| 后端接口模块 | 提供聚合统计接口 | Django REST Framework或JsonResponse、聚合查询 |
| 前端可视化模块 | 展示图表大屏 | ECharts、HTML/CSS/JavaScript、Ajax |
实际开发顺序建议是:爬虫先跑通,数据落成CSV,再建Django项目和数据表,导入数据,然后单独调试接口,最后做前端大屏。这种方式能降低联调痛苦,避免出现后端接口改字段、前端图表全部失效的情况。我见过不少同学先做前端大屏再补数据,最后发现字段对不上,只能重写图表,浪费时间。
2. 数据采集层:Requests爬虫的设计与落地
2.1 目标字段与页面结构
在动手写爬虫前,先明确需要哪些字段。我一般会列一个字段清单,包括房源编号、标题、小区、区域、板块、总价、单价、面积、户型(室厅卫)、朝向、楼层、建筑年代、关注人数、挂牌时间、爬取时间。这个清单直接决定后续数据表字段,也决定ECharts图表能画什么内容,一步到位非常关键。
拿某知名房源平台的二手房列表页来说,列表页里可以看到大部分字段,但要注意详情页里才有建筑年代、产权年限这类附加信息。毕设项目不需要追求完美,列表页能拿到的基础字段已经可以支撑绝大多数可视化分析,没必要一上来就爬详情页。如果你实在需要更多字段,再单独写一个详情页采集函数,用房源编号作为关联键,爬取时把详情页数据更新到已有记录上。
爬虫第一步是分析页面URL规律。列表页通常是地区编码加页码的组合,比如某个城市的某个区域,URL带一个region参数。建议先打开开发者工具的网络面板,刷新列表页,看XHR请求和静态HTML的关系。如果页面数据直接在初始HTML里,用Requests请求后解析HTML即可;如果页面是异步加载的,还要额外找数据接口,用JSON解析方式提取字段。拿到稳定的URL规律后,再写循环请求,否则爬虫很容易中途断掉。
2.2 请求头设置与访问频率控制
很多人爬虫失败,不是因为代码逻辑问题,而是请求头太“裸”。目标站点会检查User-Agent、Referer、Cookie等字段。建议在爬虫里定义一个请求头字典,尽量伪装成浏览器。我在实际项目里会用当前浏览器复制User-Agent,并且保持Referer为网站首页地址,同时补上Accept-Language。这样能减少很多不必要的拦截,尤其是列表页的前几页,往往只靠UA就能通过。
请求频率控制是爬虫模块最容易出问题的环节。每请求一页,至少睡眠1到3秒,随机延迟可以用time.sleep(random.uniform(1, 3))。千万不要连续快速请求,也不要完全固定间隔,固定间隔反而容易被识别为机器行为。我的习惯是在爬虫类里初始化一个记录请求次数和最近请求时间的变量,每两次请求之间动态计算间隔,既能控制总体时间,又减少被封风险。
另外要控制单日数据量。毕设项目爬5000到10000条足够完成分析,不需要追求“全量数据”。数据量太大不仅爬取时间成倍增加,数据库查询和前端图表渲染也会变慢,对答辩毫无帮助。我建议爬虫脚本设置一个最大页数参数,先爬5到10页做测试,确认字段正确后再放开到全量范围,这样排错成本最低。
2.3 429限流与请求重试机制
这里要重点说一个几乎所有爬虫新手都会遇到的报错:exceeded retry limit, last status: 429 too many requests。429的含义是“请求过多”,服务端已经主动拒绝响应。很多人在Requests请求里直接设置一个重试次数,换成重试后依然失败,原因在于重试之间没有等待时间,或者没有改变请求头的Cookie状态。单纯的retry并不会解决限流,只是把失败的请求又快速重放了一遍。
我推荐的方案是三层处理:第一层是基础请求函数,设置timeout=10,并捕获requests.RequestException;第二层是重试逻辑,当响应状态码为429或503时,等待一段指数退避的时间,比如第一次等5秒,第二次等15秒,第三次等30秒,最多重试3次;第三层是降低并发和切换入口,如果是列表页被限流,可以换个区域入口,或者干脆暂停一段时间再继续。指数退避配合随机抖动,比固定等待更接近人类访问行为,实测下来被限流的概率会低很多。
写代码时不要把重试逻辑散落在各个模块里,建议封装一个fetch_page函数,统一负责请求、异常捕获、状态码判断和返回值处理。下面是一个简化的例子:
import time import random import requests def fetch_page(url, headers, retry=3): for i in range(retry): try: resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: return resp.text if resp.status_code in (429, 503): wait_time = 5 * (2 ** i) + random.uniform(0, 2) print(f"触发限流,等待 {wait_time:.1f} 秒") time.sleep(wait_time) continue except requests.RequestException as e: print(f"请求异常:{e}") time.sleep(3) return None这个封装能帮你把限流、超时、网络异常统一收口。实际使用中,如果连续多次触发429,我会建议直接停掉爬虫,第二天再继续,而不是硬撑。很多反爬策略会临时封禁一段时间,你越重试,封禁越久。
2.4 数据清洗与入库细节
爬下来的HTML只是原始材料,需要从HTML里提取结构化字段。我用BeautifulSoup解析HTML,用CSS选择器或XPath定位房源列表项,再逐条提取。提取时要注意字段缺失和异常值:面积字段可能带“平米”,总价可能有“万”字,朝向可能是空字符串。这些都要做统一清洗。
清洗规则我写在独立脚本里,方便复用。比如把“88平米”变为88.0,把“总价320万”变为320.0,把户型“3室2厅1卫”拆成bedroom、hall、bathroom三个字段。单价如果页面没直接给,可以用总价乘以10000再除以面积大致计算,并保留两位小数。这一步看似琐碎,却决定了后续数据分析和图表是否可信。如果爬虫字段里混着“暂无数据”“业主急售”这类文本,聚合查询时很容易报错或者画出异常图表。
清洗完后先保存成CSV,再利用Django的loaddata或自写脚本导入数据库。我建议不要直接把未清洗的数据倒进数据库,否则后面写聚合查询时,字符串和数值混在一起,踩坑踩到怀疑人生。导入脚本里加上日志输出,每导入100条打印一次进度,出了问题也能快速定位到具体行。
3. 后端服务:Django框架下的接口与数据模型
3.1 Django项目结构设计
Django项目虽然可以一个App打天下,但毕设建议还是建两个App:一个叫houses,负责房源数据模型和爬虫数据导入;一个叫analysis,负责聚合查询和可视化接口。一个App会让urls.py和models.py越来越乱,两个App各司其职,代码结构更清晰,答辩时也能顺势讲出“模块化设计”的概念。
目录结构可以这样组织:
project/ ├── manage.py ├── config/ # 项目配置 │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── houses/ # 房源模型与数据管理 │ ├── models.py │ ├── admin.py │ ├── views.py │ └── urls.py ├── analysis/ # 数据分析接口 │ ├── views.py │ ├── urls.py │ └── utils.py ├── static/ # 前端静态资源 │ ├── css/ │ ├── js/ │ └── data/ ├── templates/ │ └── dashboard.html └── scripts/ ├── crawl.py └── import_data.py这样分层的好处是:爬虫脚本独立于Web服务,前端模板与静态资源独立,数据模型和业务接口分层。后面无论是扩展爬虫还是换前端框架,只需要动对应目录。两个App的URL可以通过Django的include机制挂载到主路由下,使用/api/houses/和/api/analysis/作为前缀,一眼就能看出接口归属。
3.2 房源数据模型设计
数据模型是整个项目的地基。我常用的模型字段包括CharField存小区名和区域,IntegerField存总价和面积,FloatField存单价,DateTimeField存爬取时间,再加上唯一约束避免重复房源。字段类型一定不要偷懒都用CharField,否则后面做价格区间分组时还要到处转换。
from django.db import models class House(models.Model): house_id = models.CharField(max_length=64, unique=True, verbose_name="房源编号") title = models.CharField(max_length=255, verbose_name="标题") community = models.CharField(max_length=128, verbose_name="小区") district = models.CharField(max_length=64, verbose_name="区域") biz_circle = models.CharField(max_length=64, blank=True, verbose_name="板块") total_price = models.IntegerField(verbose_name="总价(万)") unit_price = models.FloatField(verbose_name="单价(元/平米)") area = models.FloatField(verbose_name="面积(平米)") bedroom = models.IntegerField(default=0, verbose_name="室") hall = models.IntegerField(default=0, verbose_name="厅") bathroom = models.IntegerField(default=0, verbose_name="卫") orientation = models.CharField(max_length=32, blank=True, verbose_name="朝向") floor_text = models.CharField(max_length=32, blank=True, verbose_name="楼层") created_at = models.DateTimeField(auto_now_add=True, verbose_name="抓取时间") class Meta: db_table = "house" verbose_name = "房源信息" indexes = [ models.Index(fields=["district"]), models.Index(fields=["total_price"]), ]这里我特别加了district和total_price的索引,因为后面聚合查询基本都按这两个字段分组。数据量几千条的时候索引效果不明显,但如果爬了超过几万条,索引能明显加快Dashboard的加载速度。另外,house_id字段保存原始房源编号,作为唯一标识,后续爬虫增量更新时可以用它判断是新增还是更新,避免重复数据堆积。
3.3 可视化接口与聚合查询
Django后端给前端提供JSON接口,我推荐用Django REST Framework,虽然重一点,但序列化和API调试页面都帮你做好了。如果不想引第三方库,也可以用JsonResponse手动拼JSON,但代码会随着图表增多而变乱。我的习惯是先用JsonResponse快速跑通,后面需要分页、认证再引入DRF,逐步迭代更符合毕设节奏。
核心接口包括区域房源数量、价格区间分布、户型分布、面积段分布、单价Top榜、总价变化趋势等。这些数据来自同一个数据表,只是聚合维度不同。Django ORM里最常用的是values和annotate,配合Count和Avg。以“区域房源数量”为例:
from django.db.models import Count, Avg from houses.models import House def district_summary(request): data = ( House.objects .values("district") .annotate(count=Count("id"), avg_price=Avg("total_price")) .order_by("-count") ) rows = [{"district": d["district"], "count": d["count"], "avg_price": round(d["avg_price"], 1)} for d in data] return JsonResponse({"data": rows})接口返回的数据结构要尽量和前端图表字段对齐。比如饼图的数据格式是[{name: '区域', value: 数目}],柱状图的格式可以是{x: 区域, y: 平均总价}。我在utility层统一封装了转换函数,避免每个视图都写重复的列表推导式。这样前端Ajax拿到的JSON本身就是“图表友好型”数据,ECharts里几乎可以直接塞进series.data。
3.4 Django执行查询与删除对象时的性能细节
Django ORM用了爽,但有几个细节容易踩坑。第一个是查询性能,如果你在循环里调用外键关联字段,会产生N+1查询。在房源表这种单表模型里还好,但一旦扩展了小区表、经纪公司表,就要注意用select_related或prefetch_related。第二个是聚合查询的类型转换,Avg返回的是Decimal或float,直接塞进JSON时要先round和float,否则序列化会报错。
第三是删除对象的操作。很多人不知道Django的删除分为两种:QuerySet.delete()和对象实例.delete()。两者在信号触发、级联删除上不一样。如果需要根据条件批量删除重复房源,我建议先在QuerySet上filter出目标,再调用.delete()。例如保留每个house_id最新一条,删除旧数据:
from django.utils import timezone from houses.models import House from django.db.models import Count dup_ids = ( House.objects.values("house_id") .annotate(cnt=Count("id")) .filter(cnt__gt=1) .values_list("house_id", flat=True) ) for house_id in dup_ids: latest = House.objects.filter(house_id=house_id).order_by("-created_at").first() if latest: House.objects.filter(house_id=house_id).exclude(pk=latest.pk).delete()这个写法比在Python里逐条判断要安全得多。真正执行delete之前,最好在本地库里先查一遍结果集数量,确信清理范围没有问题再执行。我吃过一次亏,本来想删重复数据,结果条件写错,把正常数据也删了一部分,最后只能重新导入CSV。所以我的建议是:删除操作前先写一个查询脚本,打印出将受影响的行数,确认无误后再执行删除,这个习惯能让你的开发过程稳很多。
4. 可视化大屏与数据分析模块
4.1 大屏布局设计
可视化大屏的布局比想象中重要。常见的12列栅格布局,在1920x1080分辨率下可以分成顶部标题栏、左侧指标卡、中间地图、右侧图表区。顶部放系统名称和数据更新时间,左侧放总房源数、均价、平均面积等核心指标,中间放区域分布地图,右侧放户型分布、价格段分布、面积分布图表。这样一屏能同时容纳信息展示、空间分布和结构化分析三类内容,信息密度适中,不会显得乱。
我在实际项目中直接用Bootstrap栅格加Flex布局,没必要上重型大屏框架。大屏的本质是数据集中展示,第一眼信息层级要清晰。核心指标放在视觉中心偏左,地图放中间,柱状图和饼图放两侧。颜色建议选统一色系,避免一个页面超过四种主色,否则投影仪一投,颜色会互相干扰。暗色背景配亮色图表是目前大屏的主流风格,观感专业,还能掩盖一部分ECharts默认样式的“塑料感”。
4.2 ECharts图表选择与配置
ECharts的图表丰富,但不是越多越好。我用得最多的是地图、柱状图、饼图、折线图、横向条形图五类。地图需要市级的GeoJSON文件,如果不想引入太重的文件,可以用区域名称和数据画散点图,或者用柱状图代替。对二手房项目来说,区域分布用柱状图已经能说明问题,不一定非要地图;如果你想展示地理分布,再引入ECharts map。
以区域均价柱状图为例,js代码要做的是请求接口、解析数据、填充option。一个关键点是option里不要写死数据,所有数据都通过fetch或axios从后端接口动态获取。下面是一个简化配置:
fetch('/api/district_summary/') .then(res => res.json()) .then(json => { const districts = json.data.map(d => d.district); const avgPrices = json.data.map(d => d.avg_price); chart.setOption({ title: { text: '各区域平均总价' }, tooltip: {}, grid: { top: 40, left: 80, right: 40, bottom: 40 }, xAxis: { type: 'category', data: districts }, yAxis: { type: 'value', name: '万元' }, series: [{ type: 'bar', data: avgPrices, itemStyle: { color: '#3b82f6' } }] }); });这里要提醒一下,图表容器div必须有明确高度,否则ECharts会初始化失败或者显示空白。很多小白踩坑都是因为div高度为0,图表白屏。初始化前可以用window.addEventListener('resize')调用chart.resize(),在页面加载完成后渲染。如果图表放在Tab或者折叠面板里,还要在切换显示后手动调用resize方法,才能正常展示。
4.3 从数据到结论:二手房市场的几个分析维度
数据分析不是把图表堆出来就完事,你要能从图表里读出结论。我总结了几个适合毕设展示的分析维度:
- 区域供应量:展示各区域挂牌房源数量,判断房源集中区域。
- 价格分布:按总价区间划分,看刚需和改善型房源占比。
- 户型结构:分析室、厅、卫的分布,揭示主流居住形态。
- 面积段分析:观察90平米以下和140平米以上房源的供需情况。
- 单价与总价关系:用散点图展示面积与总价的关系,辅助判断性价比。
这四个维度分别对应饼图、柱状图、折线图或散点图。每次分析都要配一段说明文字,写清楚“数据告诉我们什么”和“为什么会出现这种现象”。比如某区域房源量最多,但均价最低,可能说明该区域是供应主力,也可能是新盘集中区域。这种结合业务背景的解释,答辩时非常加分,老师会觉得你不只会写代码,还能把数据讲出业务价值。
4.4 响应式适配与加载体验
毕业设计大多数时候是在教室投影仪上演示,分辨率可能是1920x1080,也可能是1366x768。大屏页面要用百分比或者vh/vw单位,避免出现横向滚动条。ECharts的size要基于容器元素,不能设死像素。字体大小也不要用固定px,建议用rem或vw,这样在不同屏幕上缩放不会变形。
性能上也要注意。如果爬了上万条数据,前端一次性加载所有原始房源是不可行的。正确做法是后端聚合后再返回,前端只拿汇总结果。如果地图点太多,可以做聚合和缩放优化。接口层面可以加Django缓存,比如用cache_page缓存热点接口,避免每次刷新都重新算聚合。我测试过,加一层缓存后,大屏接口响应时间能从200ms降到20ms左右,对现场演示体验提升非常明显。
5. 从本地到部署:运行、调试、答辩全流程
5.1 本地运行完整步骤
拿到源码后,推荐按下面的流程跑通:
- 安装Python环境。建议使用Python 3.8以上版本,创建虚拟环境,然后用requirements.txt安装依赖。
- 配置数据库。本地优先用SQLite,零配置,直接跑。如果要换成MySQL,需要到settings.py里改DATABASES配置,并安装mysqlclient。
- 执行迁移命令,生成数据表:python manage.py makemigrations和python manage.py migrate。
- 检查爬虫脚本,把URL换成目标站点当前可访问的城市页面,先拿少量数据测试,再批量爬取。
- 导入数据到数据库,可以用自定义脚本,也可以在Django Admin后台手动添加。推荐先用脚本导入CSV,量大且省事。
- 启动服务:python manage.py runserver,打开浏览器访问Dashboard页面。
很多毕设源码跑不通,问题大多出在依赖版本不一致和数据库配置不匹配上。建议requirements.txt里把Django版本、Requests版本、BeautifulSoup版本全部锁死。如果爬虫字段和目标站点页面结构已经变化,直接跑大概率会失败,这时候知识库里最近的页面结构才是关键的,不要固执地对着旧代码硬调。
5.2 部署时容易被问到的点
如果老师要求项目部署到服务器,有几个点要提前准备。首先是静态文件处理,Django在生产环境默认不托管静态文件,要配置STATIC_ROOT并执行collectstatic。其次是数据库,SQLite在服务器上虽然能用,但跨服务器迁移不方便,建议从开始就使用MySQL或PostgreSQL。第三是WSGI配置,用gunicorn加nginx的方式部署,配置文件尽量写清楚。
部署前还要把settings.py里的DEBUG改为False,ALLOWED_HOSTS填上服务器的域名或IP。如果不改,页面仍然可以显示,但静态文件和错误页面会出现大写异常。在答辩前至少手动部署一遍,避免现场演示时踩到环境坑。我的建议是提前准备一份部署文档,把每条命令都写清楚,既方便自己复查,也能在答辩PPT里作为“工程实践能力”的展示材料。
5.3 毕业设计答辩的展示话术
答辩不是代码朗读,重点要讲“业务逻辑+技术选型+数据结果”。开头花一分半钟讲清项目背景,比如“当前二手房信息分散,购房者需要便捷的数据分析工具”,然后用系统演示串起整个流程:先展示爬虫采集的原始数据,再展示数据库表结构和Admin后台,接着调接口查看JSON返回,最后展示大屏图表。演示顺序最好和代码目录结构一致,老师提问时也能按模块回忆。
常见的答辩追问包括:数据来源是否合规、爬虫如何反反爬、为什么数据量不大、前端图表数据从哪来。这些提前准备一两句话就行,比如“为了避免对目标站点造成压力,我控制了请求频率,并只采集了公开挂牌信息的字段,数据仅用于学习研究”。这种回答既诚实也不会给自己挖坑。如果被问到“为什么不做实时更新”,你就说当前需求主要集中在历史数据分析与可视化展示,实时更新可以放在后续扩展中,不需要把所有功能都堆在毕设里。
6. 实操心得、常见坑与扩展方向
6.1 我踩过的几个真实坑
第一个坑是图表白屏。我写过一个大屏页面,ECharts一直不渲染,排查了两小时发现div的高度没有设置,默认是0。还有一种情况是图表容器放在隐藏的Tab里面,初始化时容器不可见,等Tab切换到可见时图表已经空了,这时候需要手动调用resize或重新init。后来我统一封装了一个renderChart函数,每次初始化前先检查容器高度,低于100px就直接抛异常提醒,提前发现问题。
第二个坑是Django版本兼容。有些毕业设计源码是基于Django 2.x写的,放到Django 4.x上运行,路径配置、URL写法都变了。我的建议是严格按照源码里的requirements安装对应版本,不要图省事装最新版。如果非要用新版,特别是把url的url()改成path()时,要注意正则路由的写法变化,不然经常出现路由匹配不了、页面404的问题。
第三个坑是爬虫字段缺失。爬虫跑着跑着,某条房源数据面积可能是“暂无数据”,如果你直接用float()转换就会报错。我后来统一用自定义parse_float函数处理,遇到空值或非数字返回0.0,并打上日志,保证爬虫不会因为单条数据异常中断。还有一个容易被忽略的坑是编码问题,Windows下CSV写入要用encoding="utf-8-sig",否则用Excel打开会乱码,但Python读取又正常,非常迷惑。
6.2 常见问题速查表
| 问题 | 可能原因 | 解决办法 |
|---|---|---|
| ECharts白屏 | div高度为0或数据未加载 | 给容器设置高度,用resize重新渲染 |
| Django启动报错 | 版本不匹配或依赖缺失 | 确认requirements.txt版本并重新安装 |
| 爬虫429 | 请求频率过高 | 降低频率,加入指数退避重试 |
| 数据库中文字符乱码 | MySQL字符集配置问题 | 建库时使用utf8mb4编码,连接串加charset参数 |
| 页面CSS加载不出来 | 静态文件路径错误 | 检查STATIC_URL和static目录位置 |
| Admin后台打不开 | URL配置或ALLOWED_HOSTS问题 | 检查urls.py,修改ALLOWED_HOSTS |
6.3 后续扩展方向与个人建议
这个项目的天花板比想象中高。爬虫可以从列表页扩展到详情页,字段增加建筑年代、小区物业费、周边配套,从而支持更多分析。后端可以从Django原生视图升级到Django REST Framework加JWT认证,做成前后端分离。前端可以从静态模板转向Vue或React,大屏动画效果会更流畅。数据源也可以换成本地的CSV、Excel和多接口增量抓取,让数据更新更自动化。
我个人在实际操作中的体会是:这类“毕业设计全家桶”项目,最重要的不是追求功能炫酷,而是把全链路跑通并保证可复现。做项目的时候记录好每一步,尤其是爬虫字段变化和数据库迁移命令,后面写论文和答辩都会轻松很多。如果你拿到源码第一跑不通过,深呼吸,先看环境和版本,再逐模块debug,大部分问题都能解决。给自己留出充足的时间把项目跑熟,比临时抱佛脚看代码要有效得多。