Website-basierte Datenvisualisierungsplattform basierend auf Joint-Spider-Crawler-Daten, Online-Zugriff
Die Datenquelle stammt aus dem Projekt Joint-Spider
Backend: Django-Restframework
Frontend: Vue
# 环境配置需要 python3.7 redis mysql vue vue-cli3.x
#
# 项目启动
# 1. 后端服务启动,进入项目HouseAnalysis
python manage.py runserver
# 2. 前端项目启动,进入项目目录HouseAnalysisWeb
npm run dev
# 3. 前端项目打包
npm run build-prod
# 4. 访问地址:http://localhost:9527/
# 5. 后端接口地址: http://localhost:8000/
Einzelheiten zur Gemeindewohnungsliste
Eigenschaftenseite durchsuchen
Spezifische Eigenschaftsdetailseite
Rund ums Haus
# 项目依赖
Django==2.1.5
django-cors-headers==2.4.0
django-crispy-forms==1.7.2
django-filter==2.0.0
django-formtools==2.1
django-guardian==1.4.9
django-import-export==1.1.0
django-redis==4.10.0
django-reversion==3.0.2
djangorestframework==3.9.0
djangorestframework-jwt==1.11.0
drf-extensions==0.4.0
PyMySQL==0.9.3
redis==3.0.1
SQLAlchemy==1.3.2
Grundlegender Prozess
Django-Lebenszyklus:
Das Front-End sendet eine Anfrage -> Djangos wsgi -> Middleware -> Routing-System -> Ansicht -> ORM-Datenbankbetrieb -> Vorlage -> Rückgabedaten an den Benutzer
Rest-Framework-Lebenszyklus:
Das Front-End sendet eine Anfrage -> Djangos wsgi -> Middleware -> Routing system_Execute CBVs as_view (), das die interne Dispath-Methode ausführen soll -> Vor der Ausführung von Dispath gibt es eine Versionsanalyse und einen Renderer -> In dispath, kapseln Sie die Anfrage -> Version -> Authentifizierung -> Berechtigungen -> Aktuelles begrenzen -> Ansicht -> Wenn die Ansicht Caching verwendet (request.data oder request.query_params ) verwendet den Parser -> die Ansicht verarbeitet die Daten und verwendet Serialisierung (Serialisierung oder Überprüfung der Daten) -> die Ansicht gibt die Daten mithilfe von Paging zurück.
Projektdateibaum:
# 1. 初始化项目
django-admin startproject HouseAnalysis
# 2. 创建相关应用
cd HouseAnalysis
django-admin startapp house
# 项目根目录下创建extra_app 文件夹,存放xadmin压缩文件,运行
pip3 install xadmin
# 1. 配置settings.py 文件 位于\HouseAnalysisHouseAnalysissettings.py
# 找到INSTALLED_APPS,加入配置项
' corsheaders ' ,
' xadmin ' ,
' crispy_forms ' ,
' rest_framework ' ,
' django_filters ' ,
' house '
# 上述配置向是用于将app注册到项目中
# 2. 增加跨域支持
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
' * '
)
CORS_ALLOW_METHODS = (
' DELETE ' ,
' GET ' ,
' OPTIONS ' ,
' PATCH ' ,
' POST ' ,
' PUT ' ,
' VIEW ' ,
)
CORS_ALLOW_HEADERS = (
' XMLHttpRequest ' ,
' X_FILENAME ' ,
' accept-encoding ' ,
' authorization ' ,
' content-type ' ,
' dnt ' ,
' origin ' ,
' user-agent ' ,
' x-csrftoken ' ,
' x-requested-with ' ,
' Pragma ' ,
)
# 3. 在DATABASES中配置MySQL数据库信息
DATABASES = {
' default ' : {
' ENGINE ' : ' django.db.backends.mysql ' ,
' NAME ' : ' house ' ,
' USER ' : ' root ' ,
' PASSWORD ' : ' 123456 ' ,
' HOST ' : ' 127.0.0.1 ' ,
' PORT ' : ' 3306 ' ,
' OPTIONS ' :{
' init_command ' : " SET sql_mode='STRICT_TRANS_TABLES' "
},
}
}
# 4. 配置drf缓存
REST_FRAMEWORK_EXTENSIONS = {
# 10s后失效
' DEFAULT_CACHE_RESPONSE_TIMEOUT ' : 10
}
# 5. 配置redis缓存
CACHES = {
" default " : {
" BACKEND " : " django_redis.cache.RedisCache " ,
" LOCATION " : " redis://127.0.0.1:6379/3 " ,
" OPTIONS " : {
" CLIENT_CLASS " : " django_redis.client.DefaultClient " ,
}
}
}
Hinweis: Bei diesem Projekt handelt es sich um eine Schnittstellenentwicklung, die keine Vorlagenvorlagen und statischen Ressourcen umfasst. Daher besteht keine Notwendigkeit, diese beiden Elemente zu konfigurieren.
Geben Sie HouseAnalysisHouseAnalysisurls.py ein und konfigurieren Sie die Routing-Informationen:
Fügen Sie path('xadmin/', xadmin.site.urls)
, re_path(r'docs/', include_docs_urls(title="HA"))
zur urlpatterns
-Liste hinzu, xadmin ist das Routing der Verwaltungsseite,
Docs ist die Schnittstelle zum Weiterleiten von Dokumenten, auf die über http://localhost:8000/docs/ zugegriffen werden kann, wie unten gezeigt:
Das Django-Modelldesign ist eines der fünf grundlegenden Kerndesigns von Django (Modelldesign, URL-Konfiguration, View Writing, Template Design, From Use) und ist auch ein wichtiges Bindeglied im MVC (MVT)-Modell.
Modell ist ein Datenmodell, keine Datenbank. Es beschreibt die Struktur von Daten und die logische Beziehung zwischen ihnen. In Django ist Modelldesign im Wesentlichen eine Klasse. Jedes Modell wird einer Datenbanktabelle zugeordnet.
Geben Sie HouseAnalysishousemodels.py ein und fügen Sie die Modelle Api,Elevator,Floor,Layout,Region,Decortion,Purposes,Orientation,Constructure,Community,CommunityRange
hinzu, um Daten für die Serialisierung bereitzustellen.
Nachdem das Modell erstellt wurde, führen Sie die Datenmigration durch:
# 进入项目根目录
python manage.py makemigrations
# 同步至数据库
python manage.py migrate
Erstellen Sie eine neue Datei serializer.py im Hausordner, um Daten zu serialisieren.
Definieren Sie einen Serialisierer (im Wesentlichen eine Klasse), der im Allgemeinen Felder der Modellklasse enthält und über eigene Feldtypregeln verfügt. Nach der Implementierung des Serialisierers können Sie serialisierte Objekte und Abfragesätze für Serialisierungsvorgänge erstellen und Daten über serialisierte object.data abrufen (Sie müssen nicht selbst ein Wörterbuch erstellen und dann JSON-Daten zurückgeben).
Übergeben Sie bei Verwendung zur Serialisierung das Modellklassenobjekt an den Instanzparameter
Übergeben Sie bei Verwendung zur Deserialisierung die zu deserialisierenden Daten an den Datenparameter .
Zusätzlich zu den Instanz- und Datenparametern können Sie beim Erstellen des Serializer-Objekts auch zusätzliche Daten über den Kontextparameter hinzufügen.
Hier verwenden wir den Modell-Serializer (ModelSerializer), der mit dem regulären Serializer identisch ist, aber Folgendes bietet:
Beispiel: Hausinhalte können mit einfachem Code serialisiert werden
class HouseSerializer ( serializers . ModelSerializer ):
class Meta :
# 需要序列化的model
model = Api
# 需要序列化的字段
fields = "__all__"
Serialisieren Sie auf die gleiche Weise die übrigen Modelle, einschließlich ElevatorSerializer,FloorSerializer,LayoutSerializer,RegionSerializer,RegionS,CommunitySerializer,CommunityRangeSerializer,DecortionSerializer,PurposesSerializer,ConstructureSerializer,OrientationSerializer
, wobei RegionS
Serializer als überlegener Serialisierer von CommunitySerializer
und CommunityRangeSerializer
dient.
Die Funktion der Ansicht: Vereinfacht ausgedrückt besteht sie darin, Front-End-Anfragen zu empfangen und die Datenverarbeitung durchzuführen.
(Die Verarbeitung hier umfasst: Wenn das Front-End eine GET-Anfrage ist, erstellen Sie einen Abfragesatz und geben Sie die Ergebnisse zurück. Dieser Prozess ist Serialisierung ; wenn das Front-End eine POST-Anfrage ist, wenn Sie Änderungen an der Datenbank vornehmen möchten, Sie müssen die vom Frontend gesendeten Daten abrufen und in die Datenbank schreiben. Dieser Vorgang wird als Deserialisierung bezeichnet.
Die originellste Ansicht kann eine solche logische Verarbeitung implementieren, aber für verschiedene Anforderungen müssen in der Klassenansicht mehrere Methoden definiert werden, um ihre eigene Verarbeitung zu implementieren. Dies kann das Problem lösen, es gibt jedoch einen Fehler, nämlich die allgemeine Methode Die Logik jeder Funktion ist ähnlich: Anforderung lesen, Daten aus der Datenbank abrufen, Dinge in die Datenbank schreiben und die Ergebnisse an das Front-End zurückgeben. Dadurch wird eine Menge doppelter Code erzeugt.
Obwohl in den Ansichten der REST-API-Entwicklung die spezifischen Datenoperationen jeder Ansicht unterschiedlich sind, ist der Implementierungsprozess des Hinzufügens, Löschens, Änderns und Überprüfens im Wesentlichen Routine, sodass dieser Teil des Codes auch wiederverwendet und zum Schreiben vereinfacht werden kann:
Hinzugefügt : Anforderungsdaten überprüfen -> Deserialisierungsprozess ausführen -> Datenbank speichern -> Das gespeicherte Objekt serialisieren und zurückgeben
Löschen : Stellen Sie fest, ob die zu löschenden Daten vorhanden sind -> führen Sie die Datenbanklöschung durch
Änderung : Bestimmen Sie, ob die zu ändernden Daten vorhanden sind -> überprüfen Sie die angeforderten Daten -> führen Sie den Deserialisierungsprozess durch -> speichern Sie die Datenbank -> serialisieren Sie das gespeicherte Objekt und geben Sie es zurück
Abfrage : Datenbank abfragen -> Daten serialisieren und zurückgeben
Ansichten werden in die Datei „views.py“ geschrieben, und hier verwenden wir Klassenerkennungsdiagramme für die Entwicklung.
http://127.0.0.1/api/v1/allhouse
, müssen wir die serialisierten Ergebnisse aller Hausinformationen zurückgeben und offenlegen.http://127.0.0.1/api/v1/allhouse
/1, müssen wir die Haus-ID (die ID unter) ändern Dieses Mal kann es wie folgt konfiguriert werden: Das Serialisierungsergebnis, das den Anforderungsparameter angibt, ist 1, wird zurückgegeben und verfügbar gemacht.{id:xxx,values:xxx...}
, müssen wir die aktuellen Informationen überprüfen und die aktuellen Daten hinzufügen die Datenbank, wenn sie legal ist.Beispiel:
# CacheResponseMixin: 缓存,内存级别
# ListModelMixin处理GET(all)请求
# RetrieveModelMixin处理(id)请求
# 基础类GenericViewSet,提供分页,过滤,排序等功能
class HouseListViewSet ( CacheResponseMixin , mixins . ListModelMixin , mixins . RetrieveModelMixin , GenericViewSet ):
queryset = Api . objects . all ()
# 房源序列化
serializer_class = HouseSerializer
# 房源分页
pagination_class = HousePagination
# 自定义排序,过滤类
filter_backends = ( DjangoFilterBackend , filters . SearchFilter , filters . OrderingFilter )
# 过滤信息字段
filter_fields = ( 'price' ,)
# 搜搜字段
search_fields = ( 'title' , 'region' , 'community_name' )
# 排序字段
ordering_fields = ( 'price' , 'unit_price' , 'construction_area' )
# 重写retrieve方法,实现获取单个序列化对象信息
def retrieve ( self , request , * args , ** kwargs ):
instance = self . get_object ()
serializer = self . get_serializer ( instance )
return Response ( serializer . data )
# 分页配置项
class HousePagination ( PageNumberPagination ):
page_size = 30
page_size_query_param = 'page_size'
page_query_param = "page"
max_page_size = 100
Andere Ansichten sind ähnlich, einschließlich: ElevaorViewSet,FloorViewSet,LayoutViewSet,RegionViewSet,CommunityViewSet,CommunityPagination,AllCommunityViewSet,CommunityRangeViewSet,DecortionViewSet,PurposesViewSet,ConstructureViewSet,OrientationViewSet
Für die CommunityViewSet
-Klasse müssen wir alle ihre Informationen basierend auf der spezifischen community_id zurückgeben. Derzeit können wir die RetrieveModelMixin
-Klasse nicht verwenden. Wir müssen die retrieve
get_queryset
Methode überschreiben, um ein bestimmtes Modell zurückzugeben.
def get_queryset ( self ):
if self . request . GET [ 'rid' ] == '0' :
# 获取所有
return Community . objects . all ()
else :
return Community . objects . filter ( region = self . request . GET [ 'rid' ])
Andere Parameter
Verwenden Sie DefaultRouter
von rest_framework
für die Routenregistrierung:
from rest_framework . routers import DefaultRouter
router = DefaultRouter ()
router . register ( r'v1/api/all_house' , HouseListViewSet , base_name = "house" )
......
Router zu URL-Mustern hinzufügen:
path ( '' , include ( router . urls )),
Wenn zu diesem Zeitpunkt eine Anforderung auf die vorhandene Route zugreift, leitet die Route die Anforderung an die Ansichtsklasse weiter. Nachdem die Anforderung in der Ansichtsklasse verarbeitet wurde, wird das serialisierte Ergebnis zurückgegeben, bei dem es sich um die vom Backend bereitgestellten Schnittstellendaten handelt.
[Die Übertragung des externen Linkbildes ist möglicherweise über einen Anti-Leeching-Mechanismus verfügt. Es wird empfohlen, das Bild zu speichern und direkt hochzuladen (imgs/interface data.png)]
Datenbeschreibung
HTTP-Statuscode:
# 将所有房源数,小区数,总体单价价均价,总体总价均价,存入redis
def insertIndexInfo ():
all_number = house . count ()
com_number = Api . objects . values ( 'community_name' ). distinct (). count ()
mean_price = format ( house . aggregate ( Avg ( 'price' ))[ 'price__avg' ], '.3f' )
mean_unit_price = format ( house . aggregate ( Avg ( 'unit_price' ))[ 'unit_price__avg' ], '.3f' )
data = {
"all_number" : all_number ,
"com_number" : com_number ,
"mean_price" : mean_price ,
"mean_unit_price" : mean_unit_price
}
redis_conn . mset ( data )
# 1. 定义专门数据库操作类InsertIntoMysql
class InsertIntoMysql ():
# 属性中定义sql语句获取数据
def __init__ ( self ):
self . elevator_sql = 'select unit_price, price, elevator from house_api'
# ......
# 定义数据插入方法
def insertElevator ( self ):
index , values , price_mean , unit_price_mean = multiple ( self . elevator_sql , engine , 'elevator' )
for i in range ( len ( index )):
elevator = Elevator ()
elevator . version = 'v1'
elevator . title = '电梯分布情况'
elevator . has_ele = index [ i ]
elevator . el_num = values [ i ]
elevator . mean_price = price_mean [ i ]
elevator . mean_unit_price = unit_price_mean [ i ]
elevator . save ()
# 其他剩余数据插入方法
# ......
# 2. 数据处理模块,包括单因子和对因子处理
# 利用数据处理模块pandas
def single ( sql , engine , feature ):
"""
:param sql: sql语句
:param engine: mysql引擎
:param feature: 数据特征
:return: 特征,数量
"""
df_sql = sql
df = pd . read_sql ( df_sql , engine )
df [ feature ] = df [ feature ]. astype ( str )
if feature == 'floor' :
df [ df [ feature ] == '暂无数据' ] = '18'
df [ feature ] = df [ feature ]. apply ( lambda x : re . findall ( 'd+' , x )[ 0 ])
feature_res = df [ feature ]. value_counts ()
index = feature_res . index
values = feature_res . values
return index , values
def multiple ( sql , engine , feature ):
"""
:param sql: sql语句
:param engine: mysql引擎
:param feature: 数据特征
:return: 特征,数量,总价均价,单价均价
"""
df_sql = sql
df = pd . read_sql ( df_sql , engine )
df [ feature ] = df [ feature ]. astype ( str )
if feature == 'region' :
df [ feature ] = df [ feature ]. apply ( lambda x : re . sub ( r"[|]|'" , '' , x ). split ( ',' )[ 0 ])
feature_res = df [ feature ]. value_counts ()
index = feature_res . index
values = feature_res . values
price_mean = []
unit_price_mean = []
max_unit_price = []
min_unit_price = []
for inx , val in zip ( index , values ):
price_mean . append ( format ( df [ df [ feature ] == inx ][ 'price' ]. astype ( float ). mean (), '.3f' ))
unit_price_mean . append ( format ( df [ df [ feature ] == inx ][ 'unit_price' ]. astype ( float ). mean (), '.3f' ))
max_unit_price . append ( format ( df [ df [ feature ] == inx ][ 'unit_price' ]. astype ( float ). max (), '.3f' ))
min_unit_price . append ( format ( df [ df [ feature ] == inx ][ 'unit_price' ]. astype ( float ). min (), '.3f' ))
return index , values , price_mean , unit_price_mean , max_unit_price , min_unit_price
Das Frontend wird mit dem Basis-Framework Vue erstellt und auf Basis des Vue-Admin-Templates entwickelt. Das Haupt-Front-End-Geschäft wird für die Diagrammanzeige und Karteninformationen verwendet. Es wird hauptsächlich zur Visualisierung von Gebrauchthauspreisdaten und zugehörigen Informationen in Chengdu verwendet, einschließlich der Anzeige allgemeiner Informationen und verschiedener Merkmale – wie z. B. dem Status der Hausdekoration und der Visualisierung von Diagramminformationen. Wohnungssuche, einfache Wohnungsquellen-Informationsvisualisierung, Kartenanzeige und andere Funktionen.
Projektbeschreibung
Front-End-Projektdateibaum:
Spezifischer Prozess
# 安装到项目
npm install -S element-ui
# 全局加载: 在main.js中
import ElementUI from ' element-ui '
import ' element-ui/lib/theme-chalk/index.css '
import locale from ' element-ui/lib/locale/lang/en ' // i18n
Vue.use(ElementUI, { locale })
# 这样我们据可以在vue文件中使用UI组件,如: 按钮组件<Button />
# 安装到项目
npm install -S vue-baidu-map
# 局部加载: 在任意vue文件中
import BaiduMap from ' vue-baidu-map/components/map/Map.vue '
# 地图标注组件
import { BmMarker } from ' vue-baidu-map '
# 使用,例如:
< baidu-map ref= " baidumap "
class= " bm-view "
style= " width:100%;height:93vh "
ak= " YOUR AK "
:center= " {lng: 104.07, lat: 30.67} "
:scroll-wheel-zoom= " true "
:zoom= " 16 " >
< bm-navigation anchor= " BMAP_ANCHOR_TOP_LEFT " ></bm-navigation >
< bm-marker v-for= " spoi in searchResults " -- >
< ! --:position= " {lng: spoi.lng, lat: spoi.lat} " -- >
< ! --:title= " spoi.community_name " > -- >
< /bm-marker >
< /baidu-map >
Erstellen Sie einen neuen Ordner utils im Verzeichnis src, um unsere benutzerdefinierten Tooldateien zu speichern, erstellen Sie eine neue request.js und kapseln Sie die Axios-Methode neu, um die Vorgänge zu erfüllen, wenn wir die Schnittstelle anfordern. Diese Datei stellt ein Objekt zur Verfügung, das für asynchrone Anforderungen als erweiterte Schnittstellenzugriffsmethode verwendet wird. Diese Methode wird im Schnittstellendateidesign verwendet.
# 新建一个servie对象
const service = axios . create ( {
// 项目基本配置文件中定义的VUE_APP_BASE_API,开发和发布环境各部相同,在.env.devolopment和.env.product文件中进行配置
baseURL : process . env . VUE_APP_BASE_API ,
// 直接写死的url,无论环境如何都不会改变
// baseURL: 'http://127.0.0.1:8000/v1/api',
timeout : 8000 // 最大请求时间
} )
service . interceptors . request . use ( config => {
// 请求方法为POST时,配置请求头
config . method === 'post'
? config . data = qs . stringify ( { ... config . data } )
: config . params = { ... config . params }
config . headers [ 'Content-Type' ] = 'application/x-www-form-urlencoded'
return config
} , error => {
// 错误时提示信息
Message ( {
message : error ,
type : 'error' ,
duration : 3 * 1000
} )
Promise . reject ( error )
} )
service . interceptors . response . use (
response => {
if ( response . statusText === 'OK' ) {
// 正确响应时,直接返回数据
return response . data
} else {
Notification ( {
title : '错误' ,
message : '访问api错误' , // TODO 后端拦截错误请求
type : 'error' ,
duration : 3 * 1000
} )
}
} ,
// 错误处理
error => {
let dtext = JSON . parse ( JSON . stringify ( error ) )
try {
if ( dtext . response . status === 404 ) {
Notification ( {
type : 'error' ,
title : '出问题404' ,
message : '访问api错误或服务器忙' ,
duration : 3 * 1000
} )
}
} catch ( err ) {
Notification ( {
title : '错误' ,
message : '请求超时,请稍后再试或刷新页面' ,
type : 'error' ,
duration : 3 * 1000
} )
}
return Promise . reject ( error )
}
)
export default service
Verwenden Sie vue-router, um die Routing-Funktion zu implementieren, eine neue router.js zu erstellen und Routing-Elemente zu konfigurieren.
// 定义路由列表
export const constantRoutes = [
{
path : '/404' ,
component : ( ) => import ( '@/views/404' ) ,
hidden : true
} ,
{
path : '/' ,
component : Layout ,
redirect : '/dashboard' ,
children : [ {
path : 'dashboard' ,
name : 'Dashboard' ,
// 采用路由懒加载的方式
component : ( ) => import ( '@/views/dashboard/index' ) ,
meta : { title : '房源信息' , icon : 'house' }
} ]
} ,
// ......
]
// 构造路由对象
const createRouter = ( ) => new Router ( {
// mode: 'history', // require service support
scrollBehavior : ( ) => ( { y : 0 } ) ,
routes : constantRoutes
} )
// 在main.js 中引入并挂载
import router from './router'
new Vue ( {
el : '#app' ,
router ,
render : h => h ( App )
} )
Die Schnittstelle api.js-Datei definiert alle Schnittstellenzugriffsanforderungen im Zusammenhang mit dem Projekt, ist in Funktionen gekapselt und für die Verwendung durch Komponenten verfügbar gemacht.
// 例如获取区划信息接口
export function getRegionInfo ( id , params ) {
return request ( {
url : '/region/' + id ,
method : 'get' ,
params
} )
}
Bei der Anforderung handelt es sich um das oben gekapselte Schnittstellenzugriffsobjekt, das vor der Verwendung importiert werden muss. Wenn Sie die Schnittstelle in der Komponente verwenden, importieren Sie sie bei Bedarf, zum Beispiel:
// charts.js是接口函数文件
import { getRegionInfo } from '@/api/charts'
// 使用
// 第一种方式,由于它本来就是一个promise对象,所以直接在then中获取响应
getRegionInfo ( 1 ) . then ( ( res , err ) => {
// 相关操作
} )
// 第二种方式,使用async await方式,包含await的方法中需要加上async 作为方法前缀
let res = await getRegionInfo ( 1 )