如何从Node.js调用Python函数

时间:2014-05-03 22:35:14

标签: python node.js express

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

11 个答案:

答案 0 :(得分:167)

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

然后你可以做类似的事情:

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

然后你需要做的就是确保你的python脚本中有import sys,然后你可以使用arg1sys.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脚本,以便其中一个参数决定调用哪个函数,另一个参数传递给该函数,等等。

希望这很清楚。如果有需要澄清,请告诉我。

答案 1 :(得分:61)

示例适用于来自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!'))

答案 2 :(得分:20)

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

答案 3 :(得分:12)

Node.js的子进程模块提供了以非JavaScript语言(如Python)运行脚本或命令的功能。我们可以将机器学习算法,深度学习算法和许多通过Python库提供的功能实现到Node.js应用程序中。子进程模块允许我们在Node.js应用程序中运行Python脚本,并将数据流入/流出Python脚本。

child_process.spawn():这个方法可以帮助我们异步地生成子进程。

让我们创建一个简单的Python脚本,它将两个命令行参数作为名字和姓氏,然后显示它们。稍后我们将从Node.js应用程序运行该脚本并在浏览器窗口中显示输出。

Python脚本:

import sys
# Takes first name and last name via command
# line arguments and then display them
print("Output from Python")
print("First name: " + sys.argv[1])
print("Last name: " + sys.argv[2])

# Save the script as hello.py

Node.js服务器代码:

// Import Express.js JavaScript module into the application
// and creates its variable.
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');
} )

// Function callName() is executed whenever
// the URL is of the form localhost:3000/name
app.get('/name', callName);

function callName(req, res) {

    // 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=Mike&lastname=Will
    // So, first name = Mike and last name = Will
    var process = spawn('python',["./hello.py",
                            req.query.firstname,
                            req.query.lastname] );

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

// Save code as start.js

保存Python脚本和服务器脚本代码后,通过以下命令从其源文件夹运行代码:

node start.js

答案 4 :(得分:9)

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

在他们的首页上:

Node.js客户端

function App() {
  return (     
      <Switch>
          <Route exact path="/dashboard" component={Dashboard} />
          <Route path="/" component={Wrapper} />
      </Switch>

  );
}

Python服务器

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

答案 5 :(得分:1)

我在节点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模块不会加载我需要发出的一些请求

答案 6 :(得分:1)

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

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")

答案 7 :(得分:1)

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

答案 8 :(得分:1)

许多示例已经过时多年并且涉及复杂的设置。您可以尝试一下 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(没有顶级等待):

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.

答案 9 :(得分:0)

您可以将Python transpile拿来,然后像使用JavaScript一样调用它。我已经成功完成了此步骤,甚至还可以使其在浏览器中运行,例如brython

答案 10 :(得分:0)

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://jsfiddle.net/lalji1051/moafbcn5/6/

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