{"id":22903826,"url":"https://github.com/betacatpro/houseanalysisweb","last_synced_at":"2025-05-08T17:05:06.840Z","repository":{"id":54591724,"uuid":"243417766","full_name":"BetaCatPro/HouseAnalysisWeb","owner":"BetaCatPro","description":"基于joint-spider爬虫数据的Web端数据可视化平台","archived":false,"fork":false,"pushed_at":"2021-02-08T20:41:53.000Z","size":15283,"stargazers_count":21,"open_issues_count":2,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-08T17:04:05.322Z","etag":null,"topics":["django-rest-framework","echarts","element-ui","vue"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BetaCatPro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-02-27T03:02:09.000Z","updated_at":"2024-10-28T00:23:01.000Z","dependencies_parsed_at":"2022-08-13T20:40:37.983Z","dependency_job_id":null,"html_url":"https://github.com/BetaCatPro/HouseAnalysisWeb","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaCatPro%2FHouseAnalysisWeb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaCatPro%2FHouseAnalysisWeb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaCatPro%2FHouseAnalysisWeb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BetaCatPro%2FHouseAnalysisWeb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BetaCatPro","download_url":"https://codeload.github.com/BetaCatPro/HouseAnalysisWeb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253112072,"owners_count":21856070,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django-rest-framework","echarts","element-ui","vue"],"created_at":"2024-12-14T02:38:44.147Z","updated_at":"2025-05-08T17:05:06.763Z","avatar_url":"https://github.com/BetaCatPro.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# HouseAnalysisWeb\n基于joint-spider爬虫数据的Web端数据可视化平台，[在线访问][1]\n\n数据源来自项目 [Joint-spider][2] \n\n后端: django-restframework\n\n前端: Vue\n\n[1]: http://120.25.197.182\n[2]: https://github.com/BetaCatPro/Joint-spiders\n\n### 应用技术\n\n- Python 后端\n  - django-restframework\n  - redis\n  - mysql\n- Web 前端\n  - Vue 全家桶\n  - echarts\n  - element-ui\n- 百度地图\n  - Web服务API\n  - vue-baidu-map\n\n### 环境配置及项目启动\n\n```shell\n# 环境配置需要 python3.7 redis mysql vue vue-cli3.x\n# \n# 项目启动\n# 1. 后端服务启动,进入项目\\HouseAnalysis\\\n \t python manage.py runserver\n# 2. 前端项目启动,进入项目目录\\HouseAnalysisWeb\\\n\t npm run dev\n# 3. 前端项目打包\n\t npm run build-prod\n# 4. 访问地址:http://localhost:9527/\n# 5. 后端接口地址: http://localhost:8000/\n```\n\n#### 项目持续改进中......写的实在太烂了QAQ\n\n### 注意事项\n\n- mysql及redis服务配置均在\\HouseAnalysis\\HouseAnalysis\\settings.py文件中进行配置\n- utils文件夹下文件功能:\n  - 插入数据分析结果信息至MySQL:Insert.py\n  - 插入基本总信息至Redis:insertRedis.py\n  - 插入区划及小区信息至MySQL:com_pro.py\n\n##### 前端页面展示\n\n1. 房源信息页面\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160702363.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n\n2. 房价分析-数据探索分析可视化页面\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160715834.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n\n3. 房价分析-各区划详细信息页面\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160738530.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n\n4. 区划小区详情\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160921147.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n\n5. 小区房源列表详情\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160910311.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n\n6. 搜索房源页面\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160855963.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n\n7. 具体房源详情页面\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160843567.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n\n\n8. 房屋周边\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160814163.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n\n9. 房源热力信息图页面\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426160727195.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70)\n## 项目介绍:\n\n### 一. 系统功能架构\n\n1. **后端**\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426162910669.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70#pic_center)\n\n\n2. **前端**、\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200426162921805.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70#pic_center)\n\n\n### 二. 系统实现\n\n#### 1) 后端部分\n\n```shell\n# 项目依赖\nDjango==2.1.5\ndjango-cors-headers==2.4.0\ndjango-crispy-forms==1.7.2\ndjango-filter==2.0.0\ndjango-formtools==2.1\ndjango-guardian==1.4.9\ndjango-import-export==1.1.0\ndjango-redis==4.10.0\ndjango-reversion==3.0.2\ndjangorestframework==3.9.0\ndjangorestframework-jwt==1.11.0\ndrf-extensions==0.4.0\nPyMySQL==0.9.3\nredis==3.0.1\nSQLAlchemy==1.3.2\n```\n\n**基本流程**\n\n- Django生命周期:\n\n  前端发送请求--\u003eDjango的wsgi--\u003e中间件--\u003e路由系统--\u003e视图--\u003eORM数据库操作--\u003e模板--\u003e返回数据给用户\n\n- rest framework生命周期：\n\n  前端发送请求--\u003eDjango的wsgi--\u003e中间件--\u003e路由系统_执行CBV的as_view()，就是执行内部的dispath方法--\u003e在执行dispath之前，有版本分析 和 渲染器--\u003e在dispath内，对request封装--\u003e版本--\u003e认证--\u003e权限--\u003e限流--\u003e视图--\u003e如果视图用到缓存( request.data or  request.query_params )就用到了 解析器--\u003e视图处理数据，用到了序列化(对数据进行序列化或验证) --\u003e视图返回数据可以用到分页\n\n项目文件树：\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200430224556947.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70#pic_center)\n\n\n1. 基本接口框架搭建\n\n```shell\n# 1. 初始化项目\ndjango-admin startproject HouseAnalysis\n# 2. 创建相关应用\ncd HouseAnalysis\ndjango-admin startapp house\n```\n\n2. 配置xadmin后台可视化管理系统\n\n```shell\n# 项目根目录下创建extra_app 文件夹，存放xadmin压缩文件，运行\npip3 install xadmin\n```\n\n3. 配置基本项目信息\n\n```shell\n# 1. 配置settings.py 文件 位于\\\\HouseAnalysis\\HouseAnalysis\\settings.py\n# 找到INSTALLED_APPS,加入配置项\n\t'corsheaders',\n \t'xadmin',\n    'crispy_forms',\n    'rest_framework',\n    'django_filters',\n    'house'\n# 上述配置向是用于将app注册到项目中\n# 2. 增加跨域支持\nCORS_ALLOW_CREDENTIALS = True\nCORS_ORIGIN_ALLOW_ALL = True\nCORS_ORIGIN_WHITELIST = (\n    '*'\n)\nCORS_ALLOW_METHODS = (\n    'DELETE',\n    'GET',\n    'OPTIONS',\n    'PATCH',\n    'POST',\n    'PUT',\n    'VIEW',\n)\nCORS_ALLOW_HEADERS = (\n    'XMLHttpRequest',\n    'X_FILENAME',\n    'accept-encoding',\n    'authorization',\n    'content-type',\n    'dnt',\n    'origin',\n    'user-agent',\n    'x-csrftoken',\n    'x-requested-with',\n    'Pragma',\n)\n# 3. 在DATABASES中配置MySQL数据库信息\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.mysql',\n        'NAME': 'house',\n        'USER': 'root',\n        'PASSWORD': '123456',\n        'HOST': '127.0.0.1',\n        'PORT': '3306',\n        'OPTIONS':{\n            'init_command':\"SET sql_mode='STRICT_TRANS_TABLES'\"\n        },\n    }\n}\n# 4. 配置drf缓存\nREST_FRAMEWORK_EXTENSIONS = {\n    #10s后失效\n    'DEFAULT_CACHE_RESPONSE_TIMEOUT': 10\n}\n# 5. 配置redis缓存\nCACHES = {\n    \"default\": {\n        \"BACKEND\": \"django_redis.cache.RedisCache\",\n        \"LOCATION\": \"redis://127.0.0.1:6379/3\",\n        \"OPTIONS\": {\n            \"CLIENT_CLASS\": \"django_redis.client.DefaultClient\",\n        }\n    }\n}\n```\n\n*注：本项目为接口开发不涉及template模板及static静态资源，所以不用对这两项进行配置*\n\n2. 配置接口文档\n\n进入\\\\HouseAnalysis\\HouseAnalysis\\urls.py，配置路由信息：\n\n添加`path('xadmin/', xadmin.site.urls)`,\n    `re_path(r'docs/', include_docs_urls(title=\"HA\"))`,到`urlpatterns`列表,xadmin为管理页面路由，\n\ndocs为接口文档路由，通过http://localhost:8000/docs/可进行访问，如下图：\n\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200430224619888.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2J5NjY3MTcxNQ==,size_16,color_FFFFFF,t_70#pic_center)\n\n\n3. 数据model设计\n\nDjango Model设计是Django五项基础核心设计之一（Model设计，URL配置，View编写，Template设计，From使用），也是MVC(MVT)模式中重要的环节。\n\nModel是数据模型，并不是数据库，它描述了数据的构成和他们之间的逻辑关系，在Django中，Model设计实质上就是一个类。每一个模型都映射一张数据库表。\n\n- 每个模型都是一个 Python 的类，这些类继承 django.db.models.Model；\n- 模型类的每个属性都相当于一个数据库的字段；\n- 利用这些，Django 提供了一个自动生成访问数据库的 API；\n\n进入\\HouseAnalysis\\house\\models.py ,添加`Api，Elevator，Floor，Layout，Region，Decortion，Purposes，Orientation，Constructure，Community，CommunityRange`模型,用以提供数据进行序列化。\n\nmodel创建完成后进行数据迁移：\n\n```shell\n# 进入项目根目录\npython manage.py makemigrations\n# 同步至数据库\npython manage.py migrate\n```\n\n4. 序列化器设计\n\n在house文件夹下新建文件serializer.py 文件，用来进行数据序列化。\n\n定义序列化器（本质就是一个类），一般包括模型类的字段，有自己的字段类型规则。实现了序列化器后，就可以创建序列化对象以及查询集进行序列化操作，通过序列化对象.data来获取数据（不用自己构造字典，再返回Json数据）\n\n- 用于序列化时，将模型类对象传入**instance**参数\n\n- 用于反序列化时，将要被反序列化的数据传入**data**参数\n\n- 除了instance和data参数外，在构造Serializer对象时，还可通过**context**参数额外添加数据\n\n这里我们使用模型序列化器(ModelSerializer)，ModelSerializer与常规的Serializer相同，但提供了：\n\n- 基于模型类自动生成一系列字段\n- 基于模型类自动为Serializer生成validators，比如unique_together\n- 包含默认的create()和update()的实现\n\n例如：利用简单代码就可序列化house内容\n\n```python\nclass HouseSerializer(serializers.ModelSerializer):\n\n    class Meta:\n        # 需要序列化的model\n        model = Api\n        # 需要序列化的字段\n        fields = \"__all__\"\n```\n\n同理，将剩余的model序列化，包括`ElevatorSerializer，FloorSerializer，LayoutSerializer，RegionSerializer，RegionS，CommunitySerializer，CommunityRangeSerializer，DecortionSerializer，PurposesSerializer，ConstructureSerializer，OrientationSerializer`，其中，`RegionS`序列化器作为`CommunitySerializer`和`CommunityRangeSerializer`的上级序列化器。\n\n5. 视图View：\n\n视图的功能：说白了就是接收前端请求，进行数据处理\n\n（这里的处理包括：如果前端是GET请求，则构造查询集，将结果返回，这个过程为**序列化**；如果前端是POST请求，假如要对数据库进行改动，则需要拿到前端发来的数据，进行校验，将数据写入数据库，这个过程称为**反序列化**）\n\n最原始的视图可以实现这样的逻辑处理，但是针对不同的请求，需要在类视图中定义多个方法实现各自的处理，这样是可以解决问题，但是存在一个缺陷，那就是每个函数中一般的逻辑都差不多：读请求，从数据库拿数据，写东西到数据库，返回结果给前端。这样就会产生大量的重复代码。\n\n在开发REST API的视图中，虽然每个视图具体操作的数据不同，但增、删、改、查的实现流程基本套路化，所以这部分代码也是可以复用简化编写的：\n\n**增**：校验请求数据 -\u003e 执行反序列化过程 -\u003e 保存数据库 -\u003e 将保存的对象序列化并返回\n\n**删**：判断要删除的数据是否存在 -\u003e 执行数据库删除\n\n**改**：判断要修改的数据是否存在 -\u003e 校验请求的数据 -\u003e 执行反序列化过程 -\u003e 保存数据库 -\u003e 将保存的对象序列化并返回\n\n**查**：查询数据库 -\u003e 将数据序列化并返回\n\n视图编写在views.py 文件中，这里我们使用类识图的方式进行开发。\n\n- 对于GET请求，我们需要返回一个列表，例如对于请求为：`http://127.0.0.1/api/v1/allhouse`，我们需要将所有房源信息的序列化结果返回暴露出去。\n- 对于GET/:ID请求，我们需要返回一个对用结果，例如对于请求为：`http://127.0.0.1/api/v1/allhouse`/1，我们需要将房源id(此时的id可配置为指定需求参数)为1的序列化结果返回暴露出去。\n- 对于POST请求，我们需要新建一条信息，例如对于post请求参数为：`{id:xxx,values:xxx...}`，我们需要当前信息做检验，合法后将当前数据添加到数据库中。\n- 对于PATCH请求：更新某个指定房源的信息。\n- 对于DELETE请求：删除某个房源信息。\n\n例：\n\n```python\n# CacheResponseMixin: 缓存，内存级别\n# ListModelMixin处理GET（all）请求\n# RetrieveModelMixin处理（id）请求\n# 基础类GenericViewSet，提供分页，过滤，排序等功能\nclass HouseListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):\n    queryset = Api.objects.all()\n    # 房源序列化\n    serializer_class = HouseSerializer\n    # 房源分页\n    pagination_class = HousePagination\n    # 自定义排序，过滤类\n    filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)\n    # 过滤信息字段\n    filter_fields = ('price',)\n    # 搜搜字段\n    search_fields = ('title', 'region', 'community_name')\n    # 排序字段\n    ordering_fields = ('price', 'unit_price','construction_area')\n\t\n    # 重写retrieve方法，实现获取单个序列化对象信息\n    def retrieve(self, request, *args, **kwargs):\n        instance = self.get_object()\n        serializer = self.get_serializer(instance)\n        return Response(serializer.data)\n\n# 分页配置项\nclass HousePagination(PageNumberPagination):\n    page_size = 30\n    page_size_query_param = 'page_size'\n    page_query_param = \"page\"\n    max_page_size = 100\n```\n\n其他视图类似，包括：`ElevaorViewSet，FloorViewSet，LayoutViewSet，RegionViewSet，CommunityViewSet，CommunityPagination，AllCommunityViewSet，CommunityRangeViewSet，DecortionViewSet，PurposesViewSet，ConstructureViewSet，OrientationViewSet`\n\n其中对于`CommunityViewSet`类，我们需要根据具体的community_id来返回其所有信息，这时就不能用`RetrieveModelMixin`类的`retrieve`方法了，需要重写`get_queryset`方法，返回特定的model。\n\n```python\n    def get_queryset(self):\n        if self.request.GET['rid'] == '0' :\n            # 获取所有\n            return  Community.objects.all()\n        else:\n            return Community.objects.filter(region=self.request.GET['rid'])\n```\n\n*其他参数*\n\n- ?limit=10：指定返回记录的数量\n- ?offset=10：指定返回记录的开始位置。\n- ?page=2\u0026per_page=100：指定第几页，以及每页的记录数。\n- ?sort=name\u0026order=asc：指定返回结果按照哪个属性排序，以及排序顺序。\n- ?animal_type_id=1：指定筛选条件\n\n\n\n6. 路由配置\n\n使用`rest_framework`的`DefaultRouter`进行路由注册：\n\n```python\nfrom rest_framework.routers import DefaultRouter\nrouter = DefaultRouter()\nrouter.register(r'v1/api/all_house', HouseListViewSet, base_name=\"house\")\n......\n```\n\n将router加入到urlpatterns中：\n\n```python\npath('', include(router.urls)),\n```\n\n这时当有请求访问到存在的路由后，路由会将请求转发到视图类，在视图类中将请求处理后，返回序列化结果，即后端暴露的接口数据。\n\n7. 关于接口数据\n\n[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JmH9EULX-1588257851195)(imgs/接口数据.png)]\n\n**数据描述**\n\n- count : 总数据量\n- next： 下一页url\n- previous： 上一页url\n- results: 结果集\n\nHTTP状态码：\n\n- 200 OK - [GET]：服务器成功返回用户请求的数据，该操作是幂等的（Idempotent）。\n- 201 CREATED - [POST/PUT/PATCH]：用户新建或修改数据成功。\n- 202 Accepted - [*]：表示一个请求已经进入后台排队（异步任务）\n- 204 NO CONTENT - [DELETE]：用户删除数据成功。\n- 400 INVALID REQUEST - [POST/PUT/PATCH]：用户发出的请求有错误，服务器没有进行新建或修改数据的操作，该操作是幂等的。\n- 401 Unauthorized - [*]：表示用户没有权限（令牌、用户名、密码错误）。\n- 403 Forbidden - [*] 表示用户得到授权（与401错误相对），但是访问是被禁止的。\n- 404 NOT FOUND - [*]：用户发出的请求针对的是不存在的记录，服务器没有进行操作，该操作是幂等的。\n- 406 Not Acceptable - [GET]：用户请求的格式不可得（比如用户请求JSON格式，但是只有XML格式）。\n- 410 Gone -[GET]：用户请求的资源被永久删除，且不会再得到的。\n- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时，发生一个验证错误。\n- 500 INTERNAL SERVER ERROR - [*]：服务器发生错误，用户将无法判断发出的请求是否成功。\n\n8. 关于具体数据处理-为了更方便序列化\n\n- 将总体信息数据存放到Redis数据库中，减少后端处理请求是进行大量数据操作是运算量\n\n```python\n# 将所有房源数，小区数，总体单价价均价，总体总价均价，存入redis\ndef insertIndexInfo():\n    all_number = house.count()\n    com_number = Api.objects.values('community_name').distinct().count()\n    mean_price = format(house.aggregate(Avg('price'))['price__avg'],'.3f')\n    mean_unit_price = format(house.aggregate(Avg('unit_price'))['unit_price__avg'],'.3f')\n    data = {\n        \"all_number\": all_number,\n        \"com_number\": com_number,\n        \"mean_price\": mean_price,\n        \"mean_unit_price\": mean_unit_price\n    }\n\n    redis_conn.mset(data)\n```\n\n- 将处理好的数据存入MySQL，包括(电梯，装修情况等信息)\n\n```python\n# 1. 定义专门数据库操作类InsertIntoMysql\nclass InsertIntoMysql():\n\n    # 属性中定义sql语句获取数据\n    def __init__(self):\n        self.elevator_sql = 'select unit_price, price, elevator from house_api'\n        # ......\n\t\n    # 定义数据插入方法\n    def insertElevator(self):\n        index, values, price_mean, unit_price_mean = multiple(self.elevator_sql, engine, 'elevator')\n        for i in range(len(index)):\n            elevator = Elevator()\n            elevator.version = 'v1'\n            elevator.title = '电梯分布情况'\n            elevator.has_ele = index[i]\n            elevator.el_num = values[i]\n            elevator.mean_price = price_mean[i]\n            elevator.mean_unit_price = unit_price_mean[i]\n            elevator.save()\n     # 其他剩余数据插入方法\n     # ......\n# 2. 数据处理模块，包括单因子和对因子处理\n#\t 利用数据处理模块pandas\ndef single(sql,engine,feature):\n    \"\"\"\n    :param sql: sql语句\n    :param engine: mysql引擎\n    :param feature: 数据特征\n    :return: 特征，数量\n    \"\"\"\n    df_sql = sql\n    df = pd.read_sql(df_sql, engine)\n    df[feature] = df[feature].astype(str)\n    if feature=='floor':\n        df[df[feature]=='暂无数据'] = '18'\n        df[feature] = df[feature].apply(lambda x: re.findall('\\d+',x)[0])\n    feature_res = df[feature].value_counts()\n    index = feature_res.index\n    values = feature_res.values\n    return index,values\n\ndef multiple(sql,engine,feature):\n    \"\"\"\n    :param sql: sql语句\n    :param engine: mysql引擎\n    :param feature: 数据特征\n    :return: 特征，数量，总价均价，单价均价\n    \"\"\"\n    df_sql = sql\n    df = pd.read_sql(df_sql, engine)\n    df[feature] = df[feature].astype(str)\n    if feature == 'region':\n        df[feature] = df[feature].apply(lambda x:re.sub(r\"\\[|\\]|'\",'',x).split(',')[0])\n    feature_res = df[feature].value_counts()\n    index = feature_res.index\n    values = feature_res.values\n    price_mean = []\n    unit_price_mean = []\n    max_unit_price = []\n    min_unit_price = []\n    for inx,val in zip(index,values):\n        price_mean.append(format(df[df[feature]==inx]['price'].astype(float).mean(),'.3f'))\n        unit_price_mean.append(format(df[df[feature]==inx]['unit_price'].astype(float).mean(),'.3f'))\n        max_unit_price.append(format(df[df[feature]==inx]['unit_price'].astype(float).max(),'.3f'))\n        min_unit_price.append(format(df[df[feature]==inx]['unit_price'].astype(float).min(),'.3f'))\n    return index, values, price_mean, unit_price_mean, max_unit_price, min_unit_price\n```\n\n#### 2) 前端部分\n\n​\t前端使用基本框架Vue搭建，基于vue-admin-template开发。前端主要业务用于图表展示，地图信息，主要用于做成都二手房房价数据及相关信息可视化，包括总体信息展示，各项特征-如房屋装修情况等的图表信息可视化，房源搜索，基本房源信息可视化，地图展示等功能。\n\n**项目说明**\n\n- 关于地图模块，这里我们采用的是百度地图，使用之前，需要申请百度地图ak，用于我们的WebAPI服务\n- 地图库使用开源vue-baidu-map库，并采取按需加载的方式，即在需要用到的页面才进行模块导入。\n- 项目UI使用element-ui组件库，采用全局加载模式。\n- 图表库使用echarts.js，采用按需加载的方式使用模块。\n\n\n\n前端项目文件树：\n\n![在这里插入图片描述](https://img-blog.csdnimg.cn/2020043022464976.png#pic_center)\n\n\n- src目录为项目核心代码文件，所欲自定义工具，接口文件，页面文件都在这里面编写。\n- test目录是项目测试文件，使用jest进行项目测试。\n- public目录为公共资源文件。\n- dist目录为项目开发完成后进行打包后自动生成的文件。\n\n\n\n**具体流程**\n\n1. UI组件的使用\n\n```shell\n# 安装到项目\nnpm install -S element-ui\n\n# 全局加载: 在main.js中\nimport ElementUI from 'element-ui'\nimport 'element-ui/lib/theme-chalk/index.css'\nimport locale from 'element-ui/lib/locale/lang/en' // i18n\nVue.use(ElementUI, { locale })\n\n# 这样我们据可以在vue文件中使用UI组件，如: 按钮组件\u003cButton /\u003e\n```\n\n2. 地图组件使用\n\n```shell\n# 安装到项目\nnpm install -S vue-baidu-map\n\n# 局部加载: 在任意vue文件中\nimport BaiduMap from 'vue-baidu-map/components/map/Map.vue'\n# 地图标注组件\nimport { BmMarker } from 'vue-baidu-map'\n\n# 使用，例如:\n      \u003cbaidu-map  ref=\"baidumap\"\n                  class=\"bm-view\"\n                  style=\"width:100%;height:93vh\"\n                  ak=\"YOUR AK\"\n                  :center=\"{lng: 104.07, lat: 30.67}\"\n                  :scroll-wheel-zoom=\"true\"\n                  :zoom=\"16\"\u003e\n        \u003cbm-navigation anchor=\"BMAP_ANCHOR_TOP_LEFT\"\u003e\u003c/bm-navigation\u003e\n        \u003cbm-marker v-for=\"spoi in searchResults\"--\u003e\n                   \u003c!--:position=\"{lng: spoi.lng, lat: spoi.lat}\"--\u003e\n                   \u003c!--:title=\"spoi.community_name\"\u003e--\u003e\n        \u003c/bm-marker\u003e\n      \u003c/baidu-map\u003e\n```\n\n\n\n3. 定义请求拦截-用于截获错误请求及超时请求等。\n\n在src目录下新建文件夹utils，用于存放我们自定义的工具文件，新建requests.js，重新封装axios方法，满足我们请求接口时的操作。此文件将暴露一个用作异步请求的对象，做为更高级的接口访问方法，在接口文件设计中将使用此方法。\n\n- 初始化\n\n```js\n# 新建一个servie对象\nconst service = axios.create({\n  // 项目基本配置文件中定义的VUE_APP_BASE_API，开发和发布环境各部相同，在.env.devolopment和.env.product文件中进行配置\n  baseURL: process.env.VUE_APP_BASE_API,\n  // 直接写死的url，无论环境如何都不会改变\n  // baseURL: 'http://127.0.0.1:8000/v1/api',\n  timeout: 8000 // 最大请求时间\n})\n```\n\n\n\n- 请求拦截-发送请求时\n\n```js\nservice.interceptors.request.use(config =\u003e {\n  // 请求方法为POST时，配置请求头\n  config.method === 'post'\n    ? config.data = qs.stringify({ ...config.data })\n    : config.params = { ...config.params }\n  config.headers['Content-Type'] = 'application/x-www-form-urlencoded'\n\n  return config\n}, error =\u003e {\n  // 错误时提示信息\n  Message({\n    message: error,\n    type: 'error',\n    duration: 3 * 1000\n  })\n  Promise.reject(error)\n})\n```\n\n- 响应拦截-接收数据时\n\n```js\nservice.interceptors.response.use(\n  response =\u003e {\n    if (response.statusText === 'OK') {\n      // 正确响应时，直接返回数据\n      return response.data\n    } else {\n      Notification({\n        title: '错误',\n        message: '访问api错误', // TODO 后端拦截错误请求\n        type: 'error',\n        duration: 3 * 1000\n      })\n    }\n  },\n  // 错误处理\n  error =\u003e {\n    let dtext = JSON.parse(JSON.stringify(error))\n    try {\n      if(dtext.response.status === 404) {\n        Notification({\n          type: 'error',\n          title: '出问题404',\n          message: '访问api错误或服务器忙',\n          duration: 3 * 1000\n        })\n      }\n    } catch (err) {\n      Notification({\n        title: '错误',\n        message: '请求超时,请稍后再试或刷新页面',\n        type: 'error',\n        duration: 3 * 1000\n      })\n    }\n\n\n    return Promise.reject(error)\n  }\n)\n```\n\n- 最后一定要暴露service对象\n\n```js\nexport default service\n```\n\n4. 前端路由配置\n\n使用vue-router实现路由功能，在新建router.js，进行路由项配置。\n\n```js\n// 定义路由列表\nexport const constantRoutes = [\n  {\n    path: '/404',\n    component: () =\u003e import('@/views/404'),\n    hidden: true\n  },\n  {\n    path: '/',\n    component: Layout,\n    redirect: '/dashboard',\n    children: [{\n      path: 'dashboard',\n      name: 'Dashboard',\n      // 采用路由懒加载的方式\n      component: () =\u003e import('@/views/dashboard/index'),\n      meta: { title: '房源信息', icon: 'house' }\n    }]\n  },\n  // ......\n  ]\n// 构造路由对象\nconst createRouter = () =\u003e new Router({\n  // mode: 'history', // require service support\n  scrollBehavior: () =\u003e ({ y: 0 }),\n  routes: constantRoutes\n})\n\n// 在main.js 中引入并挂载\nimport router from './router'\nnew Vue({\n  el: '#app',\n  router,\n  render: h =\u003e h(App)\n})\n\n```\n\n5. 接口函数编写\n\n接口api.js文件中定义了所有与项目有关的接口访问请求，并封装成函数暴露出去，供组件使用。\n\n```js\n// 例如获取区划信息接口\nexport function getRegionInfo(id,params) {\n  return request({\n    url: '/region/'+id,\n    method: 'get',\n    params\n  })\n}\n```\n\n其中request为上面封装好的接口访问对象，使用前需引入，组件中使用接口的时候，按需导入，例如：\n\n```js\n// charts.js是接口函数文件\nimport { getRegionInfo } from '@/api/charts'\n\n// 使用\n// 第一种方式，由于它本来就是一个promise对象，所以直接在then中获取响应\ngetRegionInfo(1).then((res,err)=\u003e{\n    // 相关操作\n})\n// 第二种方式，使用async await方式,包含await的方法中需要加上async 作为方法前缀\nlet res = await getRegionInfo(1)\n```\n\n6. 项目页面编写\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20200430224712964.png#pic_center)\n\n\n- dashbord为总体信息展示面班，也是页面入口\n- ~~heatmap为热力图页面~~\n- house为房源搜索和基本信息展示页面\n- map为房源地图分布图\n- multiple为区划信息页面\n- single为房源一系列数据可视化图表页\n- ~~word为词云页~~\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetacatpro%2Fhouseanalysisweb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbetacatpro%2Fhouseanalysisweb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbetacatpro%2Fhouseanalysisweb/lists"}