Node.jsからPython関数を呼び出す方法


208

Express Node.jsアプリケーションがありますが、Pythonで使用する機械学習アルゴリズムもあります。Node.jsアプリケーションからPython関数を呼び出して、機械学習ライブラリの機能を利用する方法はありますか?


4
node-python。ただし、自分で使用したことはありません。
univerio 2014年

22
2年後、node-python放棄されたプロジェクトのようです。
imrek 2016年


pythonをjavascriptにコンパイルして起動するには、github.com / QQuick / Transcryptも参照してください
Jonathan

回答:


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スクリプトを再構成して、引数の1つが呼び出す関数を決定し、他の引数がその関数に渡されるようにすることができます。

これが明確であることを願っています。何か説明が必要な場合はお知らせください。


17
@ PauloS.Abreu:私が抱えている問題execは、ストリームではなくバッファーを返すことです。データがmaxBuffer設定(デフォルトでは200kB)を超えると、バッファー超過の例外が発生し、プロセスが強制終了されます。spawnはストリームを使用するため、よりも柔軟性がありexecます。
NeverForgetY2K 2016年

2
ちょっとしたメモですが、ノードを使用する場合は、おそらくprocessキーワードを使用しないでください
alexvicegrab '28

2
外部のpip依存関係をインストールするにはどうすればよいですか?プロジェクトにnumpyが必要ですが、インストールされていないため実行できません。
javiergarval

2
@javiergarvalコメントではなく、新しい質問の方が適しています。
NeverForgetY2K

3
印刷以外の方法でPythonからデータを返す方法はありますか?私のpythonスクリプトは大量のログデータを出力し、そのすべてのデータをフラッシュするのに問題があるようです
lxknvlk

112

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

sysPythonスクリプトにモジュールは必要ありません。

以下は、を使用してタスクを実行するよりモジュール化された方法です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の回答は問題ありませんが、この回答にはポートリスニングを含むより詳細な例が含まれており、constやpromiseなどの最新のJS規則をうまく使用しています。
マイクウィリアムソン

2
素晴らしい例です。1つは、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を確認してください。


3
この問題が原因で
mhlavacka

1
このエラーが発生した場合-TypeError:PythonShell.runは関数ではないため、次のようにインポートしてください。var {PythonShell} = require( 'python-shell');
Mohammed

4

PythonとzerorpcなどのJavascriptをサポートするRPCライブラリを使用できるようになりました

彼らのフロントページから:

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

python:

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

2

私はノード10と子プロセスにいます1.0.2。Pythonからのデータはバイト配列であり、変換する必要があります。Pythonでhttpリクエストを行うもう1つの簡単な例。

ノード

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

ノードのhttpモジュールは私がする必要があるいくつかのリクエストをロードしないので、psは不自然な例ではありません


私はnodejsにサーバーバックエンドビルドを持っていますが、nodejsサーバーでリクエストを受信するたびにnodejsを介して子プロセスspawnを使用して生成するいくつかの機械学習関連のpythonスクリプトがあります。このスレッドで提案されているように。私の質問は、これは正しい方法ですか、それともPythonスクリプトをフラスコサービスのように実行して、zmqを使用してポートにバインドし、nodejsからこのサービスへのプロミスを実行することです。正確には、メモリの節約と速度の節約の方法はどちらですか。
アスウィン

1
おそらく、Pythonのものを個別に実行する必要があります。ハードコードの依存関係、特にmlサービスのように複雑なものは必要ありません。このアーキテクチャに別の要素を追加したい場合はどうなりますか?mlの前のキャッシングレイヤー、またはmlモデルに追加のパラメーターを追加する方法のように?Pythonサーバーを実行するのもメモリですが、おそらく柔軟性が必要になります。後で2つの部分を2つのサーバーに分離できます
1mike12

彼は関数を呼び出すことができるかどうか尋ねました、これは質問に答えません。
K-SOの毒性が高まっています。

2
@ 1mike12 "karl_morrison_is_a_pedant()"ハハハハハハハハハハハ!
K-SOの毒性が高まっています。

0

あなたはあなたのPythonを取り、それをトランスパイルし、それをjavascriptであるかのように呼び出すことができます。私はゾッとのためにこれをうまくやった、そしてそれをブラウザでa 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スクリプトがプロジェクトフォルダにあることを確認してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.