Vue+Node调用Python并将结果显示到Web页面中

前言

关于这个知识点,其实很早之前我就想写一篇文章记录一下遇见的一些问题了,但是由于各种问题,一直没有来得及写,最近时间相对充裕,对我在学习过程中遇见的问题进行记录一下。

关于我为什么要这么来回折腾?如果你看过我以前的文章,可以很明显的看出我是一个纯前端,对于后端语言并不会,因此如果让我使用Java去调用Python,肯定花的时间会更多,因此我通过Node,做了一个调用Python的代码程序。

这个其实并不是很难,只要用过Node开发的,都可以做到。我这篇仅记录我从零接触,在实现过程中遇见的一些问题,并不做深入的讲解。

实现了什么

在Vue项目中写一个按钮,当用户点击按钮的时候,使用JavaScript调用Node的接口,通过新建子进程的方式,调用Python的代码块,如下:

实现过程

前端Web

  1. 首先我们需要创建一个Vue3+Vite的前端工程;
  2. 创建页面,支持用户触发事件,以调用Node接口驱动Python;

下面我们先来准备接口请求需要的环境

  • 通过npm install axios安装插件;

  • 在/public目录下创建config.js,用来存储接口请求路径(避免多个地址导致需要创建多个请求文件);

    js 复制代码
    const QUERY_BASE_URL = "http://127.0.0.1:3000"; // 接口请求基础地址
    window.config = {
      baseUrl: QUERY_BASE_URL,
    };
  • 在/public目录下创建server.js,用来将配置暴露到引入文件中;

    js 复制代码
    export const data = {
      baseUrl: window.config.baseUrl,
    };
  • 创建request.js,方便接口调用;

    js 复制代码
    import axios from "axios";
    const service = axios.create({
      timeout: 120000,
      headers: {
        "Content-Type": "application/json",
      },
    });
    service.interceptors.request.use(
      (config) => {
        if (sessionStorage.getItem("token")) {
          const token = sessionStorage.getItem("token");
          config.headers["authorization"] = "Bearer " + token;
        }
        return config;
      },
      (error) => {
        Promise.reject(error);
      }
    );
    service.interceptors.response.use(
      (response) => {
        return response.data;
      },
      (error) => {
        if (error.response) {
          if (error.request.status == 0) {
            error.message = "服务器发生错误,请检查服务器。";
          }
        }
        return Promise.reject(error);
      }
    );
    export default service;
  • 创建接口请求api,下面的接口会在Node部分讲到;

    js 复制代码
    import request from "@/api/request";
    import { data } from "~/server"; // /public/server
    const baseUrl = data.baseUrl + ""; // 添加代理用的
    // 测试Get参数使用
    export function testNodeGet(param) {
      return request({
        url: baseUrl + "/nodeTestGet",
        method: "get",
        params: param,
      });
    }
    // 测试Post参数使用
    export function TestNodePost(data) {
      return request({
        url: baseUrl + "/nodeTestPost",
        method: "post",
        data,
      });
    }
    // 测试Python脚本使用
    export function testPyScript(param) {
      return request({
        url: baseUrl + "/py/script",
        method: "get",
        params: param,
      });
    }
  • 创建前端页面,调用接口;

    html 复制代码
    <template>
      <div>
        <button @click="runCommandGet">测试Get传参</button>
        <button @click="runCommandPost">测试Post传参</button>
        <button @click="runCommandPython">Python九九乘法表</button>
        <pre>{{ output }}</pre>
      </div>
    </template>
    
    <script>
    import { testNodeGet, TestNodePost, testPyScript } from "@/api/modules/node.js";
    export default {
      data() {
        return {
          output: "",
        };
      },
      methods: {
        async runCommandGet() {
          try {
            const params = { name: "张三" };
            const response = await testNodeGet(params);
            this.output = response.result;
          } catch (error) {
            this.output = "Error: " + error;
          }
        },
        async runCommandPost() {
          try {
            const params = { name: "张三", age: 28, type: "user" };
            const response = await TestNodePost(params);
            this.output = response.result;
          } catch (error) {
            this.output = "Error: " + error;
          }
        },
        async runCommandPython() {
          try {
            const params = {};
            const response = await testPyScript(params);
            this.output = response.result;
          } catch (error) {
            this.output = "Error: " + error;
          }
        },
      },
    };
    </script>
    
    <style scoped lang="scss">
    button {
      margin-right: 12px;
    }
    </style>

Node服务

  • 创建Node接口程序,我使用express来实现,由于我的Node程序与依赖安装在Vue工程里面,因此这里需要注意引用的方式,我这里使用的是import的方式,并没有使用require,具体两者有什么区别,可以看我之前写的文章:NodeJS模块化,里面介绍了二者在引用、导出、使用过程中的区别与需要注意的点。下面是使用express创建的一个简单的接口程序:

    js 复制代码
    import express from "express";
    const app = express();
    const port = 3000;
    app.get("/api/get", (req, res) => {
      res.send({ success: true, result: req.query, });
    });
    app.listen(port, () => {
      console.log(`Server is running on http://localhost:${port}`);
    });
  • 上面的案例中,使用get方式进行了接口的实现,然而post相比较,需要注意的就是,post通过body进行传参,因此我们在抓取用户传入参数的时候,就不能直接通过req.query进行抓取了,我们需要在上面代码的基础上,添加如下代码:

    js 复制代码
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));

    通过这段代码,将post的入参进行解码,使能够正常访问,接口的实现如下:

    js 复制代码
    app.post("/api/post", (req, res) => {
      res.send({ success: true, result: req.body, });
    });
  • 创建子线程,调用python脚本。

    • 使用child_process实现创建子线程

    • 但是在创建子线程使用spawn调用python的时候,由于我们文件夹结构的原因,无法正常访问到python文件,因此这里需要对文件夹结构进行格式化,又用到了path,代码如下:

      js 复制代码
      import express from "express";
      import { spawn } from "child_process";
      import path from "path";
      
      const app = express();
      const port = 3000;
      
      let urlArr = new URL(".", import.meta.url).pathname.split("/");
      const __dirname = urlArr.filter((o) => !!o).join("/");
      
      app.get("/py/script", async (req, res) => {
        const pythonProcess = await spawn("python", [
          path.resolve(__dirname, "../python/test.py"), // python执行脚本路径
          "getFunc", // python脚本中的函数名
          JSON.stringify(req.query), // 需要传入的参数,因为是数组,保持一致性
        ]);
        try {
          pythonProcess.stdout.on("data", (data) => {
            // 接口返回数据
            res.send({
              success: true,
              result: data.toString(),
            });
          });
        } catch (error) {
          pythonProcess.stderr.on("data", (data) => {
            res.status(500).send(data.toString());
          });
        }
      });
  • Node完整代码如下:

    js 复制代码
    import express from "express";
    import { spawn } from "child_process";
    import path from "path";
    
    const app = express();
    const port = 3000;
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    
    let urlArr = new URL(".", import.meta.url).pathname.split("/");
    const __dirname = urlArr.filter((o) => !!o).join("/");
    
    app.get("/nodeTestGet", (req, res) => {
      res.send({
        success: true,
        result: req.query,
      });
    });
    
    app.post("/nodeTestPost", (req, res) => {
      res.send({
        success: true,
        result: req.body,
      });
    });
    
    app.get("/py/script", async (req, res) => {
      const pythonProcess = await spawn("python", [
        path.resolve(__dirname, "../python/test.py"), // python执行脚本路径
        "getFunc", // python脚本中的函数名
        JSON.stringify(req.query), // 需要传入的参数,因为是数组,保持一致性
      ]);
      try {
        pythonProcess.stdout.on("data", (data) => {
          res.send({
            success: true,
            result: data.toString(),
          });
        });
      } catch (error) {
        pythonProcess.stderr.on("data", (data) => {
          res.status(500).send(data.toString());
        });
      }
    });
    
    app.listen(port, () => {
      console.log(`Server is running on http://localhost:${port}`);
    });

Python脚本

这里只需要注意一个点,python需要返回到接口的数据,直接print即可,如果print多个,会拼接返回。

python 复制代码
import sys
import json

def getFunc():
    for i in range(1, 10):
        for j in range(1, i+1):
            print(f"{j} x {i} = {i*j}", end="\t")
        print()

def getHello(params): # params是传入的参数,如果要返回给js模块,直接使用print输出即可
    print("Hello, World!", 1+1)

if __name__ == "__main__":
    func_name = sys.argv[1] # Node中调用python的第二个参数
    args = sys.argv[2] # Node中调用python的第三个参数
    if func_name == "getFunc":
        getFunc()
    elif func_name == "getHello":
        params = json.loads(args)  # 解析 JSON 字符串
        getHello(params)

代码仓库

本文相关代码已上传至gitee,点击跳转

相关推荐
Bruce_Liuxiaowei18 分钟前
《基于Python与DashScope的智能语音合成工具开发》
开发语言·python·ai·dashscope
程序猿小柒42 分钟前
4.Python字符串和列表:字符串输入、字符串输出、下标和切片、字符串常见函数、列表(list)、列表的循环遍历、列表的增删改查、列表的嵌套、列表的切片
开发语言·python
扫地僧0091 小时前
第18章 不可变对象设计模式(Java高并发编程详解:多线程与系统设计)
java·python·设计模式
web_132334214361 小时前
如何升级node.js版本
node.js
睡不着还睡不醒1 小时前
anaconda中可以import cv2,但是notebook中cv2 module not found
python·opencv·计算机视觉
华清远见IT开放实验室1 小时前
【每天学点AI】实战仿射变换在人工智能图像处理中的应用
图像处理·人工智能·python·opencv·仿射变换
码界筑梦坊2 小时前
基于Flask的历史空难数据可视化分析系统的设计与实现
python·信息可视化·flask·毕业设计
萧鼎2 小时前
使用Python的Tabulate库优雅地格式化表格数据
python
hz.ts2 小时前
Node.js 实现简单爬虫
爬虫·node.js
程序员小远2 小时前
接口自动化测试框架(pytest+allure+aiohttp+ 用例自动生成)
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest