尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python房产数据分析大屏:从数据爬取到可视化展示的全链路实战

Python房产数据分析大屏:从数据爬取到可视化展示的全链路实战 最近在帮几个学弟学妹看计算机毕业设计选题发现一个高频出现的“雷区”选题听起来高大上什么“大数据分析”、“可视化大屏”但实际做起来要么是数据爬不动要么是分析逻辑混乱最后只能硬着头皮交一个“静态展示”的Demo。如果你也正为“Python房产数据分析大屏”这类题目发愁觉得它无非是Django搭个网站、ECharts画几个图那可能低估了它的价值也错过了做出亮点的机会。这个项目的核心难点和加分项从来不在Django或ECharts本身而在于如何构建一个从数据获取、清洗、分析到可视化的完整、可靠且可解释的数据管道。很多人卡在第一步——爬虫。不是被封IP就是数据结构解析失败拿到的数据脏乱差后续分析全是空中楼阁。更关键的是数据分析不是简单算个均价、画个饼图。你需要回答有业务价值的问题某个区域的房价受地铁影响有多大学区房溢价在近几年是升是降不同类型的房源商品房、二手房、酒店民宿其价格波动周期有何不同本文将帮你拆解这个毕业设计不止于完成更着眼于“出彩”。我会用一个清晰的流程涵盖从数据爬取策略、Django后端架构、到数据分析方法论与大屏交互设计的全链路并提供可复用的代码模块和避坑指南。目标是让你交出一份不仅功能完整更能体现数据处理能力和业务洞察的作品。1. 这个毕业设计真正考察什么在导师眼里“Python房产数据分析大屏”不是一个简单的网站开发作业。它本质上是一个微型的数据工程项目旨在考察你以下几方面的综合能力数据获取与治理能力爬虫能否稳定、合规地获取结构化数据如何处理反爬数据清洗的流程是否规范后端服务与数据接口能力Django能否设计合理的数据库模型来存储异构数据二手房、新房、民宿能否构建高效、安全的API为前端提供数据服务数据分析与挖掘能力Pandas/NumPy能否运用基本的统计方法和数据挖掘思路如相关性分析、趋势预测从数据中提炼信息而非简单展示数据可视化与前端交互能力ECharts/前端能否选择合适的图表类型传达核心结论大屏布局是否清晰交互是否流畅系统架构与工程化思维项目整体整个数据流采集→存储→处理→服务→展示的设计是否清晰、可扩展、可维护最容易失分的地方把项目做成了一个“静态图表展示网站”。即数据是手动导入的CSV分析是写死的几个数字大屏是固定的几张图片。这完全失去了“数据分析”的动态性和“数据挖掘”的探索性。正确的思路构建一个动态的、数据驱动的系统。即使你的爬虫每天只跑一次但系统设计上要支持数据的定期更新、增量处理分析结果能随着新数据的加入而变化。这才是加分项。2. 技术栈选型与核心概念澄清面对“数据分析”、“数据挖掘”、“大数据”这些词首先要明确在本项目语境下的具体所指避免好高骛远。Python毫无疑问的核心语言在爬虫、数据处理、后端开发上都有成熟生态。Django作为一个“重量级”的全栈Web框架它优势在于其自带的管理后台Admin和健全的ORM对象关系映射。对于需要快速构建数据管理后台和定义复杂数据模型的毕业设计来说能节省大量时间。爬虫通常指requestsBeautifulSoup/lxml或Scrapy框架。本项目数据量不会达到“大数据”级别传统爬虫足够。数据分析核心是Pandas和NumPy。用于数据清洗、转换、聚合和基本统计分析如描述性统计、分组聚合、透视表。数据挖掘在本项目中可以理解为稍深入的分析例如相关性分析计算房价与面积、楼层、学区、地铁距离等的相关系数。简单回归分析尝试用面积、房间数等特征预测房价线性回归。聚类分析对房源进行聚类发现高价区、低价区等K-Means。可视化ECharts是首选图表丰富、交互性强、社区活跃。在Django中可以通过API提供JSON数据由前端JavaScript调用ECharts渲染。大数据请谨慎使用这个词。除非你使用了Hadoop、Spark处理TB级数据否则在毕业设计中提及“大数据”可能适得其反。本项目更贴切的描述是“多源房产数据分析”。技术栈推荐组合后端Django Django REST Framework (用于构建API)数据获取与处理Scrapy (或 requests/BeautifulSoup) Pandas NumPy数据存储PostgreSQL (推荐对JSON、地理空间数据支持好) 或 MySQL前端展示HTML/CSS/JavaScript ECharts (可选) Bootstrap 用于快速布局任务调度APScheduler(用于定时运行爬虫和分析脚本)3. 环境准备与项目初始化假设你使用 macOS/Linux 或 WSL2 (Windows)这是最稳妥的开发环境。3.1 创建虚拟环境与安装依赖永远不要在系统全局Python中安装项目依赖。使用虚拟环境隔离。# 1. 创建项目目录并进入 mkdir property_analysis_dashboard cd property_analysis_dashboard # 2. 创建Python虚拟环境使用Python3.8 python3 -m venv venv # 3. 激活虚拟环境 # macOS/Linux: source venv/bin/activate # Windows: # venv\Scripts\activate # 4. 安装核心依赖 pip install django django-rest-framework pandas numpy scipy scikit-learn # 基础框架与数据分析 pip install requests beautifulsoup4 lxml scrapy # 爬虫相关 pip install psycopg2-binary # PostgreSQL驱动如果用MySQL则安装mysqlclient pip install apscheduler # 定时任务 pip install echarts-united-kingdom-pypi # ECharts Python接口可选用于服务端生成图表3.2 初始化Django项目与应用# 1. 创建Django项目项目名为 config 或 property_analysis django-admin startproject config . # 2. 创建核心应用如 dashboard python manage.py startapp dashboard # 3. 创建数据处理专用应用如 crawler, data_processor python manage.py startapp crawler python manage.py startapp data_processor3.3 配置数据库以PostgreSQL为例在PostgreSQL中创建数据库createdb property_db修改config/settings.py# config/settings.py DATABASES { default: { ENGINE: django.db.backends.postgresql, NAME: property_db, USER: your_db_user, PASSWORD: your_db_password, HOST: localhost, PORT: 5432, } } # 将新建的应用添加到INSTALLED_APPS INSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, # 第三方应用 rest_framework, # 自定义应用 dashboard, crawler, data_processor, ]4. 核心流程拆解四层架构设计一个健壮的系统应该分层这是体现你工程思维的关键。建议采用以下四层架构数据源层 (Sources) ↓ (爬取) 原始数据层 (Raw Data) - 存储在 crawler 应用的模型中 ↓ (清洗、处理) 业务数据层 (Business Data) - 存储在 dashboard 应用的模型中 ↓ (聚合、分析) 分析结果层 (Analysis Results) - 可由 data_processor 计算后存入数据库或缓存 ↓ (API 提供) 展示层 (Presentation) - Django 模板 ECharts4.1 第一层数据模型设计核心中的核心这是项目的基石。设计不合理后面全是坑。在crawler/models.py中定义存储原始爬取数据的模型。它的字段应尽量与源网站保持一致哪怕很“脏”。# crawler/models.py from django.db import models class RawSecondHandHouse(models.Model): 原始二手房数据 source_id models.CharField(max_length100, uniqueTrue) # 源网站ID title models.CharField(max_length255) total_price models.CharField(max_length50) # 字符串如“350万” unit_price models.CharField(max_length50) # 字符串如“65432元/平米” district models.CharField(max_length50) # 区域 area models.CharField(max_length50) # 面积字符串 layout models.CharField(max_length50) # 户型 floor models.CharField(max_length100) # 楼层 direction models.CharField(max_length50) # 朝向 build_year models.CharField(max_length20) # 建造年份 tags models.TextField(blankTrue) # 标签如“满五唯一”“近地铁”JSON字符串或逗号分隔 detail_url models.URLField() crawled_time models.DateTimeField(auto_now_addTrue) source_site models.CharField(max_length50) class Meta: db_table raw_second_hand_house # 指定表名 # 类似地可以定义 RawNewHouse, RawHotel 等模型在dashboard/models.py中定义清洗后的、用于分析和展示的业务模型。这里的字段应该是清洗、转换后的规范格式。# dashboard/models.py from django.db import models class SecondHandHouse(models.Model): 清洗后的二手房业务数据 raw_data models.OneToOneField(crawler.RawSecondHandHouse, on_deletemodels.CASCADE, nullTrue) title models.CharField(max_length255) total_price models.DecimalField(max_digits12, decimal_places2) # 转换为数字单位万元 unit_price models.IntegerField() # 转换为数字单位元/平方米 district models.CharField(max_length50) area models.FloatField() # 转换为数字单位平方米 room models.IntegerField() # 几室从layout中解析 hall models.IntegerField() # 几厅从layout中解析 floor_level models.IntegerField(nullTrue) # 所在楼层数字 total_floors models.IntegerField(nullTrue) # 总楼层数字 direction models.CharField(max_length10) build_year models.IntegerField(nullTrue) has_subway models.BooleanField(defaultFalse) # 是否近地铁从tags解析 is_school_district models.BooleanField(defaultFalse) # 是否学区房 listing_date models.DateField() # 挂牌日期从爬取时间或页面解析 # 可以添加经纬度字段用于地图可视化 # longitude models.FloatField(nullTrue) # latitude models.FloatField(nullTrue) class Meta: indexes [ models.Index(fields[district]), models.Index(fields[unit_price]), models.Index(fields[listing_date]), ]为什么要分两层保留原始痕迹方便回溯和排查解析错误。解耦爬虫逻辑变动不影响业务分析逻辑。灵活性可以针对同一份原始数据尝试不同的清洗规则。4.2 第二层爬虫实现以Scrapy为例在crawler目录下创建Scrapy项目或直接编写爬虫脚本。这里展示一个使用requests和BeautifulSoup的简单模块化示例。# crawler/spiders/lianjia_spider.py import requests from bs4 import BeautifulSoup import time import random from django.utils import timezone from crawler.models import RawSecondHandHouse class LianJiaSpider: def __init__(self): self.base_url https://{city}.lianjia.com/ershoufang/pg{page}/ self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... } def parse_page(self, html_content): 解析列表页HTML soup BeautifulSoup(html_content, lxml) house_list soup.find_all(li, class_clear LOGCLICKDATA) data_list [] for house in house_list: try: title_elem house.find(div, class_title) title title_elem.a.text.strip() if title_elem else detail_url title_elem.a[href] if title_elem else price_info house.find(div, class_priceInfo) total_price price_info.find(div, class_totalPrice).text.strip() unit_price price_info.find(div, class_unitPrice).text.strip() house_info house.find(div, class_houseInfo) info_text house_info.text.strip() if house_info else # 简单拆分实际需要更复杂的解析 infos info_text.split(|) district infos[0].strip() if len(infos) 0 else area_text infos[1].strip() if len(infos) 1 else layout infos[2].strip() if len(infos) 2 else # ... 解析更多字段 data { source_id: detail_url.split(/)[-1].replace(.html, ), title: title, total_price: total_price, unit_price: unit_price, district: district, area: area_text, layout: layout, detail_url: detail_url, source_site: lianjia, } data_list.append(data) except Exception as e: print(f解析房源信息时出错: {e}) continue return data_list def save_to_db(self, data_list): 批量保存原始数据到数据库 objs_to_create [] for data in data_list: # 使用 update_or_create 避免重复 obj, created RawSecondHandHouse.objects.update_or_create( source_iddata[source_id], defaultsdata ) objs_to_create.append(obj) # 批量创建 RawSecondHandHouse.objects.bulk_create(objs_to_create, ignore_conflictsTrue) print(f成功保存 {len(objs_to_create)} 条数据) def run(self, citybj, max_pages3): 主运行方法 for page in range(1, max_pages 1): url self.base_url.format(citycity, pagepage) try: resp requests.get(url, headersself.headers, timeout10) resp.raise_for_status() data_list self.parse_page(resp.text) self.save_to_db(data_list) print(f第 {page} 页抓取完成) time.sleep(random.uniform(1, 3)) # 礼貌性延迟避免被封 except requests.RequestException as e: print(f请求第 {page} 页失败: {e}) break关键点设置User-Agent。异常处理网络请求、解析都可能失败必须捕获异常。延迟time.sleep是基本道德也是保护自己的手段。去重使用update_or_create或检查source_id避免重复数据。4.3 第三层数据清洗与业务逻辑这是体现你数据分析能力的关键环节。在data_processor应用中编写清洗脚本。# data_processor/cleaners/second_hand_cleaner.py import re import pandas as pd from django.db import transaction from crawler.models import RawSecondHandHouse from dashboard.models import SecondHandHouse class SecondHandDataCleaner: staticmethod def extract_number_from_string(text): 从字符串中提取数字如‘350万’ - 350.0 if not text: return None match re.search(r[\d\.], text) return float(match.group()) if match else None staticmethod def parse_layout(layout_str): 解析户型字符串如‘3室1厅’ - (3, 1) if not layout_str: return (None, None) room_match re.search(r(\d)室, layout_str) hall_match re.search(r(\d)厅, layout_str) room int(room_match.group(1)) if room_match else None hall int(hall_match.group(1)) if hall_match else None return (room, hall) staticmethod def parse_floor(floor_str): 解析楼层如‘高楼层/共6层’ - (6, 6) 或 ‘中楼层/共18层’ - (9, 18) # 这是一个简化示例实际逻辑更复杂 if not floor_str: return (None, None) # 实现你的解析逻辑... return (None, None) def clean_and_save_batch(self, batch_size100): 批量清洗并保存数据 raw_qs RawSecondHandHouse.objects.filter(processedFalse)[:batch_size] if not raw_qs.exists(): print(没有待处理的原始数据) return 0 cleaned_objs [] for raw in raw_qs: try: # 清洗逻辑 total_price_num self.extract_number_from_string(raw.total_price) # 万 unit_price_text raw.unit_price.replace(元/平米, ).replace(,, ) unit_price_num self.extract_number_from_string(unit_price_text) area_num self.extract_number_from_string(raw.area) # 平米 room_num, hall_num self.parse_layout(raw.layout) # 判断标签 tags raw.tags.split(,) if raw.tags else [] has_subway any(地铁 in tag for tag in tags) is_school_district any(学区 in tag or 实验 in tag for tag in tags) cleaned_house SecondHandHouse( raw_dataraw, titleraw.title[:255], total_pricetotal_price_num, unit_priceint(unit_price_num) if unit_price_num else None, districtraw.district, areaarea_num, roomroom_num, hallhall_num, directionraw.direction[:10], build_yearint(self.extract_number_from_string(raw.build_year)) if raw.build_year else None, has_subwayhas_subway, is_school_districtis_school_district, listing_dateraw.crawled_time.date() # 假设挂牌日期为爬取日期 ) cleaned_objs.append(cleaned_house) raw.processed True # 假设给Raw模型加一个processed字段 raw.save() except Exception as e: print(f清洗房源 {raw.source_id} 时出错: {e}) continue with transaction.atomic(): SecondHandHouse.objects.bulk_create(cleaned_objs, ignore_conflictsTrue) print(f成功清洗并保存 {len(cleaned_objs)} 条业务数据) return len(cleaned_objs)4.4 第四层数据分析与API提供在dashboard应用中使用 Django REST Framework (DRF) 创建API为前端大屏提供聚合和分析后的数据。首先定义序列化器Serializer和视图集ViewSet。# dashboard/serializers.py from rest_framework import serializers from .models import SecondHandHouse class SecondHandHouseSerializer(serializers.ModelSerializer): class Meta: model SecondHandHouse fields __all__ # dashboard/views.py from rest_framework.viewsets import ReadOnlyModelViewSet from rest_framework.decorators import action from rest_framework.response import Response from django.db.models import Avg, Count, Max, Min from .models import SecondHandHouse from .serializers import SecondHandHouseSerializer import pandas as pd class DashboardDataViewSet(ReadOnlyModelViewSet): queryset SecondHandHouse.objects.all() serializer_class SecondHandHouseSerializer action(detailFalse, methods[get]) def price_trend_by_district(self, request): API: 获取各区域均价趋势按月 # 使用Django ORM进行聚合查询性能更好 from django.db.models.functions import TruncMonth queryset SecondHandHouse.objects.exclude(listing_date__isnullTrue).exclude(unit_price__isnullTrue) data (queryset .annotate(monthTruncMonth(listing_date)) .values(district, month) .annotate(avg_priceAvg(unit_price), countCount(id)) .order_by(district, month)) return Response(data) action(detailFalse, methods[get]) def subway_effect_analysis(self, request): API: 分析地铁对房价的影响 # 使用Pandas进行更复杂的分析 queryset SecondHandHouse.objects.exclude(unit_price__isnullTrue).values(district, has_subway, unit_price) df pd.DataFrame.from_records(queryset) if df.empty: return Response({error: No data}) result [] for district in df[district].unique(): df_district df[df[district] district] subway_avg df_district[df_district[has_subway] True][unit_price].mean() non_subway_avg df_district[df_district[has_subway] False][unit_price].mean() premium ((subway_avg - non_subway_avg) / non_subway_avg * 100) if non_subway_avg else None result.append({ district: district, subway_avg_price: round(subway_avg, 2) if not pd.isna(subway_avg) else None, non_subway_avg_price: round(non_subway_avg, 2) if not pd.isna(non_subway_avg) else None, price_premium_percent: round(premium, 2) if premium else None, sample_count: len(df_district) }) return Response(result) action(detailFalse, methods[get]) def layout_price_distribution(self, request): API: 户型与单价分布 queryset SecondHandHouse.objects.exclude(unit_price__isnullTrue).exclude(room__isnullTrue) data (queryset .values(room) .annotate(avg_priceAvg(unit_price), house_countCount(id)) .order_by(room)) return Response(data)然后在urls.py中注册路由。# dashboard/urls.py from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import DashboardDataViewSet router DefaultRouter() router.register(rdashboard-data, DashboardDataViewSet, basenamedashboard-data) urlpatterns [ path(api/, include(router.urls)), ] # config/urls.py (项目主路由) from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(, include(dashboard.urls)), # 将dashboard的API挂载到根路径 ]4.5 第五层前端大屏与ECharts集成这是展示层。在dashboard/templates/dashboard/下创建index.html。!-- dashboard/templates/dashboard/index.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title房产数据分析大屏/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script link hrefhttps://cdn.jsdelivr.net/npm/bootstrap5.1.3/dist/css/bootstrap.min.css relstylesheet style .chart-container { height: 400px; margin-bottom: 20px; } .card { margin-bottom: 20px; } /style /head body div classcontainer-fluid mt-3 h1 classtext-center mb-4城市房产数据分析大屏/h1 div classrow !-- 图表1: 各区域均价对比 -- div classcol-md-6 div classcard div classcard-header各区域二手房均价对比/div div classcard-body div idchart1 classchart-container/div /div /div /div !-- 图表2: 地铁溢价分析 -- div classcol-md-6 div classcard div classcard-header地铁对房价的影响分析/div div classcard-body div idchart2 classchart-container/div /div /div /div /div !-- 更多图表行... -- /div script // 使用 Fetch API 从Django后端获取数据 function fetchDataAndRender() { // 图表1各区域均价 fetch(/api/dashboard-data/price_trend_by_district/) .then(response response.json()) .then(data { // 这里需要将后端数据聚合为各区域最新均价 // 假设我们处理成 {districts: [], prices: []} const processedData processDistrictPriceData(data); renderChart1(processedData); }); // 图表2地铁溢价 fetch(/api/dashboard-data/subway_effect_analysis/) .then(response response.json()) .then(data { renderChart2(data); }); } function processDistrictPriceData(apiData) { // 简化处理取每个区域最近一个月的数据 const districtMap {}; apiData.forEach(item { if (!districtMap[item.district] || item.month districtMap[item.district].month) { districtMap[item.district] item; } }); const districts Object.keys(districtMap); const prices districts.map(d districtMap[d].avg_price); return { districts, prices }; } function renderChart1(data) { const chartDom document.getElementById(chart1); const myChart echarts.init(chartDom); const option { title: { text: 各区域二手房均价元/㎡, left: center }, tooltip: { trigger: axis }, xAxis: { type: category, data: data.districts, axisLabel: { rotate: 45 } }, yAxis: { type: value, name: 均价 }, series: [{ data: data.prices, type: bar, itemStyle: { color: #5470c6 }, label: { show: true, position: top } }] }; myChart.setOption(option); window.addEventListener(resize, () myChart.resize()); } function renderChart2(apiData) { const chartDom document.getElementById(chart2); const myChart echarts.init(chartDom); // 处理数据绘制柱状对比图有地铁 vs 无地铁 const districts apiData.map(d d.district); const subwayPrices apiData.map(d d.subway_avg_price || 0); const nonSubwayPrices apiData.map(d d.non_subway_avg_price || 0); const option { title: { text: 地铁房源与非地铁房源均价对比, left: center }, tooltip: { trigger: axis }, legend: { data: [近地铁, 非近地铁] }, xAxis: { type: category, data: districts, axisLabel: { rotate: 45 } }, yAxis: { type: value, name: 均价 }, series: [ { name: 近地铁, type: bar, data: subwayPrices }, { name: 非近地铁, type: bar, data: nonSubwayPrices } ] }; myChart.setOption(option); window.addEventListener(resize, () myChart.resize()); } // 页面加载完成后执行 document.addEventListener(DOMContentLoaded, fetchDataAndRender); /script /body /html最后创建一个简单的视图来渲染这个模板。# dashboard/views.py (补充) from django.shortcuts import render def dashboard_view(request): return render(request, dashboard/index.html)# dashboard/urls.py (补充) from .views import dashboard_view urlpatterns [ path(, dashboard_view, namedashboard), # 首页 path(api/, include(router.urls)), ]5. 运行结果与效果验证启动开发服务器python manage.py makemigrations python manage.py migrate python manage.py runserver访问http://127.0.0.1:8000/admin创建超级用户登录可以看到Django Admin后台管理着你定义的模型。运行爬虫 在Django Shell中运行你的爬虫脚本或者编写一个管理命令。python manage.py shell from crawler.spiders.lianjia_spider import LianJiaSpider spider LianJiaSpider() spider.run(citysh, max_pages2) # 抓取上海前2页运行数据清洗python manage.py shell from data_processor.cleaners.second_hand_cleaner import SecondHandDataCleaner cleaner SecondHandDataCleaner() cleaner.clean_and_save_batch(batch_size50)验证数据与API访问http://127.0.0.1:8000/admin检查RawSecondHandHouse和SecondHandHouse表中是否有数据。访问http://127.0.0.1:8000/api/dashboard-data/price_trend_by_district/应该能看到JSON格式的聚合数据。访问http://127.0.0.1:8000/应该能看到大屏页面并且图表正在加载或已显示数据取决于你是否已运行爬虫和清洗脚本。预期效果大屏页面正常加载布局合理。图表根据后端API返回的数据动态渲染。鼠标悬停在图表上有交互提示。不同图表从不同角度展示了数据分析结果区域对比、地铁影响、户型分布等。6. 常见问题与排查思路问题现象可能原因排查方式解决方案爬虫抓不到数据或被封IP1. 网站反爬验证码、JS渲染2. 请求头User-Agent不正确3. 请求频率过高1. 打印响应状态码和文本前500字符2. 使用浏览器开发者工具对比请求头3. 检查页面是否由JavaScript动态生成1. 完善请求头添加Referer, Accept等2. 显著增加请求延迟如3-5秒3. 考虑使用Selenium或Playwright处理JS渲染4.务必遵守网站robots.txt控制抓取量仅用于学习Djangomakemigrations报错1. 模型字段修改冲突2. 数据库连接失败1. 查看具体错误信息2. 检查settings.py数据库配置1. 删除迁移文件migrations/下除__init__.py外的文件和数据库表重新迁移仅开发环境2. 使用python manage.py showmigrations查看状态访问API返回404或5001. URL配置错误2. 视图或序列化器代码有语法错误3. 数据库查询异常1. 检查urls.py路由2. 查看Django运行终端报错信息3. 使用浏览器开发者工具查看网络请求详情1. 确保视图类已正确注册到router2. 在视图函数中加print或使用断点调试3. 使用try...except包裹数据库查询返回友好错误信息ECharts图表不显示1. JavaScript报错控制台查看2. API返回数据格式与ECharts要求不符3. DOM容器高度为01. 浏览器按F12打开控制台2. 打印fetch返回的数据检查结构3. 检查chart-container的CSS高度1. 确保ECharts库已加载2. 按ECharts文档要求格式化数据通常是{xAxis: {data: [...]}, series: [{data: [...]}]}3. 为图表容器设置明确的高度数据分析结果不合理如均价为01. 数据清洗函数有bug导致数值解析失败2. 原始数据中存在大量异常值或空值1. 在清洗脚本中打印中间变量2. 在数据库中用SQL查询unit_price0的记录检查原始数据1. 加强清洗脚本的异常处理和日志记录2. 在清洗前或聚合前进行数据过滤exclude(unit_price__isnullTrue)3. 考虑使用中位数而非平均数7. 最佳实践与工程建议毕业设计加分项使用Django Management Command将爬虫和清洗脚本封装成Django管理命令便于调用和定时任务集成。# crawler/management/commands/run_spider.py from django.core.management.base import BaseCommand from crawler.spiders.lianjia_spider import LianJiaSpider class Command(BaseCommand): help 运行链家爬虫 def add_arguments(self, parser): parser.add_argument(--city, typestr, defaultbj, help城市拼音缩写) parser.add_argument(--pages, typeint, default3, help抓取页数) def handle(self, *args, **options): spider LianJiaSpider() spider.run(cityoptions[city], max_pagesoptions[pages]) self.stdout.write(self.style.SUCCESS(爬虫任务完成))运行python manage.py run_spider --citysh --pages5引入定时任务使用APScheduler在Django内定时执行爬虫和数据清洗模拟生产环境的数据更新流程。可以在config/__init__.py或config/cron.py中启动调度器。添加数据监控与管理利用Django Admin的强大功能为模型添加自定义的筛选、搜索和操作按钮。甚至可以编写简单的Admin Action来手动触发数据清洗。进行简单的数据挖掘在data_processor中增加一个分析模块使用scikit-learn进行简单的线性回归预测房价。即使模型很简单也能极大提升项目深度。# data_processor/analyzers/price_predictor.py import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_error def train_simple_price_model(): from dashboard.models import SecondHandHouse queryset SecondHandHouse.objects.exclude(unit_price__isnullTrue).exclude(area__isnullTrue).exclude(room__isnullTrue) df pd.DataFrame.from_records(queryset.values(area, room, unit_price)) if len(df) 10: return None, 数据量不足 X df[[area, room]] y df[unit_price] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test) mae mean_absolute_error(y_test, y_pred) return model, f模型训练完成测试集平均绝对误差(MAE): {mae:.2f} 元/㎡前端优化加载动画在数据加载时显示loading动画。错误处理处理API请求失败的情况给用户提示。响应式布局确保大屏在不同尺寸设备上基本可用。图表导出添加ECharts的图表导出为图片功能。文档与部署在项目根目录添加README.md清晰说明项目结构、环境配置、运行步骤。尝试使用Docker和docker-compose将项目容器化这会是简历上的一个亮点。考虑将前端静态文件HTML/CSS/JS与Django后端分离部署或使用WhiteNoise服务静态文件。遵循以上步骤和思路你的“Python房产数据分析大屏”毕业设计将不再是一个简单的可视化作业而是一个展现了数据工程全链路思维的完整项目。从稳定的数据采集、规范的数据治理、深入的数据分析到清晰的数据服务和友好的数据展示每一个环节都值得深入打磨。这不仅能帮你顺利通过答辩更能成为你求职时一个有力的实战项目经验。
返回列表