如何从Node.js调用Python函数


208

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


4
节点python。不过,我自己从未使用过它。
univerio

21
两年后,node-python似乎是一个废弃的项目。
imrek


另见github.com/QQuick/Transcrypt,将python编译为javascript然后调用它
Jonathan

Answers:


262

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

然后,您可以执行以下操作:

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

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

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

print(dataToSendBack)
sys.stdout.flush()

然后节点可以使用以下方法侦听数据:

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

由于这允许使用spawn将多个参数传递给脚本,因此您可以重组python脚本,以便其中一个参数决定调用哪个函数,而另一个参数传递给该函数,依此类推。

希望这很清楚。让我知道是否需要澄清。


17
@ PauloS.Abreu:我遇到的问题exec是它返回一个缓冲区而不是流,并且如果您的数据超出了maxBuffer默认值为200kB 的设置,则会出现缓冲区超出异常,并且进程被杀死。由于spawn使用流,因此比更加灵活exec
NeverForgetY2K

2
请注意,如果您使用节点,则可能不应该使用process关键字
alexvicegrab

2
我应该如何安装外部pip依赖项?我需要一个项目的numpy,但由于未安装它而无法使其运行。
javiergarval

2
@javiergarval它将更适合作为新问题而不是评论。
NeverForgetY2K

3
除了打印以外,还有其他方法可以从python返回数据吗?我的python脚本输出了很多日志数据,显然无法刷新所有数据
lxknvlk

111

来自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!'))

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

以下是使用以下命令执行任务的更模块化的方式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!'))

8
我很惊讶这还没有得到更多的投票。虽然@ NeverForgetY2K的答案很好,但此答案包含一个更详细的示例,其中包括端口侦听,并且很好地使用了更现代的JS约定(例如const&promises)。
Mike Williamson

2
很好的例子。答应一个很好的发现我在python脚本上出现的一些错误。
htafoya

38

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

安装: npm install python-shell

运行一个简单的Python脚本:

var PythonShell = require('python-shell');

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

使用参数和选项运行Python脚本:

var PythonShell = require('python-shell');

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



1
如果出现此错误-TypeError:PythonShell.run不是函数,那么请确保像这样输入它var {PythonShell} = require('python-shell');
穆罕默德

4

现在,您可以使用支持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()

您还可以在Node和Python端都使用socket.io
Bruno Gabuzomeu

3

先前的大多数答案都将on(“ data”)中的promise称为成功,这不是正确的方法,因为如果您收到大量数据,您只会得到第一部分。相反,您必须在结束事件上执行此操作。

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});

蟒蛇:

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

2

我在节点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()

ps不是一个人为的例子,因为节点的http模块不会加载我需要发出的一些请求


我有一个在nodejs上构建的服务器后端,并且我有一些与机器学习相关的python脚本,每当我在nodejs服务器上收到请求时,我都会使用通过子代生成的子进程使用子进程生成。如该线程中所建议。我的问题是,这是正确的方法吗?还是我可以使用zmq将python脚本像Flask服务一样运行到绑定到端口的端口上,并从nodejs运行对该服务的Promise。正确的意思是,节省内存和节省速度的方法是哪一种?
阿斯温

1
您可能希望python东西独立运行。您不需要硬编码依赖性,尤其是对于像ml服务这样更复杂的东西。如果您想在此体系结构中添加其他内容,该怎么办?像ml前面的缓存层,还是向ml模型添加额外参数的方法?它也是运行python服务器的内存,但是您可能需要灵活性。稍后,您可以将两部分分离到两台服务器上
1mike12 '18

他问是否可以调用一个函数,但这没有回答问题。
K-SO的毒性在增加。

2
@ 1mike12“ karl_morrison_is_a_pedant()”哈哈喜欢它!
K-SO的毒性在增加。

0

您可以将自己的python进行转换,然后像调用JavaScript一样对其进行调用。我已经成功完成了此步骤,甚至还可以在浏览器中运行la brython


0
/*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脚本位于项目文件夹中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.