背景
最近在electrion项目中,我需要使用一个特定的 Python 库功能,希望使用 JavaScript 调用Python方法并得到运行结果。
技术调研
Node与Python通信
前提:你的操作系统上已安装python3。
Node.js 的child_process
模块允许你创建子进程来执行系统命令,包括运行 Python 脚本。child_process
模块提供了两个常用命令exec
和spawn
。
exec
适用于执行短命令并在命令完成后获取完整的输出。它将子进程的输出缓存在内存中,适合处理小数据量。
spawn
适用于处理流式数据。这意味着如果你需要处理大数据量的输出,比如从子进程持续接收数据并进行处理,spawn
更加合适。
示例代码:
javascript
// Nodejs代码
const { exec } = require('child_process');
const command = 'python3 example.py chenchen501';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`执行错误: ${error}`);
return;
}
console.log(`输出: ${stdout}`); // 此处输出结果 Hello, chenchen501
if (stderr) {
console.error(`错误: ${stderr}`);
}
});
// example.py代码
import sys
def main():
if len(sys.argv) > 1:
name = sys.argv[1]
print(f"Hello, {name}!")
else:
print("Hello, World!")
if __name__ == "__main__":
main()
当然如果你有大量数据从python脚本中返回,需要使用spawn,否则会因为内存溢出导致异常。
javascript
const { spawn } = require('child_process');
// 调用 Python 脚本
const pythonProcess = spawn("python3", ["example.py", "chenchen501"]);
// 处理输出
pythonProcess.stdout.on('data', (data) => {
console.log(`Output: ${data}`);
});
// 处理错误
pythonProcess.stderr.on('data', (data) => {
console.error(`Error: ${data}`);
});
// 处理进程结束
pythonProcess.on('close', (code) => {
console.log(`Python process exited with code ${code}`);
});
还是spawn命令适用范围更广,但是写上还是用的会调,更加简洁的写法是类似Promise的链式调用写法,为了达此目的使用tinyspawn库:
ini
npm install tinyspawn
const tinyspawn = require('tinyspawn');
const command = 'python3';
const args =["example.py", "chenchen501"];
// 使用 tinyspawn 执行命令
tinyspawn(command, args)
.then(result => {
console.log(`输出: ${result.stdout}`);
if (result.stderr) {
console.error(`错误: ${result.stderr}`);
}
})
.catch(error => {
console.error(`执行错误: ${error}`);
});
支持多平台
由于在macOS上的命令是python3,但是在windows上是python。所以代码中要识别系统环境并调用不同的命令。
javascript
const tinyspawn = require("tinyspawn");
const path = require("path");
function getPythonCommand() {
// 检测操作系统
const isMac = process.platform === "darwin";
const isWindows = process.platform === "win32";
let result = "python3";
if (isMac) {
result = "python3";
} else if (isWindows) {
result = "python";
} else {
// 对于其他类 Unix 系统(如 Linux),通常也使用 python3
result = "python3";
}
return result;
}
function getPythonScript() {
return path.join(__dirname, "example.py");
}
// 使用 tinyspawn 执行命令
tinyspawn(getPythonCommand(), [getPythonScript(), "chenchen501"])
.then((result) => {
console.log(`输出: ${result.stdout}`);
if (result.stderr) {
console.error(`错误: ${result.stderr}`);
}
})
.catch((error) => {
console.error(`执行错误: ${error}`);
});
增加python环境判断
因为我们的nodejs程序需要有python环境才能调用python脚本,所以最好加上环境判断逻辑,这里推荐一个小库binary-version-check, 轻松判断命令的版本。
javascript
import binaryVersionCheck from 'binary-version-check';
try {
await binaryVersionCheck('python3', '>=3.7');
} catch (error) {
console.log(error);
//=> 'InvalidBinaryVersion: curl 7.30.0 doesn't satisfy the version requirement of >=8'
}
支持无python环境
如果制作如electron客户端应用。需要安装到用户电脑上,可以考虑将python脚本连通python一起打包成二进制可执行程序,这样可以无需安装python也可以利用python的能力。
总结
如上,我们学会了在nodejs中使用python,为了提供给其他前端同学使用,我们同样可以将调用python的逻辑封装到npm包中,这样可以让其他同学无感知的使用python脚本的能力啦。