如何从Node.js调用Python函数

我有一个Express Node.js应用程序,但我也有一个机器学习算法在Python中使用。是否有一种方法可以从我的Node.js应用程序调用Python函数来利用机器学习库的强大功能?

283572 次浏览

我所知道的最简单的方法是使用“child_process”;与节点打包的包。

然后你可以这样做:

const spawn = require("child_process").spawn;
const pythonProcess = spawn('python',["path/to/script.py", arg1, arg2, ...]);

然后你所要做的就是确保你在python脚本中import sys,然后你可以使用sys.argv[1]访问arg1,使用sys.argv[2]访问arg2,等等。

要将数据发送回节点,只需在python脚本中执行以下操作:

print(dataToSendBack)
sys.stdout.flush()

然后node可以使用以下命令监听数据:

pythonProcess.stdout.on('data', (data) => {
// Do something with the data returned from python script
});

由于这允许使用spawn将多个参数传递给脚本,您可以重新构造python脚本,以便其中一个参数决定调用哪个函数,而另一个参数传递给该函数,等等。

希望这是清楚的。如果有需要澄清的地方请告诉我。

例子适用于具有Python背景并希望将其机器学习模型集成到Node.js应用程序中的人:

它使用child_process核心模块:

const express = require('express')
const app = express()


app.get('/', (req, res) => {


const { spawn } = require('child_process');
const pyProg = spawn('python', ['./../pypy.py']);


pyProg.stdout.on('data', function(data) {


console.log(data.toString());
res.write(data);
res.end('end');
});
})


app.listen(4000, () => console.log('Application listening on port 4000!'))

在Python脚本中不需要sys模块。

下面是使用Promise执行任务的更模块化的方式:

const express = require('express')
const app = express()


let runPy = new Promise(function(success, nosuccess) {


const { spawn } = require('child_process');
const pyprog = spawn('python', ['./../pypy.py']);


pyprog.stdout.on('data', function(data) {


success(data);
});


pyprog.stderr.on('data', (data) => {


nosuccess(data);
});
});


app.get('/', (req, res) => {


res.write('welcome\n');


runPy.then(function(fromRunpy) {
console.log(fromRunpy.toString());
res.end(fromRunpy);
});
})


app.listen(4000, () => console.log('Application listening on port 4000!'))

python-shell模块通过extrabacon是一种从Node.js运行Python脚本的简单方法,具有基本但有效的进程间通信和更好的错误处理。

安装:

与npm < p >: npm install python-shell . < / p >

或用纱线: yarn add python-shell < / p >

运行一个简单的Python脚本:

const PythonShell = require('python-shell').PythonShell;


PythonShell.run('my_script.py', null, function (err) {
if (err) throw err;
console.log('finished');
});

运行带有参数和选项的Python脚本:

const PythonShell = require('python-shell').PythonShell;


var options = {
mode: 'text',
pythonPath: 'path/to/python',
pythonOptions: ['-u'],
scriptPath: 'path/to/my/scripts',
args: ['value1', 'value2', 'value3']
};


PythonShell.run('my_script.py', options, function (err, results) {
if (err)
throw err;
// Results is an array consisting of messages collected during execution
console.log('results: %j', results);
});

完整的文档和源代码,请查看https://github.com/extrabacon/python-shell

我在节点10和子进程1.0.2上。来自python的数据是一个字节数组,必须进行转换。这是另一个用python发出http请求的快速示例。

节点

const process = spawn("python", ["services/request.py", "https://www.google.com"])


return new Promise((resolve, reject) =>{
process.stdout.on("data", data =>{
resolve(data.toString()); // <------------ by default converts to utf-8
})
process.stderr.on("data", reject)
})

request.py

import urllib.request
import sys


def karl_morrison_is_a_pedant():
response = urllib.request.urlopen(sys.argv[1])
html = response.read()
print(html)
sys.stdout.flush()


karl_morrison_is_a_pedant()

p.s.不是一个人为的例子,因为节点的http模块不加载我需要做的一些请求

你可以用你的python transpile它,然后像调用javascript一样调用它。我已经成功地为screeps做了这件事,甚至让它在浏览器中运行布立吞人

之前的大多数答案都将承诺的成功称为on(“数据”),这不是正确的方法,因为如果你收到很多数据,你只会得到第一部分。相反,你必须在end事件上做。

const { spawn } = require('child_process');
const pythonDir = (__dirname + "/../pythonCode/"); // Path of python script folder
const python = pythonDir + "pythonEnv/bin/python"; // Path of the Python interpreter


/** remove warning that you don't care about */
function cleanWarning(error) {
return error.replace(/Detector is not able to detect the language reliably.\n/g,"");
}


function callPython(scriptName, args) {
return new Promise(function(success, reject) {
const script = pythonDir + scriptName;
const pyArgs = [script, JSON.stringify(args) ]
const pyprog = spawn(python, pyArgs );
let result = "";
let resultError = "";
pyprog.stdout.on('data', function(data) {
result += data.toString();
});


pyprog.stderr.on('data', (data) => {
resultError += cleanWarning(data.toString());
});


pyprog.stdout.on("end", function(){
if(resultError == "") {
success(JSON.parse(result));
}else{
console.error(`Python error, you can reproduce the error with: \n${python} ${script} ${pyArgs.join(" ")}`);
const error = new Error(resultError);
console.error(error);
reject(resultError);
}
})
});
}
module.exports.callPython = callPython;

电话:

const pythonCaller = require("../core/pythonCaller");
const result = await pythonCaller.callPython("preprocessorSentiment.py", {"thekeyYouwant": value});

python:

try:
argu = json.loads(sys.argv[1])
except:
raise Exception("error while loading argument")

你现在可以使用支持Python和Javascript的RPC库,比如zerorpc

从他们的头版:

node . js的客户

var zerorpc = require("zerorpc");


var client = new zerorpc.Client();
client.connect("tcp://127.0.0.1:4242");


client.invoke("hello", "RPC", function(error, res, more) {
console.log(res);
});

Python服务器

import zerorpc


class HelloRPC(object):
def hello(self, name):
return "Hello, %s" % name


s = zerorpc.Server(HelloRPC())
s.bind("tcp://0.0.0.0:4242")
s.run()
/*eslint-env es6*/
/*global require*/
/*global console*/
var express = require('express');
var app = express();


// Creates a server which runs on port 3000 and
// can be accessed through localhost:3000
app.listen(3000, function() {
console.log('server running on port 3000');
} )


app.get('/name', function(req, res) {


console.log('Running');


// Use child_process.spawn method from
// child_process module and assign it
// to variable spawn
var spawn = require("child_process").spawn;
// Parameters passed in spawn -
// 1. type_of_script
// 2. list containing Path of the script
//    and arguments for the script


// E.g : http://localhost:3000/name?firstname=Levente
var process = spawn('python',['apiTest.py',
req.query.firstname]);


// Takes stdout data from script which executed
// with arguments and send this data to res object
var output = '';
process.stdout.on('data', function(data) {


console.log("Sending Info")
res.end(data.toString('utf8'));
});


console.log(output);
});

这对我很管用。必须将python.exe添加到此代码段的路径变量中。另外,确保你的python脚本在你的项目文件夹中。

Boa很适合您的需求,请参阅在JavaScript中扩展Python tensorflow keras.Sequential类的示例。

const fs = require('fs');
const boa = require('@pipcook/boa');
const { tuple, enumerate } = boa.builtins();


const tf = boa.import('tensorflow');
const tfds = boa.import('tensorflow_datasets');


const { keras } = tf;
const { layers } = keras;


const [
[ train_data, test_data ],
info
] = tfds.load('imdb_reviews/subwords8k', boa.kwargs({
split: tuple([ tfds.Split.TRAIN, tfds.Split.TEST ]),
with_info: true,
as_supervised: true
}));


const encoder = info.features['text'].encoder;
const padded_shapes = tuple([
[ null ], tuple([])
]);
const train_batches = train_data.shuffle(1000)
.padded_batch(10, boa.kwargs({ padded_shapes }));
const test_batches = test_data.shuffle(1000)
.padded_batch(10, boa.kwargs({ padded_shapes }));


const embedding_dim = 16;
const model = keras.Sequential([
layers.Embedding(encoder.vocab_size, embedding_dim),
layers.GlobalAveragePooling1D(),
layers.Dense(16, boa.kwargs({ activation: 'relu' })),
layers.Dense(1, boa.kwargs({ activation: 'sigmoid' }))
]);


model.summary();
model.compile(boa.kwargs({
optimizer: 'adam',
loss: 'binary_crossentropy',
metrics: [ 'accuracy' ]
}));

完整的例子是:https://github.com/alibaba/pipcook/blob/master/example/boa/tf2/word-embedding.js

我在另一个项目pipook中使用了Boa,这是为了解决JavaScript开发人员的机器学习问题,我们通过Boa库在Python生态系统(tensorflow,keras,pytorch)上实现了ML/DL模型。

许多例子都是过时的,并且涉及复杂的设置。你可以尝试JSPyBridge / pythonia(完全披露:我是作者)。它是一种普通的JS,可以让你操作外部Python对象,就好像它们存在于JS中一样。事实上,它实现了互操作性,因此Python代码可以通过回调和传递函数返回调用JS。

numpy + matplotlib的例子,用ES6导入系统:

import { py, python } from 'pythonia'
const np = await python('numpy')
const plot = await python('matplotlib.pyplot')


// Fixing random state for reproducibility
await np.random.seed(19680801)
const [mu, sigma] = [100, 15]
// Inline expression evaluation for operator overloading
const x = await py`${mu} + ${sigma} * ${np.random.randn(10000)}`


// the histogram of the data
const [n, bins, patches] = await plot.hist$(x, 50, { density: true, facecolor: 'g', alpha: 0.75 })
console.log('Distribution', await n) // Always await for all Python access
await plot.show()
python.exit()

通过CommonJS(没有顶级await):

const { py, python } = require('pythonia')
async function main() {
const np = await python('numpy')
const plot = await python('matplotlib.pyplot')
...
// the rest of the code
}
main().then(() => python.exit()) // If you don't call this, the process won't quit by itself.
const util = require('util');
const exec = util.promisify(require('child_process').exec);
    

function runPythonFile() {
const { stdout, stderr } = await exec('py ./path_to_python_file -s asdf -d pqrs');
if (stdout) { // do something }
if (stderr) { // do something }
}

欲了解更多信息,请访问Nodejs官方子进程页面:https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback

你可以在NPM上查看我的包 https://www.npmjs.com/package/@guydev/native-python < / p >

它提供了一种非常简单而强大的方式来从node运行python函数

import { runFunction } from '@guydev/native-python'


const example = async () => {
const input = [1,[1,2,3],{'foo':'bar'}]
const { error, data } = await runFunction('/path/to/file.py','hello_world', '/path/to/python', input)


// error will be null if no error occured.
if (error) {
console.log('Error: ', error)
}


else {
console.log('Success: ', data)
// prints data or null if function has no return value
}
}

python模块

# module: file.py


def hello_world(a,b,c):
print( type(a), a)
# <class 'int'>, 1


print(type(b),b)
# <class 'list'>, [1,2,3]


print(type(c),c)
# <class 'dict'>, {'foo':'bar'}