【原创】三十分钟实时数据可视化网站前后端教程 Scrapy + Django + React 保姆级教程向

这个本来是想做视频的,所以是以讲稿的形式写的。最后没做视频,但是觉得这篇文还是值得记录一下。真的要多记录,不然一些不常用的东西即使做过几个月又有点陌生了。

文章目录

      • 爬虫 SCRAPY
        • xpath
      • 后端 DJANGO
      • 前端 REACT

Hello大家好这里是小鱼,最近我做了一个前后端分离的实时数据大屏,因为是自己做全栈开发,所以从零开始学了不少新的知识,想在这里分享或者说记录一下,这个教程会尽量短,但是从数据获取,到后端项目和前端项目,包括项目部署到云服务器都会讲到,那我们开始吧。

爬虫 SCRAPY

首先我们要考虑数据大屏的数据从哪来,数据大屏的重要优势,一个是多端显示(比如做好了之后无论在手机/电脑还是电视这种更大的显示器都可以看),再一个就是实时性,一般数据大屏不同于那种一次性生成的数据可视化图,数据大屏的数据是实时更新的。
给公司做项目的话可能会有现成的实时数据给到你,那我们做自己项目就要考虑实时数据从哪来,有一些网站会向外提供免费开放的数据api接口,直接调用就可以拿到数据,这是一个方法。但是我今天想做的是全头到尾不依赖其他第三方,独立的写一个完整的项目。
我这边想到的获取数据的方法就是爬虫,先叠个甲,咋们这个视频只是用于交流学习,不涉及商业行为,如有侵权联系我删除。
我选的框架是scrapy,当然你也可以用你熟悉的框架,那我们就直接开始。

首先安装scrapy,安装过程大家根据自己的操作系统搜索一下,到命令行输入scrapy有如下显示则安装成功。
请添加图片描述
使用scrapy的命令创建项目
scrapy startproject douban
在这里插入图片描述

这时候可以用编辑器打开这个项目,能看到目录结构是这样的:
在这里插入图片描述

里面有一个 spiders 目录,我们后面爬虫代码的主要逻辑就写在这个目录里。
Items 文件用来定义我们爬取到的数据字段和类型。
pipelines 文件用来执行保存数据的操作,后面我们会在其中写和数据库连接相关的代码。
middlewares 文件是中间件,我们这次用不上可以先忽略。
settings 文件顾名思义是设置文件,里面可以配置的东西很多,后面再详细。

好,看完目录现在来写主要的代码,我想要爬取的是豆瓣现在正在上映电影的评价,我们知道豆瓣的评分相对来说还是比较能代表口碑的,它又是一个会实时变化的值,那我们对数据爬取之后存入自己的数据库,就可以在前端展示当前线上电影评价排行还有单个电影的评价趋势等数据图表,方便自己挑选电影。
我们使用这个命令创建一个爬虫主文件,scrapy genspider main_dou 'https://movie.douban.com/cinema/nowplaying/yichang/'命令。
这时会看到spiders目录下有一个main_dou.py文件,里面已经给出了大致的架构,
在这里插入图片描述

里面有一个用scrapy.Spider类创建的子类,并有三个属性和一个方法。
name 是唯一名称
allow_domains 是搜索的域名范围,规定只爬取这个域名下的网页
从 start_urls 开始抓取数据
parse(self, response) :每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,负责解析返回的网页数据(response.body),提取结构化数据(生成item)

在这里插入图片描述
首先在settings中我们将user agent换一下
然后我们输入命令跑一下看能不能成功。
抓出来是乱码,是header的问题。

xpath

只需要懂三个符号
/ 直接子集(根元素)
// 可以跳级(不考虑位置)
@ 属性访问

我们直接在浏览器F12,在dom里可以按xpath语法搜索,
在这里插入图片描述

这里输入的意思就是找属性里id为nowplaying的div,是不是很简单。
在这里插入图片描述

这样就找到了所有有上映影片信息的li标签,可以看到标签里有影片信息,我们提取对应属性就行。
最后的代码是这样,extract()方法用于从XPath结果中提取文本值,这样就爬到了所有影片名字和打分。
接下来我们要将数据存到数据库中,实现持久化。

我们在items文件里定义好数据字段,然后在主程里拼好每一组数据
在这里插入图片描述
在这里插入图片描述

接下来我们要连接数据库,我这里用的mongodb,你们也可以使用自己熟悉的数据库比如mysql等。
在settings里写好数据库的地址,库名表名,账号密码等信息,在pipelines文件里进行注入。如果你也是用的mongodb,可以和我一样配置,不过其实别的数据库也差不多的。
再跑一下项目,发现数据库里已经成功写入数据了,我们这一步就完成了。
在这里插入图片描述

再只需要让这个scrapy项目按时自动执行我们数据库就有实时数据了,这个我们后面部署的部分再讲。

后端 DJANGO

我们已经有了数据,现在需要开发一个后端项目,对数据库的数据进行操作,并暴露api給前端。
因为我平时用python比较多,后端框架选型的时候就选了Django,当然也可以选你熟悉的框架。Django其实是一款前后端不分离的框架,将前端代码写在django的templates文件夹里。前后端冗杂在一块会比较难维护,针对前后端分离的情况,Django退出了DRF(Django Rest Framework)。那我们一步步来做。
首先在开发后端项目之前可以整一个虚拟环境,因为后端项目依赖的库会比较多,而各个后端项目之间如果库的版本各不相同就会比较麻烦,所以开启一个虚拟环境保证这个项目的依赖都是独立的。

sudo pip3 install virtualenv
virtualenv douban-env
source env/bin/activate

在这里插入图片描述

安装django

pip3 install django==3.2.1
pip3 install djangorestframework==3.12.4
django-admin.py startproject be_douban
cd be_douban
python3 manage.py startapp douban

此时的目录结构是这样的
在这里插入图片描述

douban那个文件夹是我们创建出来的app,我们先到settings中注册这个app。
为了操作mongodb,我们需要安装djongo

我们来看一下DRF这个架构(这里借用一下大佬的图,图源见水印)
在这里插入图片描述

DRF是将数据库的东西通过ORM的映射取出来,通过view和serializers文件绑定REST接口,当前端请求时,返回序列化好的json。
我们从Modal开始修改:Model文件里定义了数据的结构和关系,类似于数据库表的描述。我们把之前爬虫存在数据库里的字段再这边再声明一下。

from djongo import models

# Create your models here.

class Douban(models.Model):
    _id = models.CharField(max_length=128, primary_key=True)
    name = models.CharField(max_length=128)
    score = models.FloatField()
    time = models.IntegerField()

    class Meta:
        db_table = 'spider_douban'

然后是序列化器,新建一个serializer.py文件,因为我们直接需要所有字段,这边fields直接__all__就行,如果不需要所有的,可以自己用数组定义 例如 fields = [‘_id’, ‘station’, ‘time’]

from douban.models import Douban
from rest_framework.serializers import ModelSerializer


class DoubanSerializer(ModelSerializer):
    class Meta:
        model = Douban
        fields = '__all__'

最后是视图view文件,定义了API的行为,我们主要对数据处理的逻辑写在这,我们是需要一个接口拿到最新的所有电影的分数,然后在前端进行排行,那么就这么写。

from rest_framework.decorators import api_view
from rest_framework.response import Response
from douban.models import Douban
from douban.serializers import DoubanSerializer
from django.db.models import Max

@api_view(['GET'])
def movie_lasttime_list(request):
    if request.method == 'GET':
        # 使用Djongo查询获取最后一次时间的时间戳
        last_time = Douban.objects.aggregate(Max('time'))['time__max']
        
        # 使用过滤器获取最后一次时间的所有数据
        movies = Douban.objects.filter(time=last_time)
        
        # 将查询到的数据序列化并返回
        serializer = DoubanSerializer(movies, many=True)
        return Response(serializer.data)

之后定义一下url,就是api的路径,我们在douban app下创建一个url文件

from django.urls import include, path
from douban import views

urlpatterns = [
    path('api/current', views.movie_lasttime_list),
]

之后在主url文件中引入这个app的url文件

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('douban.urls')),
]

现在就做完了,可以跑项目了,第一次要执行一下迁移命令

python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py runserver

这时候看到
在这里插入图片描述

证明已经跑起来了
浏览器输入对应url可以看到
在这里插入图片描述

我们的接口就写好了。

除了最新数据,我们还需要一个趋势数据,就不一步步讲解了,和上面的一样,展示一下view文件的逻辑:

@api_view(['GET'])
def movie_scores_by_name(request):
    if request.method == 'GET':
        movies = Douban.objects.all()  # 获取所有电影数据
        
        movie_scores = {}  # 创建一个字典来存储电影名称和对应的时间和分数列表
        
        for movie in movies:
            if movie.name in movie_scores:
                movie_scores[movie.name].append({"time": movie.time, "score": movie.score})
            else:
                movie_scores[movie.name] = [{"time": movie.time, "score": movie.score}]
        
        return Response(movie_scores)

前端 REACT

接下来看前端部分。
一般经常写前端的人都有自己熟悉的脚手架,我也有一套 react + ts + less + webpack 的脚手架。但是为了简单,毕竟这个教程不是一个前端各种技术栈的介绍,我们就是用最简单的官方的脚手架实现一下,它内部封装了webpack。

npm install -g create-react-app
create-react-app fe_douban

我们 npm start 看到下面这个页面就是项目跑起来了:
在这里插入图片描述
在这里插入图片描述

这是原始的目录,我们稍微调整一下结构。把src下的文件删掉只保留index.js和index.css,再创建一个container和component文件夹分别放容器和组件。
在这里插入图片描述

引入echarts,在命令行输入npm install echarts,可以看到package.json文件里已经有Echarts的依赖了
而echarts怎么用呢,我们在component目录下创建一个bar-chart文件夹,新建index.ts文件。用函数组件的写法吧,在useEddect里面对echart实例进行操作,简单来说就是先init初始化,然后定义Option,我们图表的所有配置都在option里面,这部分就不详述了,可以去Echarts官网看例子,还有手册,每个配置项都很详细,我就直接展示了。

import React, { useEffect, useRef } from 'react';
import * as echarts from 'echarts';

import './index.css';

const mockData = [
  {
    "_id": "8c7c957e237ff7ec2f848908fbea9817",
    "name": "奥本海默",
    "score": 8.7,
    "time": 1693390018
  }, 
  {
    "_id": "21d53373fa67f1866993492cfb782d17",
    "name": "燃冬",
    "score": 6.2,
    "time": 1693390018
  }
]

const BarChart = (props) => {
  const chartRef = useRef();

  let data = props.data || mockData;
  data.sort((a, b) => a.score - b.score);
  console.log(data)

  useEffect(() => {
    const chart = echarts.init(chartRef.current); //echart初始化容器
    let option = {
      title: {
        text: props.title ? props.title : '',
        textStyle: {
          color: '#aad8f8',
          fontWeight: 300,
          fontStyle: 'italic'
        }
      },
      color: ['#faeea0'],
      grid: {
        left: '15%'
      },
      tooltip: {
        trigger: 'axis'
      },
      xAxis: {
        type: 'value',
        axisLabel: {
          color: '#ffffff'
        }
      },
      yAxis: {
        type: 'category',
        data: data.map((item) => item.name)
      },
      series: [
        {
          data: data.map((item) => item.score),
          label: {
            show: true,
            position: 'inside',
            color: '#ffffff'
          },
          type: 'bar'
        }
      ]
    };

    chart.setOption(option);

    const echartsResize = () => {
      echarts.init(chartRef.current).resize();
    };
    window.addEventListener('resize', echartsResize);
    return () => window.removeEventListener('resize', echartsResize);
  }, [props]);

  return <div className='bar-chart-container' ref={chartRef}></div>;
};

export default BarChart;

写好BarChart组件后在container创建一个homePage=,在其中引用之前写好的组件,并传入数据。数据是通过fetch方法在我们后端接口取得,具体代码如下:

import React from "react";

import './index.css'
import BarChart from "../../component/bar-chart";

class HomePage extends React.Component {
  state = {
    barData: null
  }

  componentDidMount() {
    fetch("http://127.0.0.1:8000/api/current")
      .then(res => res.json())
      .then(data => {
        this.setState({
          barData: data
        });
      }, (e) => console.log(e))
  }

  render() {
    return (
      <div className="home-page">
        {/* <LineChart /> */}
        <BarChart title={"今日在映电影评分排名"} data={this.state.barData} />
      </div>
    )
  }
}

export default HomePage;

现在打开页面,已经可以看到排名后的电影和评分了。这样整个流程就走通了。
最后将爬虫项目,前后端都部署到云服务器,将爬虫设置为定时任务,前端可以用 nginx,后端用 uwsgi + nginx。就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/475316.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

企业用大模型如何更具「效价比」?百度智能云发布5款大模型新品

服务8万企业用户&#xff0c;累计帮助用户精调1.3万个大模型&#xff0c;帮助用户开发出16万个大模型应用&#xff0c;自2023年12月以来百度智能云千帆大模型平台API日调用量环比增长97%...从一年前国内大模型平台的“开路先锋”到如今的大模型“超级工厂”&#xff0c;百度智能…

【MySQL | 第五篇】MySQL事务总结

文章目录 5.MySQL事务5.1什么是事务&#xff1f;5.2什么是数据库事务&#xff1f;5.3数据库事务四大特性5.4并发事务带来的问题及解决方案&#xff1f;5.4.1脏读/不可重复读/幻读5.4.2不可重复读和幻读有什么区别&#xff1f;5.4.3解决并发事务带来的问题&#xff08;1&#xf…

雷池 WAF 社区版:下一代 Web 应用防火墙的革新

黑客的挑战 智能语义分析算法&#xff1a; 黑客们常利用复杂技术进行攻击&#xff0c;但雷池社区版的智能语义分析算法能深入解析攻击本质&#xff0c;即使是最复杂的攻击手法也难以逃脱。 0day攻击防御&#xff1a; 传统防火墙难以防御未知攻击&#xff0c;但雷池社区版能有效…

南方某电网公司如何通过代码审计保障能源数字化转型?

​南方某电网公司供电营业区覆盖十几个州市&#xff0c;是所在省域电网运营和交易的主体&#xff0c;也是承担对外供电和培育电力支柱产业的重要企业。近年来该电网公司在数字化转型方面深耕细作&#xff0c;紧跟工业互联网的时代浪潮&#xff0c;打造设备智慧运维数字化场景&a…

tinyrenderer-三角形光栅化和背面剔除

画空心三角形 根据之前的画线算法&#xff0c;可以很简单画出一个空心三角形&#xff0c;对三角形三个顶点&#xff0c;按顺序分别首尾画连线就可以 void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image, TGAColor color) { line(t0, t1, image, color); line(…

Python 批量读取文件夹中图像

两种方法 一、用PIL库 import os from PIL import Imagedef read_images(folder_path):images []for filename in os.listdir(folder_path):if filename.endswith((.png, .jpg, .jpeg, .bmp, .tif, .tiff)):img_path os.path.join(folder_path, filename)image Image.open…

MES管理系统在生产计划与排产调度流程中的应用

在现代工业生产中&#xff0c;MES管理系统已经成为企业优化生产流程、提升生产效率的重要工具。MES管理系统在生产计划与排产调度中的具体应用&#xff0c;不仅能够帮助企业更好地控制生产过程&#xff0c;还能实现资源的合理配置&#xff0c;从而提高企业的整体竞争力。 首先&…

简单几点让你清楚VR全景制作方式,快来免费学习!

VR全景展示作为当下一种新型的宣传方式&#xff0c;能够有效解决商家企业的展示、宣传推广成本高的问题&#xff0c;也成为了实体门店获客引流的全新方式&#xff0c;助力行业实现低成本、高效率的宣传推广。 而从实际全景成品效果来看&#xff0c;不仅没有“美颜嫌疑”&#x…

蓝桥杯-模拟-航班时间

题目 思路 去时到达外地的时间-去时离开本地的时间 时区差时飞行时间 回时到达本地的时间-回时离开外地的时间 -时区差时飞行时间 故二者加起来即可得到飞行时间 代码 # 去时到达外地的时间-去时离开本地的时间 时区差时飞行时间 # 回时到达本地的时间-回时离开外地的时间 -…

初阶数据结构之---二叉树链式结构(二叉树的构建,二叉树的前序,中序,后序和层序遍历,计算二叉树结点个数,第k层结点个数,叶子结点个数,判断是否为完全二叉树)

引言 本篇博客是初阶数据结构树的收尾&#xff0c;将会讲掉基本二叉树链式结构的具体内容和实现&#xff0c;包括二叉树的构建&#xff0c;前序遍历&#xff0c;中序遍历&#xff0c;后序遍历和层序遍历&#xff0c;计算二叉树结点个数&#xff0c;第k层结点个数&#xff0c;二…

为什么高铁提前三分钟停止检票?

为什么高铁提前三分钟停止检票&#xff1f; 高铁&#xff0c;作为现代交通方式的代表&#xff0c;以其高速、便捷、舒适的特点受到了广大乘客的青睐。然而&#xff0c;在乘坐高铁的过程中&#xff0c;乘客们可能会遇到一个问题&#xff1a;为什么高铁会提前三分钟停止检票呢&a…

Vue.js+SpringBoot开发服装店库存管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服装档案模块2.4 服装入库模块2.5 服装出库模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 服装档案表3.2.3 服装入库表3.2.4 服装出库表 四、系统展示五、核心代码5.…

Redis 又双叒叕改开源协议了,微软提前推出高性能替代方案 Garnet

Redis 官宣&#xff1a;是的&#xff0c;我们又改开源协议了 3 月 20 号&#xff0c;Redis 商业公司 CEO Rowan Trollope 在公司官方博客上宣布了一项重大变革。Redis 核心软件将从 BSD 3-Clause 许可证过渡到双重许可证模式&#xff0c;这一变化将从 Redis v7.4 版本开始&…

Java 应用程序监控

Java 监控涉及监控在 Java 上运行的应用程序的关键性能指标&#xff0c;以及 支持 Java 应用程序的服务器。Java 监控可以帮助优化 Java 应用程序的性能&#xff0c;发现和识别以下问题&#xff1a; 导致常见的应用程序问题&#xff0c;并在问题影响最终用户之前解决问题。 Ja…

美联储3月会议来袭,市场对6月开启降息或仍较为乐观

KlipC报道&#xff1a;美国联邦储备委员会结束了为期两天的货币政策会议&#xff0c;宣布仍继续将联邦基金利率目标区间维持在5.25%-5.50%之间不变。 值得一提的是会议声明声明几乎只字未改&#xff0c;唯一改动的是在描述就业市场时&#xff0c;将“新增就业过去一年有所放缓但…

解决Oracle VM VirtualBox无法与Windows互相复制粘贴的教程

说明&#xff1a;要实现从Windows上复制然后在VM VirtualBox上粘贴&#xff0c;必须要在VM VirtualBox上下载virtualbox-guest-dkms 第一步&#xff1a; 第二步&#xff1a; 按CtrlALTT打开命令终端输入 sudo apt-get install virtualbox-guest-dkms 然后重启 第三步&…

Linux下QT界面小程序开发

背景&#xff1a;需要在linux不同环境下可以测试我们的读卡器设备 搭建本地linux开发环境&#xff08;本来想VS里开发然后通过SSH的方式在linux下编译&#xff0c;但是工具链一直没搞起来&#xff0c;所以我是在ubuntu里安装的QT Creator工具直接开发的&#xff09;&#xff1b…

(一)手把手教你如何通过ARM DesignStart计划在FPGA上搭建一个Cortex-M3软核

&#xff08;一&#xff09;手把手教你如何通过ARM DesignStart计划在FPGA上搭建一个Cortex-M3软核 一、ARM DesignStart计划 1.1 如何下载ARM DesignStart Cortex-M3相关文件 ​ 关于ARM DesignStart计划的介绍:ARM DesignStart计划——私人定制一颗ARM处理器 - 知乎 (zhih…

Android ViewPager不支持wrap_content的原因

文章目录 Android ViewPager不支持wrap_content的原因问题源码分析解决 Android ViewPager不支持wrap_content的原因 问题 <androidx.viewpager.widget.ViewPagerandroid:id"id/wrap_view_pager"android:layout_width"match_parent"android:layout_he…

快速画流程图

使用在线工具&#xff1a;PlantUML PlantUML 官网:https://plantuml.com/ 中文官网:https://plantuml.com/zh/ 使用步骤如下&#xff1a; 1、拷贝一个完成函数&#xff1a; int func_init(const char *tag) {if (tag ! NULL) {printf("set TAG :%s", tag);}print…