当前位置: 首页 > news >正文

基于Python的二手房源信息爬取与分析的设计和实现,7000字论文编写

摘要

本文设计并实现了一个基于 Python 的二手房源信息爬取与分析系统。该系统通过网络爬虫技术自动从房地产网站获取二手房源信息,经过数据清洗、存储后进行多维度分析,并通过可视化界面展示分析结果。系统采用模块化设计,包括爬虫模块、数据处理模块、存储模块、分析模块和展示模块。实验结果表明,该系统能够有效收集和分析二手房市场数据,为购房者、卖房者和房地产从业者提供决策支持。

1 引言

1.1 研究背景与意义

随着房地产市场的不断发展,二手房交易在整个房地产市场中所占的比重越来越大。对于购房者来说,了解市场行情、比较不同房源的价格和特点是做出明智决策的关键;对于卖房者来说,掌握市场动态、合理定价是成功交易的前提;对于房地产从业者来说,深入分析市场数据、把握市场趋势是提供专业服务的基础。

然而,目前房地产市场信息分散在各个网站,购房者、卖房者和房地产从业者需要花费大量的时间和精力去收集和整理这些信息。此外,由于信息不透明和不对称,市场上存在着价格虚高、虚假房源等问题,给消费者带来了很大的困扰。

因此,开发一个能够自动收集、整理和分析二手房源信息的系统具有重要的现实意义。该系统可以帮助用户快速获取全面、准确的市场信息,提高决策效率和准确性;可以促进市场信息的透明化和对称化,减少市场乱象;可以为房地产研究提供数据支持,推动房地产市场的健康发展。

1.2 国内外研究现状
1.2.1 网络爬虫技术研究现状

网络爬虫是一种自动获取网页内容的程序,它可以按照一定的规则,自动地抓取万维网上的信息。网络爬虫技术的研究始于 20 世纪 90 年代,经过多年的发展,已经取得了很大的进展。目前,网络爬虫技术主要应用于搜索引擎、数据挖掘、信息检索等领域。

在房地产领域,网络爬虫技术也得到了广泛的应用。例如,一些房地产网站利用网络爬虫技术收集其他网站的房源信息,以扩大自己的房源数据库;一些房地产研究机构利用网络爬虫技术收集房地产市场数据,以进行市场分析和预测。

1.2.2 房地产数据分析研究现状

房地产数据分析是指利用统计学、机器学习等方法对房地产市场数据进行分析和挖掘,以发现市场规律、预测市场趋势。房地产数据分析技术的研究始于 20 世纪 70 年代,经过多年的发展,已经取得了很大的进展。目前,房地产数据分析技术主要应用于市场分析、价格预测、投资决策等领域。

在房地产数据分析方面,国内外学者已经开展了大量的研究工作。例如,一些学者利用时间序列分析、回归分析等方法对房价走势进行预测;一些学者利用聚类分析、关联规则挖掘等方法对房地产市场进行细分和分析;一些学者利用空间分析、地理信息系统等方法对房地产市场进行空间分析和可视化。

1.2.3 现有系统存在的问题

虽然国内外已经开发了一些房地产信息系统,但这些系统仍然存在一些问题:

  1. 数据获取不全面:大多数系统只能获取部分网站的房源信息,无法获取整个市场的信息。
  2. 数据质量不高:由于网络爬虫技术的局限性,获取的房源信息可能存在噪声、缺失值等问题。
  3. 分析功能单一:大多数系统只能提供简单的统计分析功能,无法进行深入的数据分析和挖掘。
  4. 可视化效果不佳:大多数系统的可视化效果较差,无法直观地展示分析结果。
  5. 用户体验不好:大多数系统的界面设计不够友好,操作不够便捷,用户体验较差。
1.3 研究内容与目标
1.3.1 研究内容

本论文的研究内容主要包括以下几个方面:

  1. 二手房源信息爬取技术研究:研究如何从房地产网站自动获取二手房源信息,包括网页解析、数据提取、反爬虫策略等。
  2. 二手房源信息处理技术研究:研究如何对爬取的房源信息进行清洗、去重、标准化等处理,提高数据质量。
  3. 二手房源信息存储技术研究:研究如何设计数据库结构,将处理后的房源信息存储到数据库中,方便后续查询和分析。
  4. 二手房源信息分析技术研究:研究如何对存储的房源信息进行多维度分析,包括价格分析、区域分析、户型分析、面积分析等。
  5. 二手房源信息可视化技术研究:研究如何将分析结果以图表、地图等形式直观地展示出来,提高用户体验。
  6. 系统架构设计与实现:研究如何设计系统架构,将各个模块有机地结合起来,实现一个完整的二手房源信息爬取与分析系统。
1.3.2 研究目标

本论文的研究目标是开发一个基于 Python 的二手房源信息爬取与分析系统,该系统应具有以下特点:

  1. 数据获取全面:能够从多个房地产网站自动获取二手房源信息,覆盖整个市场。
  2. 数据质量高:能够对爬取的房源信息进行清洗、去重、标准化等处理,提高数据质量。
  3. 分析功能丰富:能够对存储的房源信息进行多维度分析,包括价格分析、区域分析、户型分析、面积分析等。
  4. 可视化效果好:能够将分析结果以图表、地图等形式直观地展示出来,提高用户体验。
  5. 用户体验好:界面设计友好,操作便捷,用户体验好。
  6. 系统稳定可靠:系统运行稳定可靠,能够满足大规模数据处理和分析的需求。

2 相关技术与理论基础

2.1 Python 编程语言

Python 是一种高级、解释型、通用编程语言,具有以下特点:

  1. 语法简洁:Python 的语法简洁明了,代码可读性高,开发效率高。
  2. 丰富的库支持:Python 拥有丰富的第三方库,如 Scrapy、BeautifulSoup、Pandas、NumPy、Matplotlib、Seaborn 等,这些库可以大大简化开发工作。
  3. 跨平台:Python 可以在 Windows、Linux、Mac OS 等多种操作系统上运行。
  4. 开源免费:Python 是开源免费的,用户可以自由使用、修改和分发。

在本系统中,Python 主要用于开发网络爬虫、数据处理、数据分析和可视化等模块。

2.2 网络爬虫技术

网络爬虫是一种自动获取网页内容的程序,它可以按照一定的规则,自动地抓取万维网上的信息。网络爬虫技术主要包括以下几个方面:

  1. 网页下载:从互联网上下载网页内容。
  2. 网页解析:解析网页内容,提取有用信息。
  3. 数据存储:将提取的信息存储到数据库中。
  4. 反爬虫策略:应对网站的反爬虫机制,保证爬虫的正常运行。

在本系统中,使用 Python 的 Scrapy 框架开发网络爬虫,Scrapy 是一个为了爬取网站数据、提取结构性数据而编写的应用框架,它可以应用在数据挖掘、信息处理或存储历史数据等一系列的程序中。

2.3 数据处理技术

数据处理是指对原始数据进行清洗、转换、整合等操作,以提高数据质量和可用性的过程。数据处理技术主要包括以下几个方面:

  1. 数据清洗:去除数据中的噪声、缺失值、重复值等。
  2. 数据转换:将数据从一种格式转换为另一种格式,如将文本数据转换为数值数据。
  3. 数据整合:将来自不同数据源的数据整合到一起。
  4. 数据标准化:将数据按照一定的标准进行规范化,如将数据进行归一化处理。

在本系统中,使用 Python 的 Pandas 库进行数据处理,Pandas 是一个开源的 Python 数据分析库,它提供了高效、灵活的数据结构和数据分析工具,可以帮助用户快速处理和分析大量数据。

2.4 数据分析技术

数据分析是指利用统计学、机器学习等方法对数据进行分析和挖掘,以发现数据中隐藏的模式和规律的过程。数据分析技术主要包括以下几个方面:

  1. 描述性统计分析:对数据的基本特征进行描述,如均值、中位数、标准差等。
  2. 相关性分析:分析变量之间的相关性,如皮尔逊相关系数、斯皮尔曼相关系数等。
  3. 回归分析:建立变量之间的回归模型,如线性回归、逻辑回归等。
  4. 聚类分析:将数据按照相似性进行分组,如 K-means 聚类、层次聚类等。
  5. 分类分析:将数据按照一定的规则进行分类,如决策树、支持向量机等。

在本系统中,使用 Python 的 NumPy、SciPy 等库进行数据分析,NumPy 是一个开源的 Python 科学计算库,它提供了高效的多维数组对象和计算工具;SciPy 是一个基于 NumPy 的科学计算库,它提供了许多科学计算算法和工具。

2.5 数据可视化技术

数据可视化是指将数据以图表、地图等形式直观地展示出来,以帮助用户更好地理解和分析数据的过程。数据可视化技术主要包括以下几个方面:

  1. 图表可视化:将数据以柱状图、折线图、饼图等形式展示出来。
  2. 地图可视化:将数据以地图的形式展示出来,如热力图、气泡图等。
  3. 交互式可视化:提供交互式界面,让用户可以自由地探索和分析数据。

在本系统中,使用 Python 的 Matplotlib、Seaborn、Plotly 等库进行数据可视化,Matplotlib 是一个开源的 Python 绘图库,它提供了丰富的绘图功能;Seaborn 是一个基于 Matplotlib 的统计数据可视化库,它提供了更高级的绘图功能;Plotly 是一个交互式数据可视化库,它提供了丰富的交互式绘图功能。

2.6 数据库技术

数据库是指按照一定的数据模型组织、存储和管理数据的仓库。数据库技术主要包括以下几个方面:

  1. 关系型数据库:以关系模型为基础的数据库,如 MySQL、Oracle、SQL Server 等。
  2. 非关系型数据库:不以关系模型为基础的数据库,如 MongoDB、Redis 等。
  3. 数据库设计:设计数据库的结构和关系,包括表结构、字段类型、索引等。
  4. 数据库操作:对数据库进行增删改查等操作。

在本系统中,使用 MySQL 作为数据库,MySQL 是一个开源的关系型数据库管理系统,它具有高性能、可靠性和易用性等特点。

3 系统需求分析

3.1 功能需求
3.1.1 数据爬取模块
  1. 支持多网站爬取:能够从多个房地产网站爬取二手房源信息。
  2. 定时爬取:支持定时自动爬取,确保数据的及时性和准确性。
  3. 增量爬取:只爬取新的或更新的房源信息,避免重复爬取。
  4. 反爬虫策略:能够应对网站的反爬虫机制,如 IP 代理、User-Agent 轮换等。
  5. 爬取进度监控:能够监控爬取进度,及时发现和处理爬取过程中出现的问题。
3.1.2 数据处理模块
  1. 数据清洗:去除爬取数据中的噪声、缺失值、重复值等。
  2. 数据转换:将爬取数据转换为统一的格式,便于后续处理和分析。
  3. 数据标准化:对数据进行标准化处理,如将价格、面积等数据进行归一化处理。
  4. 数据验证:验证数据的有效性和合法性,如验证价格、面积等数据是否在合理范围内。
  5. 数据存储:将处理后的数据存储到数据库中。
3.1.3 数据分析模块
  1. 价格分析:分析不同区域、不同户型、不同面积的房价走势和分布情况。
  2. 区域分析:分析不同区域的房源数量、价格水平、供需关系等。
  3. 户型分析:分析不同户型的房源数量、价格水平、受欢迎程度等。
  4. 面积分析:分析不同面积段的房源数量、价格水平、供需关系等。
  5. 趋势预测:基于历史数据,预测房价走势和市场趋势。
  6. 相关性分析:分析房价与区域、户型、面积等因素之间的相关性。
3.1.4 数据可视化模块
  1. 图表可视化:将分析结果以柱状图、折线图、饼图等形式展示出来。
  2. 地图可视化:将房源信息以地图的形式展示出来,支持按区域、价格等条件进行筛选。
  3. 交互式可视化:提供交互式界面,让用户可以自由地探索和分析数据。
  4. 报表生成:支持生成分析报表,如 PDF、Excel 等格式。
3.1.5 用户管理模块
  1. 用户注册与登录:支持用户注册和登录系统。
  2. 用户权限管理:支持不同角色的用户拥有不同的权限。
  3. 用户收藏管理:支持用户收藏感兴趣的房源信息。
  4. 用户分析报告:支持为用户生成个性化的分析报告。
3.1.6 系统管理模块
  1. 爬虫配置管理:支持配置爬虫的参数,如爬取频率、爬取范围等。
  2. 数据备份与恢复:支持定期备份数据,并在需要时恢复数据。
  3. 系统日志管理:记录系统的运行日志,便于排查问题。
  4. 系统监控:监控系统的运行状态,如 CPU 使用率、内存使用率等。
3.2 非功能需求
3.2.1 性能需求
  1. 响应时间:系统的平均响应时间应不超过 3 秒,最大响应时间应不超过 10 秒。
  2. 并发用户数:系统应支持至少 100 个并发用户同时访问。
  3. 数据处理能力:系统应能够处理每天至少 10 万条房源信息的爬取和处理。
  4. 数据分析效率:系统应能够在 5 分钟内完成对 100 万条房源信息的分析。
3.2.2 可靠性需求
  1. 系统可用性:系统的可用性应不低于 99.9%。
  2. 数据完整性:系统应保证数据的完整性,避免数据丢失或损坏。
  3. 容错能力:系统应具备一定的容错能力,能够处理异常情况。
  4. 恢复能力:系统应具备快速恢复能力,在发生故障后能够尽快恢复正常运行。
3.2.3 安全性需求
  1. 数据安全:系统应保证数据的安全性,避免数据泄露。
  2. 用户认证:系统应提供用户认证机制,确保只有授权用户才能访问系统。
  3. 权限控制:系统应提供权限控制机制,确保用户只能访问其权限范围内的资源。
  4. 数据加密:系统应对敏感数据进行加密处理,如用户密码、支付信息等。
3.2.4 易用性需求
  1. 界面友好:系统的界面应简洁、美观、易用,符合用户的使用习惯。
  2. 操作简单:系统的操作应简单、直观,用户无需过多的培训即可上手使用。
  3. 帮助文档:系统应提供详细的帮助文档,帮助用户解决使用过程中遇到的问题。
  4. 反馈机制:系统应提供反馈机制,及时响应用户的意见和建议。
3.2.5 可扩展性需求
  1. 模块扩展:系统应支持模块扩展,能够方便地添加新的功能模块。
  2. 数据扩展:系统应支持数据扩展,能够方便地处理新的数据源和数据类型。
  3. 性能扩展:系统应支持性能扩展,能够通过增加硬件资源来提高系统的性能。

4 系统设计

4.1 总体架构设计

系统采用分层架构设计,主要包括以下几层:

系统的核心模块包括:

  • 数据爬取模块:负责从房地产网站爬取二手房源信息。
  • 数据处理模块:负责对爬取的房源信息进行清洗、转换、标准化等处理。
  • 数据分析模块:负责对处理后的房源信息进行多维度分析。
  • 数据可视化模块:负责将分析结果以图表、地图等形式直观地展示出来。
  • 用户管理模块:负责用户的注册、登录、权限管理等。
  • 系统管理模块:负责系统的配置、监控、备份等管理工作。
4.2 数据库设计
4.2.1 数据库表结构

房源信息表 (house_info)

sql

CREATE TABLE `house_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '房源ID',`house_code` varchar(50) NOT NULL COMMENT '房源编码',`title` varchar(255) NOT NULL COMMENT '房源标题',`price` decimal(10,2) NOT NULL COMMENT '价格(万元)',`unit_price` int(11) DEFAULT NULL COMMENT '单价(元/平方米)',`area` decimal(10,2) DEFAULT NULL COMMENT '面积(平方米)',`house_type` varchar(50) DEFAULT NULL COMMENT '户型',`floor` varchar(50) DEFAULT NULL COMMENT '楼层',`build_year` varchar(20) DEFAULT NULL COMMENT '建造年份',`orientation` varchar(20) DEFAULT NULL COMMENT '朝向',`decoration` varchar(20) DEFAULT NULL COMMENT '装修情况',`community` varchar(100) DEFAULT NULL COMMENT '小区名称',`region` varchar(50) DEFAULT NULL COMMENT '区域',`subway` varchar(100) DEFAULT NULL COMMENT '地铁信息',`address` varchar(255) DEFAULT NULL COMMENT '详细地址',`longitude` decimal(10,7) DEFAULT NULL COMMENT '经度',`latitude` decimal(10,7) DEFAULT NULL COMMENT '纬度',`house_url` varchar(255) DEFAULT NULL COMMENT '房源URL',`publish_time` datetime DEFAULT NULL COMMENT '发布时间',`crawl_time` datetime NOT NULL COMMENT '爬取时间',`source` varchar(50) NOT NULL COMMENT '数据源',`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(1:有效,0:无效)',`update_time` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `idx_house_code` (`house_code`),KEY `idx_price` (`price`),KEY `idx_area` (`area`),KEY `idx_region` (`region`),KEY `idx_community` (`community`),KEY `idx_crawl_time` (`crawl_time`),KEY `idx_source` (`source`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源信息表';

小区信息表 (community_info)

sql

CREATE TABLE `community_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '小区ID',`name` varchar(100) NOT NULL COMMENT '小区名称',`region` varchar(50) NOT NULL COMMENT '区域',`address` varchar(255) DEFAULT NULL COMMENT '详细地址',`build_year` varchar(20) DEFAULT NULL COMMENT '建造年份',`developer` varchar(100) DEFAULT NULL COMMENT '开发商',`property_company` varchar(100) DEFAULT NULL COMMENT '物业公司',`property_fee` decimal(5,2) DEFAULT NULL COMMENT '物业费(元/平方米·月)',`building_count` int(11) DEFAULT NULL COMMENT '楼栋总数',`house_count` int(11) DEFAULT NULL COMMENT '房屋总数',`longitude` decimal(10,7) DEFAULT NULL COMMENT '经度',`latitude` decimal(10,7) DEFAULT NULL COMMENT '纬度',`avg_price` decimal(10,2) DEFAULT NULL COMMENT '均价(元/平方米)',`crawl_time` datetime NOT NULL COMMENT '爬取时间',`source` varchar(50) NOT NULL COMMENT '数据源',`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(1:有效,0:无效)',`update_time` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `idx_name_region` (`name`,`region`),KEY `idx_region` (`region`),KEY `idx_build_year` (`build_year`),KEY `idx_avg_price` (`avg_price`),KEY `idx_crawl_time` (`crawl_time`),KEY `idx_source` (`source`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='小区信息表';

区域信息表 (region_info)

sql

CREATE TABLE `region_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '区域ID',`name` varchar(50) NOT NULL COMMENT '区域名称',`parent_id` bigint(20) DEFAULT NULL COMMENT '父区域ID',`level` tinyint(4) NOT NULL COMMENT '区域级别(1:城市,2:区县,3:街道/乡镇)',`longitude` decimal(10,7) DEFAULT NULL COMMENT '经度',`latitude` decimal(10,7) DEFAULT NULL COMMENT '纬度',`area` decimal(10,2) DEFAULT NULL COMMENT '面积(平方公里)',`population` bigint(20) DEFAULT NULL COMMENT '人口',`gdp` decimal(15,2) DEFAULT NULL COMMENT 'GDP(亿元)',`avg_price` decimal(10,2) DEFAULT NULL COMMENT '均价(元/平方米)',`house_count` int(11) DEFAULT NULL COMMENT '房源数量',`update_time` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `idx_name_parent_id` (`name`,`parent_id`),KEY `idx_parent_id` (`parent_id`),KEY `idx_level` (`level`),KEY `idx_avg_price` (`avg_price`),KEY `idx_house_count` (`house_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='区域信息表';

地铁信息表 (subway_info)

sql

CREATE TABLE `subway_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '地铁ID',`name` varchar(50) NOT NULL COMMENT '地铁名称',`line` varchar(50) NOT NULL COMMENT '线路',`region` varchar(50) DEFAULT NULL COMMENT '所在区域',`longitude` decimal(10,7) DEFAULT NULL COMMENT '经度',`latitude` decimal(10,7) DEFAULT NULL COMMENT '纬度',`update_time` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `idx_name_line` (`name`,`line`),KEY `idx_line` (`line`),KEY `idx_region` (`region`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='地铁信息表';

用户信息表 (user_info)

sql

CREATE TABLE `user_info` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',`username` varchar(50) NOT NULL COMMENT '用户名',`password` varchar(100) NOT NULL COMMENT '密码(加密)',`email` varchar(100) DEFAULT NULL COMMENT '邮箱',`phone` varchar(20) DEFAULT NULL COMMENT '手机号',`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',`id_card` varchar(20) DEFAULT NULL COMMENT '身份证号',`avatar` varchar(255) DEFAULT NULL COMMENT '头像',`role` tinyint(4) NOT NULL DEFAULT '1' COMMENT '角色(1:普通用户,2:管理员)',`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(1:正常,0:禁用)',`create_time` datetime NOT NULL COMMENT '创建时间',`update_time` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `idx_username` (`username`),UNIQUE KEY `idx_email` (`email`),UNIQUE KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';

用户收藏表 (user_favorite)

sql

CREATE TABLE `user_favorite` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '收藏ID',`user_id` bigint(20) NOT NULL COMMENT '用户ID',`house_id` bigint(20) NOT NULL COMMENT '房源ID',`create_time` datetime NOT NULL COMMENT '收藏时间',PRIMARY KEY (`id`),UNIQUE KEY `idx_user_id_house_id` (`user_id`,`house_id`),KEY `idx_house_id` (`house_id`),KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户收藏表';

系统配置表 (system_config)

sql

CREATE TABLE `system_config` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '配置ID',`config_key` varchar(100) NOT NULL COMMENT '配置键',`config_value` text NOT NULL COMMENT '配置值',`description` varchar(255) DEFAULT NULL COMMENT '描述',`create_time` datetime NOT NULL COMMENT '创建时间',`update_time` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `idx_config_key` (`config_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';

爬虫日志表 (crawl_log)

sql

CREATE TABLE `crawl_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '日志ID',`source` varchar(50) NOT NULL COMMENT '数据源',`url` varchar(255) DEFAULT NULL COMMENT 'URL',`status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态(1:成功,0:失败)',`message` text DEFAULT NULL COMMENT '消息',`start_time` datetime NOT NULL COMMENT '开始时间',`end_time` datetime NOT NULL COMMENT '结束时间',`duration` int(11) DEFAULT NULL COMMENT '耗时(毫秒)',`create_time` datetime NOT NULL COMMENT '创建时间',PRIMARY KEY (`id`),KEY `idx_source` (`source`),KEY `idx_status` (`status`),KEY `idx_start_time` (`start_time`),KEY `idx_end_time` (`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='爬虫日志表';

分析报告表 (analysis_report)

sql

CREATE TABLE `analysis_report` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '报告ID',`user_id` bigint(20) DEFAULT NULL COMMENT '用户ID',`title` varchar(255) NOT NULL COMMENT '报告标题',`type` varchar(50) NOT NULL COMMENT '报告类型',`content` text NOT NULL COMMENT '报告内容',`start_date` date DEFAULT NULL COMMENT '开始日期',`end_date` date DEFAULT NULL COMMENT '结束日期',`region` varchar(50) DEFAULT NULL COMMENT '区域',`create_time` datetime NOT NULL COMMENT '创建时间',`update_time` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),KEY `idx_user_id` (`user_id`),KEY `idx_type` (`type`),KEY `idx_create_time` (`create_time`),KEY `idx_region` (`region`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分析报告表';
4.3 系统架构图

4.4 部署架构图

4.5 用例图

4.6 界面原型
4.6.1 首页

首页展示热门房源、价格趋势、区域分布等信息,用户可以进行搜索、筛选等操作。

4.6.2 房源列表页

展示符合条件的房源列表,包括房源基本信息、价格、面积等,用户可以进行排序、筛选等操作。

4.6.3 房源详情页

展示房源的详细信息,包括房源描述、户型图、小区信息、周边配套等,用户可以进行收藏、分享等操作。

4.6.4 数据分析页

展示房价趋势、区域分析、户型分析、面积分析等数据可视化图表,用户可以进行筛选、导出等操作。

4.6.5 地图找房页

以地图形式展示房源分布,用户可以通过地图浏览房源,查看房源详情。

4.6.6 用户中心页

展示用户的个人信息、收藏房源、分析报告等,用户可以进行个人信息修改、密码修改等操作。

4.6.7 管理后台首页

展示系统概览信息,包括房源总数、新增房源数、爬虫状态等。

4.6.8 爬虫管理页

管理员可以配置爬虫参数、启动 / 停止爬虫、查看爬虫日志等。

4.6.9 数据管理页

管理员可以进行数据清洗、转换、分析等操作,查看数据统计信息。

4.6.10 用户管理页

管理员可以查看、编辑、禁用用户信息,分配用户权限等。

4.6.11 系统配置页

管理员可以配置系统参数,如数据库连接、缓存配置、邮件配置等。

4.6.12 系统监控页

管理员可以监控系统的运行状态,如 CPU 使用率、内存使用率、网络流量等。

5 系统实现

5.1 后端实现
5.1.1 项目结构

plaintext

house-analysis-system/
├── src/
│   ├── main/
│   │   ├── python/
│   │   │   └── house_analysis/
│   │   │       ├── __init__.py
│   │   │       ├── app.py                      # Flask应用入口
│   │   │       ├── config.py                   # 配置文件
│   │   │       ├── models/                    # 数据模型
│   │   │       │   ├── __init__.py
│   │   │       │   ├── house_info.py
│   │   │       │   ├── community_info.py
│   │   │       │   ├── region_info.py
│   │   │       │   ├── subway_info.py
│   │   │       │   ├── user_info.py
│   │   │       │   ├── user_favorite.py
│   │   │       │   ├── system_config.py
│   │   │       │   ├── crawl_log.py
│   │   │       │   └── analysis_report.py
│   │   │       ├── controllers/               # 控制器
│   │   │       │   ├── __init__.py
│   │   │       │   ├── house_controller.py
│   │   │       │   ├── community_controller.py
│   │   │       │   ├── region_controller.py
│   │   │       │   ├── subway_controller.py
│   │   │       │   ├── user_controller.py
│   │   │       │   ├── analysis_controller.py
│   │   │       │   └── admin_controller.py
│   │   │       ├── services/                  # 服务层
│   │   │       │   ├── __init__.py
│   │   │       │   ├── house_service.py
│   │   │       │   ├── community_service.py
│   │   │       │   ├── region_service.py
│   │   │       │   ├── subway_service.py
│   │   │       │   ├── user_service.py
│   │   │       │   ├── crawl_service.py
│   │   │       │   ├── data_processing_service.py
│   │   │       │   ├── analysis_service.py
│   │   │       │   └── report_service.py
│   │   │       ├── spiders/                   # 爬虫
│   │   │       │   ├── __init__.py
│   │   │       │   ├── base_spider.py
│   │   │       │   ├── lianjia_spider.py
│   │   │       │   ├── ke_spider.py
│   │   │       │   ├── anjuke_spider.py
│   │   │       │   └── community_spider.py
│   │   │       ├── utils/                     # 工具类
│   │   │       │   ├── __init__.py
│   │   │       │   ├── db_utils.py
│   │   │       │   ├── security_utils.py
│   │   │       │   ├── date_utils.py
│   │   │       │   ├── file_utils.py
│   │   │       │   ├── http_utils.py
│   │   │       │   ├── log_utils.py
│   │   │       │   └── spider_utils.py
│   │   │       ├── auth/                      # 认证授权
│   │   │       │   ├── __init__.py
│   │   │       │   ├── jwt_utils.py
│   │   │       │   └── auth_middleware.py
│   │   │       ├── scheduler/                 # 定时任务
│   │   │       │   ├── __init__.py
│   │   │       │   ├── daily_tasks.py
│   │   │       │   └── hourly_tasks.py
│   │   │       └── api/                       # API路由
│   │   │           ├── __init__.py
│   │   │           ├── v1/
│   │   │           │   ├── __init__.py
│   │   │           │   ├── house_api.py
│   │   │           │   ├── community_api.py
│   │   │           │   ├── region_api.py
│   │   │           │   ├── subway_api.py
│   │   │           │   ├── user_api.py
│   │   │           │   ├── analysis_api.py
│   │   │           │   └── admin_api.py
│   │   ├── static/                            # 静态资源
│   │   │   ├── css/
│   │   │   ├── js/
│   │   │   ├── images/
│   │   │   └── reports/
│   │   └── templates/                         # HTML模板
│   │       ├── index.html
│   │       ├── house_list.html
│   │       ├── house_detail.html
│   │       ├── analysis.html
│   │       ├── map.html
│   │       ├── user_center.html
│   │       ├── login.html
│   │       ├── register.html
│   │       └── admin/
│   │           ├── dashboard.html
│   │           ├── spiders.html
│   │           ├── data_management.html
│   │           ├── users.html
│   │           ├── system_config.html
│   │           └── system_monitor.html
│   └── test/                                  # 测试代码
├── requirements.txt                           # 依赖包
├── run.py                                     # 运行入口
└── README.md                                  # 项目说明
5.1.2 核心代码实现

爬虫模块实现

python

运行

import scrapy
import re
import time
import random
import logging
from datetime import datetime
from scrapy.http import Request
from scrapy.selector import Selector
from house_analysis.models import HouseInfo, CommunityInfo, CrawlLog
from house_analysis.utils import db_utils, date_utils, spider_utils
from house_analysis.services import house_service, community_serviceclass BaseSpider(scrapy.Spider):"""基础爬虫类"""def __init__(self, *args, **kwargs):"""初始化爬虫"""super(BaseSpider, self).__init__(*args, **kwargs)self.logger = logging.getLogger(self.name)self.headers = {'User-Agent': spider_utils.get_random_user_agent(),'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2','Connection': 'keep-alive','Upgrade-Insecure-Requests': '1',}self.proxies = spider_utils.get_proxies()self.current_proxy = Noneself.switch_proxy_count = 0self.max_switch_proxy_count = 100def get_proxy(self):"""获取代理"""if not self.proxies:return Noneif self.current_proxy is None or self.switch_proxy_count >= self.max_switch_proxy_count:self.current_proxy = random.choice(self.proxies)self.switch_proxy_count = 0else:self.switch_proxy_count += 1return self.current_proxydef start_requests(self):"""开始请求"""start_urls = self.get_start_urls()for url in start_urls:yield Request(url=url,headers=self.headers,meta={'proxy': self.get_proxy()},callback=self.parse,errback=self.handle_error)def parse(self, response):"""解析响应"""raise NotImplementedError("子类必须实现parse方法")def handle_error(self, failure):"""处理错误"""request = failure.requestself.logger.error(f"Request failed: {request.url}, error: {failure}")# 记录错误日志crawl_log = CrawlLog()crawl_log.source = self.namecrawl_log.url = request.urlcrawl_log.status = 0crawl_log.message = str(failure)crawl_log.start_time = datetime.fromtimestamp(request.meta.get('start_time', time.time()))crawl_log.end_time = datetime.now()crawl_log.duration = int((crawl_log.end_time - crawl_log.start_time).total_seconds() * 1000)crawl_log.create_time = datetime.now()db_utils.db.session.add(crawl_log)db_utils.db.session.commit()# 切换代理self.current_proxy = None# 重试请求if 'retry_times' not in request.meta or request.meta['retry_times'] < 3:retry_times = request.meta.get('retry_times', 0) + 1self.logger.info(f"Retrying request: {request.url}, retry_times: {retry_times}")yield Request(url=request.url,headers=self.headers,meta={'proxy': self.get_proxy(),'retry_times': retry_times,'start_time': time.time()},callback=self.parse,errback=self.handle_error,dont_filter=True)def get_start_urls(self):"""获取起始URL"""raise NotImplementedError("子类必须实现get_start_urls方法")def save_house_info(self, house_data):"""保存房源信息"""# 创建房源对象house = HouseInfo()house.house_code = house_data.get('house_code')house.title = house_data.get('title')house.price = house_data.get('price')house.unit_price = house_data.get('unit_price')house.area = house_data.get('area')house.house_type = house_data.get('house_type')house.floor = house_data.get('floor')house.build_year = house_data.get('build_year')house.orientation = house_data.get('orientation')house.decoration = house_data.get('decoration')house.community = house_data.get('community')house.region = house_data.get('region')house.subway = house_data.get('subway')house.address = house_data.get('address')house.longitude = house_data.get('longitude')house.latitude = house_data.get('latitude')house.house_url = house_data.get('house_url')house.publish_time = house_data.get('publish_time')house.crawl_time = datetime.now()house.source = self.namehouse.status = 1house.update_time = datetime.now()# 保存房源信息house_service.save_house(house)# 记录爬取日志crawl_log = CrawlLog()crawl_log.source = self.namecrawl_log.url = house.house_urlcrawl_log.status = 1crawl_log.start_time = house_data.get('start_time', datetime.now())crawl_log.end_time = datetime.now()crawl_log.duration = int((crawl_log.end_time - crawl_log.start_time).total_seconds() * 1000)crawl_log.create_time = datetime.now()db_utils.db.session.add(crawl_log)db_utils.db.session.commit()def save_community_info(self, community_data):"""保存小区信息"""# 创建小区对象community = CommunityInfo()community.name = community_data.get('name')community.region = community_data.get('region')community.address = community_data.get('address')community.build_year = community_data.get('build_year')community.developer = community_data.get('developer')community.property_company = community_data.get('property_company')community.property_fee = community_data.get('property_fee')community.building_count = community_data.get('building_count')community.house_count = community_data.get('house_count')community.longitude = community_data.get('longitude')community.latitude = community_data.get('latitude')community.avg_price = community_data.get('avg_price')community.crawl_time = datetime.now()community.source = self.namecommunity.status = 1community.update_time = datetime.now()# 保存小区信息community_service.save_community(community)# 记录爬取日志crawl_log = CrawlLog()crawl_log.source = self.namecrawl_log.url = community_data.get('community_url')crawl_log.status = 1crawl_log.start_time = community_data.get('start_time', datetime.now())crawl_log.end_time = datetime.now()crawl_log.duration = int((crawl_log.end_time - crawl_log.start_time).total_seconds() * 1000)crawl_log.create_time = datetime.now()db_utils.db.session.add(crawl_log)db_utils.db.session.commit()class LianjiaSpider(BaseSpider):"""链家爬虫"""name = "lianjia"allowed_domains = ["lianjia.com"]base_url = "https://bj.lianjia.com/ershoufang/"def get_start_urls(self):"""获取起始URL"""# 获取区域列表regions = self.get_regions()# 构建起始URL列表start_urls = []for region in regions:for page in range(1, 101):  # 最多爬取100页url = f"{self.base_url}{region}/pg{page}/"start_urls.append(url)return start_urlsdef get_regions(self):"""获取区域列表"""# 这里简化处理,实际应从页面解析获取regions = ["dongcheng", "xicheng", "chaoyang", "haidian", "fengtai", "shijingshan", "tongzhou", "changping", "daxing", "yizhuangkaifaqu", "shunyi", "fangshan"]return regionsdef parse(self, response):"""解析房源列表页"""# 记录开始时间start_time = datetime.now()# 解析房源列表sel = Selector(response)house_items = sel.css('.sellListContent li.clear')for item in house_items:# 解析房源信息house_url = item.css('.title a::attr(href)').extract_first()if not house_url:continue# 访问房源详情页yield Request(url=house_url,headers=self.headers,meta={'proxy': self.get_proxy(),'start_time': start_time},callback=self.parse_house_detail,errback=self.handle_error)# 解析下一页next_page = sel.css('.next::attr(href)').extract_first()if next_page:next_page_url = response.urljoin(next_page)yield Request(url=next_page_url,headers=self.headers,meta={'proxy': self.get_proxy(),'start_time': start_time},callback=self.parse,errback=self.handle_error)def parse_house_detail(self, response):"""解析房源详情页"""# 记录开始时间start_time = response.meta.get('start_time', datetime.now())# 解析房源信息sel = Selector(response)# 提取房源编码house_code = response.url.split('/')[-1].split('.')[0]# 提取标题title = sel.css('.title .main::text').extract_first()# 提取价格total_price = sel.css('.price .total::text').extract_first()if total_price:total_price = float(total_price)else:total_price = 0unit_price = sel.css('.unitPriceValue::text').extract_first()if unit_price:unit_price = int(re.sub(r'[^\d]', '', unit_price))else:unit_price = 0# 提取基本信息base_info = sel.css('.base .content li')area = 0house_type = ""floor = ""orientation = ""decoration = ""build_year = ""for info in base_info:label = info.css('::text').extract_first()value = info.css('::text').extract()[1] if len(info.css('::text').extract()) > 1 else ""if "建筑面积" in label:area = float(re.sub(r'[^\d.]', '', value))elif "房屋户型" in label:house_type = valueelif "所在楼层" in label:floor = valueelif "房屋朝向" in label:orientation = valueelif "装修情况" in label:decoration = valueelif "建成年代" in label:build_year = value# 提取小区信息community = sel.css('.communityName .info::text').extract_first()# 提取区域信息region = sel.css('.areaName .info a::text').extract_first()# 提取地铁信息subway = sel.css('.subway .info::text').extract_first()# 提取地址address = sel.css('.areaName .info a::text').extract()address = " ".join(address) if address else ""# 提取经纬度longitude = 0latitude = 0map_info = sel.css('.around .info a::attr(href)').extract_first()if map_info and "resblockPosition" in map_info:position = map_info.split("resblockPosition=")[1].split("&")[0]if "," in position:longitude, latitude = position.split(",")longitude = float(longitude)latitude = float(latitude)# 提取发布时间publish_time = sel.css('.transaction .content li:nth-child(3) span:nth-child(2)::text').extract_first()if publish_time:publish_time = date_utils.parse_date(publish_time)# 保存房源信息house_data = {'house_code': house_code,'title': title,'price': total_price,'unit_price': unit_price,'area': area,'house_type': house_type,'floor': floor,'build_year': build_year,'orientation': orientation,'decoration': decoration,'community': community,'region': region,'subway': subway,'address': address,'longitude': longitude,'latitude': latitude,'house_url': response.url,'publish_time': publish_time,'start_time': start_time}self.save_house_info(house_data)# 提取小区链接并爬取小区信息community_url = sel.css('.communityName .info::attr(href)').extract_first()if community_url:community_url = response.urljoin(community_url)yield Request(url=community_url,headers=self.headers,meta={'proxy': self.get_proxy(),'start_time': datetime.now()},callback=self.parse_community_detail,errback=self.handle_error)def parse_community_detail(self, response):"""解析小区详情页"""# 记录开始时间start_time = response.meta.get('start_time', datetime.now())# 解析小区信息sel = Selector(response)# 提取小区名称name = sel.css('.detailHeader .title::text').extract_first()# 提取区域信息region = sel.css('.areaName a::text').extract()region = " ".join(region) if region else ""# 提取地址address = sel.css('.detailHeader .subTitle::text').extract_first()# 提取基本信息base_info = sel.css('.xiaoquInfoItem')build_year = ""developer = ""property_company = ""property_fee = 0building_count = 0house_count = 0for info in base_info:label = info.css('.xiaoquInfoLabel::text').extract_first()value = info.css('.xiaoquInfoContent::text').extract_first()if "建成年代" in label:build_year = valueelif "开发商" in label:developer = valueelif "物业公司" in label:property_company = valueelif "物业费" in label:if value:property_fee = float(re.sub(r'[^\d.]', '', value))elif "楼栋总数" in label:if value:building_count = int(re.sub(r'[^\d]', '', value))elif "房屋总数" in label:if value:house_count = int(re.sub(r'[^\d]', '', value))# 提取均价avg_price = sel.css('.xiaoquUnitPrice::text').extract_first()if avg_price:avg_price = int(re.sub(r'[^\d]', '', avg_price))else:avg_price = 0# 提取经纬度longitude = 0latitude = 0map_info = sel.css('.mapContainer::attr(data-coord)').extract_first()if map_info and "," in map_info:longitude, latitude = map_info.split(",")longitude = float(longitude)latitude = float(latitude)# 保存小区信息community_data = {'name': name,'region': region,'address': address,'build_year': build_year,'developer': developer,'property_company': property_company,'property_fee': property_fee,'building_count': building_count,'house_count': house_count,'longitude': longitude,'latitude': latitude,'avg_price': avg_price,'community_url': response.url,'start_time': start_time}self.save_community_info(community_data)

数据分析模块实现

python

运行

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
from sqlalchemy import func
from matplotlib.font_manager import FontProperties
from house_analysis.models import HouseInfo, CommunityInfo, RegionInfo, AnalysisReport
from house_analysis.utils import db_utils, date_utils, file_utilsclass AnalysisService:"""数据分析服务"""def __init__(self):"""初始化分析服务"""self.font = FontProperties(fname='simhei.ttf', size=12)plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号sns.set(font='SimHei')  # 解决Seaborn中文显示问题def get_house_data(self, start_date=None, end_date=None, region=None, min_price=None, max_price=None, min_area=None, max_area=None, house_type=None):"""获取房源数据"""query = HouseInfo.query# 筛选日期范围if start_date and end_date:query = query.filter(HouseInfo.crawl_time.between(start_date, end_date))# 筛选区域if region:query = query.filter(HouseInfo.region == region)# 筛选价格范围if min_price:query = query.filter(HouseInfo.price >= min_price)if max_price:query = query.filter(HouseInfo.price <= max_price)# 筛选面积范围if min_area:query = query.filter(HouseInfo.area >= min_area)if max_area:query = query.filter(HouseInfo.area <= max_area)# 筛选户型if house_type:query = query.filter(HouseInfo.house_type.like(f'%{house_type}%'))# 获取数据houses = query.all()# 转换为DataFramedata = []for house in houses:data.append({'id': house.id,'house_code': house.house_code,'title': house.title,'price': house.price,'unit_price': house.unit_price,'area': house.area,'house_type': house.house_type,'floor': house.floor,'build_year': house.build_year,'orientation': house.orientation,'decoration': house.decoration,'community': house.community,'region': house.region,'subway': house.subway,'address': house.address,'longitude': house.longitude,'latitude': house.latitude,'publish_time': house.publish_time,'crawl_time': house.crawl_time,'source': house.source})return pd.DataFrame(data)def get_community_data(self, start_date=None, end_date=None, region=None):"""获取小区数据"""query = CommunityInfo.query# 筛选日期范围if start_date and end_date:query = query.filter(CommunityInfo.crawl_time.between(start_date, end_date))# 筛选区域if region:query = query.filter(CommunityInfo.region == region)# 获取数据communities = query.all()# 转换为DataFramedata = []for community in communities:data.append({'id': community.id,'name': community.name,'region': community.region,'address': community.address,'build_year': community.build_year,'developer': community.developer,'property_company': community.property_company,'property_fee': community.property_fee,'building_count': community.building_count,'house_count': community.house_count,'longitude': community.longitude,'latitude': community.latitude,'avg_price': community.avg_price,'crawl_time': community.crawl_time,'source': community.source})return pd.DataFrame(data)def analyze_price_trend(self, df, date_field='crawl_time', price_field='price'):"""分析价格趋势"""if df.empty:return None# 按日期分组计算平均价格df[date_field] = pd.to_datetime(df[date_field])df['date'] = df[date_field].dt.dateprice_trend = df.groupby('date')[price_field].mean().reset_index()# 按日期排序price_trend = price_trend.sort_values('date')return price_trenddef analyze_region_price(self, df, region_field='region', price_field='price'):"""分析区域价格"""if df.empty:return None# 按区域分组计算平均价格region_price = df.groupby(region_field)[price_field].mean().reset_index()# 按价格排序region_price = region_price.sort_values(price_field, ascending=False)return region_pricedef analyze_house_type_price(self, df, house_type_field='house_type', price_field='price'):"""分析户型价格"""if df.empty:return None# 按户型分组计算平均价格house_type_price = df.groupby(house_type_field)[price_field].mean().reset_index()# 按价格排序house_type_price = house_type_price.sort_values(price_field, ascending=False)return house_type_pricedef analyze_area_price(self, df, area_field='area', price_field='price'):"""分析面积价格"""if df.empty:return None# 创建面积区间bins = [0, 50, 70, 90, 110, 130, 150, float('inf')]labels = ['0-50', '50-70', '70-90', '90-110', '110-130', '130-150', '150+']df['area_range'] = pd.cut(df[area_field], bins=bins, labels=labels)# 按面积区间分组计算平均价格area_price = df.groupby('area_range')[price_field].mean().reset_index()return area_pricedef analyze_correlation(self, df, columns=['price', 'area', 'unit_price']):"""分析相关性"""if df.empty:return None# 选择需要分析的列corr_df = df[columns]# 计算相关性矩阵correlation = corr_df.corr()return correlationdef generate_price_trend_chart(self, price_trend, title='房价趋势分析', xlabel='日期', ylabel='平均价格(万元)', output_path=None):"""生成价格趋势图表"""if price_trend is None or price_trend.empty:return Noneplt.figure(figsize=(12, 6))plt.plot(price_trend['date'], price_trend['price'], marker='o')plt.title(title, fontproperties=self.font)plt.xlabel(xlabel, fontproperties=self.font)plt.ylabel(ylabel, fontproperties=self.font)plt.grid(True)plt.xticks(rotation=45)plt.tight_layout()if output_path:plt.savefig(output_path)return pltdef generate_region_price_chart(self, region_price, title='区域房价分析', xlabel='区域', ylabel='平均价格(万元)', output_path=None):"""生成区域价格图表"""if region_price is None or region_price.empty:return Noneplt.figure(figsize=(12, 6))plt.bar(region_price['region'], region_price['price'])plt.title(title, fontproperties=self.font)plt.xlabel(xlabel, fontproperties=self.font)plt.ylabel(ylabel, fontproperties=self.font)plt.grid(True, axis='y')plt.xticks(rotation=45)plt.tight_layout()if output_path:plt.savefig(output_path)return pltdef generate_house_type_price_chart(self, house_type_price, title='户型房价分析', xlabel='户型', ylabel='平均价格(万元)', output_path=None):"""生成户型价格图表"""if house_type_price is None or house_type_price.empty:return Noneplt.figure(figsize=(12, 6))plt.bar(house_type_price['house_type'], house_type_price['price'])plt.title(title, fontproperties=self.font)plt.xlabel(xlabel, fontproperties=self.font)plt.ylabel(ylabel, fontproperties=self.font)plt.grid(True, axis='y')plt.xticks(rotation=45)plt.tight_layout()if output_path:plt.savefig(output_path)return pltdef generate_area_price_chart(self, area_price, title='面积段房价分析', xlabel='面积区间(平方米)', ylabel='平均价格(万元)', output_path=None):"""生成面积价格图表"""if area_price is None or area_price.empty:return Noneplt.figure(figsize=(12, 6))plt.bar(area_price['area_range'], area_price['price'])plt.title(title, fontproperties=self.font)plt.xlabel(xlabel, fontproperties=self.font)plt.ylabel(ylabel, fontproperties=self.font)plt.grid(True, axis='y')plt.tight_layout()if output_path:plt.savefig(output_path)return pltdef generate_correlation_heatmap(self, correlation, title='相关性分析', output_path=None):"""生成相关性热力图"""if correlation is None or correlation.empty:return Noneplt.figure(figsize=(10, 8))sns.heatmap(correlation, annot=True, cmap='coolwarm', fmt='.2f')plt.title(title, fontproperties=self.font)plt.tight_layout()if output_path:plt.savefig(output_path)return pltdef generate_region_price_map(self, region_price, title='区域房价分布图', output_path=None):"""生成区域价格地图"""# 这里简化处理,实际应使用地图库如folium、pyecharts等# 生成区域房价分布图的代码passdef generate_analysis_report(self, user_id=None, title='房价分析报告', start_date=None, end_date=None, region=None, min_price=None, max_price=None, min_area=None, max_area=None, house_type=None):"""生成分析报告"""# 获取房源数据df = self.get_house_data(start_date, end_date, region, min_price, max_price, min_area, max_area, house_type)if df.empty:return None, "没有找到符合条件的数据"# 分析数据price_trend = self.analyze_price_trend(df)region_price = self.analyze_region_price(df)house_type_price = self.analyze_house_type_price(df)area_price = self.analyze_area_price(df)correlation = self.analyze_correlation(df)# 生成图表report_dir = f"src/main/static/reports/{int(time.time())}"file_utils.create_dir(report_dir)price_trend_chart = self.generate_price_trend_chart(price_trend, output_path=f"{report_dir}/price_trend.png")region_price_chart = self.generate_region_price_chart(region_price, output_path=f"{report_dir}/region_price.png")house_type_price_chart = self.generate_house_type_price_chart(house_type_price, output_path=f"{report_dir}/house_type_price.png")area_price_chart = self.generate_area_price_chart(area_price, output_path=f"{report_dir}/area_price.png")correlation_heatmap = self.generate_correlation_heatmap(correlation, output_path=f"{report_dir}/correlation.png")# 关闭图表plt.close('all')# 生成报告内容report_content = {'title': title,'start_date': start_date.strftime('%Y-%m-%d') if start_date else '无','end_date': end_date.strftime('%Y-%m-%d') if end_date else '无','region': region if region else '全部','house_count': len(df),'avg_price': df['price'].mean(),'max_price': df['price'].max(),'min_price': df['price'].min(),'avg_area': df['area'].mean(),'max_area': df['area'].max(),'min_area': df['area'].min(),'price_trend': price_trend.to_dict('records'),'region_price': region_price.to_dict('records'),'house_type_price': house_type_price.to_dict('records'),'area_price': area_price.to_dict('records'),'correlation': correlation.to_dict(),'charts': {'price_trend': f"reports/{int(time.time())}/price_trend.png",'region_price': f"reports/{int(time.time())}/region_price.png",'house_type_price': f"reports/{int(time.time())}/house_type_price.png",'area_price': f"reports/{int(time.time())}/area_price.png",'correlation': f"reports/{int(time.time())}/correlation.png"}}# 保存报告report = AnalysisReport()report.user_id = user_idreport.title = titlereport.type = "house_analysis"report.content = json.dumps(report_content)report.start_date = start_datereport.end_date = end_datereport.region = regionreport.create_time = datetime.now()report.update_time = datetime.now()db_utils.db.session.add(report)db_utils.db.session.commit()return report, "报告生成成功"

API 接口实现

python

运行

from flask import Blueprint, request, jsonify, make_response
from flask_jwt_extended import jwt_required, get_jwt_identity
from house_analysis.models import HouseInfo, CommunityInfo, UserInfo, UserFavorite, AnalysisReport
from house_analysis.services import house_service, community_service, user_service, analysis_service, report_service
from house_analysis.utils import db_utils, date_utils, security_utils, file_utils
import json
import pandas as pd
from datetime import datetime, timedeltaapi = Blueprint('house_api', __name__, url_prefix='/api/v1')@api.route('/houses', methods=['GET'])
def get_houses():"""获取房源列表"""# 获取查询参数page = int(request.args.get('page', 1))page_size = int(request.args.get('page_size', 20))region = request.args.get('region', None)min_price = request.args.get('min_price', None)max_price = request.args.get('max_price', None)min_area = request.args.get('min_area', None)max_area = request.args.get('max_area', None)house_type = request.args.get('house_type', None)sort_by = request.args.get('sort_by', 'price')sort_order = request.args.get('sort_order', 'asc')# 转换参数类型if min_price:min_price = float(min_price)if max_price:max_price = float(max_price)if min_area:min_area = float(min_area)if max_area:max_area = float(max_area)# 获取房源列表houses, total = house_service.get_houses(page, page_size, region, min_price, max_price, min_area, max_area, house_type, sort_by, sort_order)# 转换为字典列表house_list = []for house in houses:house_dict = {'id': house.id,'house_code': house.house_code,'title': house.title,'price': house.price,'unit_price': house.unit_price,'area': house.area,'house_type': house.house_type,'floor': house.floor,'build_year': house.build_year,'orientation': house.orientation,'decoration': house.decoration,'community': house.community,'region': house.region,'subway': house.subway,'address': house.address,'longitude': house.longitude,'latitude': house.latitude,'house_url': house.house_url,'publish_time': house.publish_time.strftime('%Y-%m-%d') if house.publish_time else None,'crawl_time': house.crawl_time.strftime('%Y-%m-%d %H:%M:%S'),'source': house.source}house_list.append(house_dict)# 返回结果return jsonify({'code': 200,'message': 'success','data': {'houses': house_list,'total': total,'page': page,'page_size': page_size}})@api.route('/houses/<house_id>', methods=['GET'])
def get_house_detail(house_id):"""获取房源详情"""# 获取房源信息house = house_service.get_house_by_id(house_id)if not house:return jsonify({'code': 404,'message': '房源不存在'}), 404# 获取小区信息community = community_service.get_community_by_name(house.community)# 转换为字典house_dict = {'id': house.id,'house_code': house.house_code,'title': house.title,'price': house.price,'unit_price': house.unit_price,'area': house.area,'house_type': house.house_type,'floor': house.floor,'build_year': house.build_year,'orientation': house.orientation,'decoration': house.decoration,'community': house.community,'region': house.region,'subway': house.subway,'address': house.address,'longitude': house.longitude,'latitude': house.latitude,'house_url': house.house_url,'publish_time': house.publish_time.strftime('%Y-%m-%d') if house.publish_time else None,'crawl_time': house.crawl_time.strftime('%Y-%m-%d %H:%M:%S'),'source': house.source}# 添加小区信息if community:house_dict['community_info'] = {'id': community.id,'name': community.name,'region': community.region,'address': community.address,'build_year': community.build_year,'developer': community.developer,'property_company': community.property_company,'property_fee': community.property_fee,'building_count': community.building_count,'house_count': community.house_count,'avg_price': community.avg_price,'crawl_time': community.crawl_time.strftime('%Y-%m-%d %H:%M:%S')}# 返回结果return jsonify({'code': 200,'message': 'success','data': house_dict})@api.route('/communities', methods=['GET'])
def get_communities():"""获取小区列表"""# 获取查询参数page = int(request.args.get('page', 1))page_size = int(request.args.get('page_size', 20))region = request.args.get('region', None)min_price = request.args.get('min_price', None)max_price = request.args.get('max_price', None)build_year = request.args.get('build_year', None)sort_by = request.args.get('sort_by', 'avg_price')sort_order = request.args.get('sort_order', 'desc')# 转换参数类型if min_price:min_price = float(min_price)if max_price:max_price = float(max_price)# 获取小区列表communities, total = community_service.get_communities(page, page_size, region, min_price, max_price, build_year, sort_by, sort_order)# 转换为字典列表community_list = []for community in communities:community_dict = {'id': community.id,'name': community.name,'region': community.region,'address': community.address,'build_year': community.build_year,'developer': community.developer,'property_company': community.property_company,'property_fee': community.property_fee,'building_count': community.building_count,'house_count': community.house_count,'longitude': community.longitude,'latitude': community.latitude,'avg_price': community.avg_price,'crawl_time': community.crawl_time.strftime('%Y-%m-%d %H:%M:%S'),'source': community.source}community_list.append(community_dict)# 返回结果return jsonify({'code': 200,'message': 'success','data': {'communities': community_list,'total': total,'page': page,'page_size': page_size}})@api.route('/regions', methods=['GET'])
def get_regions():"""获取区域列表"""# 获取区域列表regions = house_service.get_regions()# 返回结果return jsonify({'code': 200,'message': 'success','data': regions})@api.route('/house_types', methods=['GET'])
def get_house_types():"""获取户型列表"""# 获取户型列表house_types = house_service.get_house_types()# 返回结果return jsonify({'code': 200,'message': 'success','data': house_types})@api.route('/

    博主介绍:硕士研究生,专注于信息化技术领域开发与管理,会使用java、标准c/c++等开发语言,以及毕业项目实战✌

       从事基于java BS架构、CS架构、c/c++ 编程工作近16年,拥有近12年的管理工作经验,拥有较丰富的技术架构思想、较扎实的技术功底和资深的项目管理经验。

       先后担任过技术总监、部门经理、项目经理、开发组长、java高级工程师及c++工程师等职位,在工业互联网、国家标识解析体系、物联网、分布式集群架构、大数据通道处理、接口开发、远程教育、办公OA、财务软件(工资、记账、决策、分析、报表统计等方面)、企业内部管理软件(ERP、CRM等)、arggis地图等信息化建设领域有较丰富的实战工作经验;拥有BS分布式架构集群、数据库负载集群架构、大数据存储集群架构,以及高并发分布式集群架构的设计、开发和部署实战经验;拥有大并发访问、大数据存储、即时消息等瓶颈解决方案和实战经验。

       拥有产品研发和发明专利申请相关工作经验,完成发明专利构思、设计、编写、申请等工作,并获得发明专利1枚。

-----------------------------------------------------------------------------------

      大家在毕设选题、项目升级、论文写作,就业毕业等相关问题都可以给我留言咨询,非常乐意帮助更多的人或加w 908925859。

相关博客地址:

csdn专业技术博客:https://blog.csdn.net/mr_lili_1986?type=blog

Iteye博客:        https://www.iteye.com/blog/user/mr-lili-1986-163-com

门户:http://www.petsqi.cn

七、其他案例: 

 

  

 

相关文章:

  • Java 锁升级机制详解
  • Linux操作系统——批量装机
  • 好用的批量处理软件,免费使用!
  • electron在单例中实现双击打开文件,并重复打开其他文件
  • windows录频软件
  • 自己的服务器被 DDOS跟CC攻击了怎么处理,如何抵御攻击?
  • golang使用tail追踪文件变更
  • 目标检测标注格式
  • EFK架构日志采集系统
  • 国产智能体“双子星”:实在Agent vs Manus(核心架构与技术实现路径对比)
  • Screenpresso v2.1:轻量截图录屏工具安装使用指南
  • liquibase 集成 pt-online-schema-change
  • [Latex排版] 解决Something‘s wrong--perhaps a missing \item. 问题
  • Profinet转Modbus网关:破解热处理炉协议壁垒的温控通讯密码
  • 三网手机号实名认证功能-手机号实名认证接口-手机号身份核验
  • 叶片开关结构与工作原理
  • Git Switch 与 Git Restore 详解
  • kafka版本升级3.5.1-->3.9.1(集群或单体步骤一致)
  • 学习STC51单片机37(芯片为STC89C52RCRC)智能小车4(循迹小车、优化循迹小车解决转弯不平滑)
  • 基于深度学习的智能视频分析系统:技术与实践
  • 有趣网站之家/uc搜索引擎入口
  • 陕西住房和建设部网站首页/培训网站建设
  • 深圳产品型网站建设/微信crm系统
  • 网站后台无法审核/线上推广网络公司
  • 建设网站需要什么资质吗/微信seo排名优化软件
  • 一流的常州做网站/无锡seo公司哪家好