
1. 项目概述这个基于Python的租房数据分析系统是我在完成计算机专业毕业设计时开发的一个综合性项目。它整合了机器学习算法、网络爬虫技术和Web可视化展示能够对二手房市场数据进行深度挖掘和分析。系统采用Django作为后端框架结合Scrapy爬虫采集房源数据通过K-means聚类和线性回归算法进行数据分析最终以直观的可视化图表呈现结果。对于即将面临毕业设计的同学来说这个项目具有很高的参考价值。它涵盖了计算机专业多个核心知识点从数据采集、清洗存储到分析建模再到Web展示完整呈现了一个数据分析系统的开发流程。我在开发过程中踩过不少坑也积累了一些实战经验下面会详细分享这个项目的技术实现细节。2. 系统架构设计2.1 整体技术栈选择系统采用分层架构设计主要分为数据采集层、数据处理层、业务逻辑层和展示层数据采集层Scrapy Playwright 数据处理层Pandas NumPy 分析建模层Scikit-learn Web框架层Django Django REST framework 数据存储MySQL Redis 前端展示ECharts Bootstrap选择Django作为Web框架主要考虑到它的全栈特性自带ORM、Admin后台和模板引擎能快速构建功能完善的管理系统。对于需要处理动态加载内容的房源网站传统的Scrapy难以应对因此引入Playwright解决动态渲染问题。2.2 数据库设计要点房源数据表设计是系统的核心主要字段包括class House(models.Model): title models.CharField(max_length200) # 房源标题 district models.CharField(max_length50) # 行政区 address models.CharField(max_length200) # 详细地址 price models.FloatField() # 价格(万元) area models.FloatField() # 面积(㎡) room models.CharField(max_length20) # 户型 floor models.CharField(max_length30) # 楼层 build_year models.IntegerField() # 建造年份 tags models.JSONField(defaultlist) # 标签 lat models.FloatField(nullTrue) # 纬度 lng models.FloatField(nullTrue) # 经度 created_at models.DateTimeField(auto_now_addTrue)注意实际开发中建议对价格、面积等数值字段添加校验约束避免爬虫采集到异常值影响分析结果。3. 数据采集实现3.1 Scrapy爬虫配置针对主流房产网站需要编写多个Spider处理不同页面结构。以下是基础Spider示例class LianjiaSpider(scrapy.Spider): name lianjia allowed_domains [lianjia.com] def start_requests(self): districts [xuanwu, haidian, chaoyang] for district in districts: url fhttps://bj.lianjia.com/ershoufang/{district}/ yield scrapy.Request(url, meta{playwright: True}) def parse(self, response): houses response.css(.sellListContent li) for house in houses: item {} item[title] house.css(.title a::text).get() item[price] float(house.css(.totalPrice span::text).get()) yield item3.2 动态渲染处理对于需要执行JavaScript的页面配置Playwright中间件# settings.py DOWNLOAD_HANDLERS { http: scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler, https: scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler, } TWISTED_REACTOR twisted.internet.asyncioreactor.AsyncioSelectorReactor PLAYWRIGHT_BROWSER_TYPE chromium实战经验设置适当的DOWNLOAD_DELAY(建议2-5秒)和CONCURRENT_REQUESTS避免触发反爬机制。遇到验证码时可使用Playwright的page.screenshot()保存图片供人工识别。4. 数据分析模块4.1 数据预处理流程原始数据需要经过以下处理步骤缺失值处理删除关键字段缺失的记录异常值过滤剔除价格/面积超出合理范围的极端值特征工程计算单价(price/area)提取楼层类型(低层/中层/高层)计算房龄(当前年份-build_year)def clean_data(df): # 删除缺失关键字段的记录 df df.dropna(subset[price, area, district]) # 过滤异常值 df df[(df[price] 30) (df[price] 2000)] df df[(df[area] 20) (df[area] 300)] # 特征工程 df[unit_price] df[price] / df[area] df[floor_type] df[floor].apply(categorize_floor) df[house_age] datetime.now().year - df[build_year] return df4.2 K-means聚类分析对房源进行聚类可以发现价格相似区域from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler def cluster_houses(df): # 选择特征单价、面积、房龄 X df[[unit_price, area, house_age]] # 标准化 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 肘部法则确定最佳K值 distortions [] for k in range(1, 10): kmeans KMeans(n_clustersk) kmeans.fit(X_scaled) distortions.append(kmeans.inertia_) # 根据肘部位置选择k3 optimal_k 3 kmeans KMeans(n_clustersoptimal_k) df[cluster] kmeans.fit_predict(X_scaled) return df, kmeans.cluster_centers_注意事项不同城市的房价差异较大实际应用中应该按城市分别建模。聚类前务必进行特征标准化避免量纲影响。4.3 线性回归预测模型建立房价预测模型from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split def train_price_model(df): # 准备特征和标签 features [area, house_age, floor_type, district] X pd.get_dummies(df[features], columns[floor_type, district]) y df[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) # 评估 score model.score(X_test, y_test) print(f模型R2得分: {score:.3f}) return model5. Django系统实现5.1 核心功能开发系统主要功能模块房源数据管理CRUD数据分析任务调度可视化展示预测接口# views.py class HouseListView(LoginRequiredMixin, ListView): model House paginate_by 20 template_name house/list.html def get_queryset(self): queryset super().get_queryset() district self.request.GET.get(district) if district: queryset queryset.filter(districtdistrict) return queryset.order_by(-created_at) class AnalyzeView(LoginRequiredMixin, View): def post(self, request): # 获取当前城市所有房源 houses House.objects.filter(cityrequest.user.city) df pd.DataFrame(list(houses.values())) # 执行聚类分析 df_result, centers cluster_houses(df) # 保存分析结果 AnalysisResult.objects.create( userrequest.user, datadf_result.to_dict(records), centerscenters.tolist(), analysis_typecluster ) return redirect(analysis_result)5.2 可视化展示使用ECharts实现交互式图表// 价格分布直方图 function initPriceChart(data) { const chart echarts.init(document.getElementById(price-chart)); const option { title: { text: 价格分布 }, tooltip: {}, xAxis: { type: category, data: [100, 100-200, 200-300, 300-500, 500-800, 800] }, yAxis: { type: value }, series: [{ name: 房源数量, type: bar, data: data }] }; chart.setOption(option); } // 聚类结果散点图 function initClusterChart(data) { const chart echarts.init(document.getElementById(cluster-chart)); const option { title: { text: 房源聚类分析 }, tooltip: { formatter: params { return 单价: ${params.value[0]}br 面积: ${params.value[1]}br 房龄: ${params.value[2]}; } }, xAxis: { name: 单价(元/㎡) }, yAxis: { name: 面积(㎡) }, series: [{ symbolSize: 10, data: data, type: scatter }] }; chart.setOption(option); }6. 部署与优化6.1 生产环境部署推荐使用Docker容器化部署# Dockerfile FROM python:3.9 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . EXPOSE 8000 CMD [gunicorn, --bind, 0.0.0.0:8000, core.wsgi]配套的docker-compose.ymlversion: 3 services: web: build: . ports: - 8000:8000 depends_on: - redis - db environment: - DJANGO_SETTINGS_MODULEcore.settings.prod redis: image: redis:alpine db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} MYSQL_DATABASE: house volumes: - db_data:/var/lib/mysql volumes: db_data:6.2 性能优化技巧数据库优化为常用查询字段添加索引使用select_related/prefetch_related减少查询次数对大数据量表考虑分表缓存策略使用Redis缓存分析结果对静态资源设置长期缓存实现视图级缓存# 使用缓存装饰器 from django.views.decorators.cache import cache_page cache_page(60 * 15) # 缓存15分钟 def price_distribution(request): # ...异步任务 耗时的分析任务使用Celery异步处理# tasks.py app.task def analyze_houses_task(user_id, city): try: user User.objects.get(iduser_id) houses House.objects.filter(citycity) df pd.DataFrame(list(houses.values())) # 执行分析... return {status: success, result_id: result.id} except Exception as e: return {status: error, message: str(e)}7. 常见问题与解决方案7.1 爬虫被封禁现象请求返回403或验证码页面解决方案增加随机User-Agent使用代理IP池降低请求频率使用Playwright模拟人类操作# middlewares.py class RandomUserAgentMiddleware: def process_request(self, request, spider): request.headers[User-Agent] random.choice(USER_AGENTS)7.2 数据分析不准确现象聚类结果不合理或预测误差大排查步骤检查数据清洗是否彻底验证特征选择是否合理尝试不同标准化方法调整模型参数7.3 Django性能瓶颈现象页面加载缓慢优化方案使用django-debug-toolbar定位慢查询添加数据库索引实现分页加载启用Gzip压缩# models.py class House(models.Model): class Meta: indexes [ models.Index(fields[district]), models.Index(fields[price]), ]8. 项目扩展方向这个基础系统还可以进一步扩展多城市支持建立城市维度分析比较不同市场特征价格预警监控异常价格波动发现投资机会房源对比允许用户收藏房源并生成对比报告移动端适配开发响应式界面或独立App增强预测引入更多特征和复杂模型(XGBoost、神经网络)# 使用XGBoost改进预测 from xgboost import XGBRegressor def train_xgboost_model(X_train, y_train): model XGBRegressor( n_estimators100, max_depth5, learning_rate0.1 ) model.fit(X_train, y_train) return model开发这个系统的过程中我最大的体会是真实世界的数据远比课堂示例复杂。数据质量决定分析上限因此在数据采集和清洗阶段投入足够时间非常必要。另一个关键点是合理设置分析粒度比如按行政区划分后再聚类结果会比全局聚类更有意义。