Cara memanggil fungsi Python dari Node.js

209

Saya memiliki aplikasi Express Node.js, tetapi saya juga memiliki algoritma pembelajaran mesin untuk digunakan dalam Python. Apakah ada cara saya bisa memanggil fungsi Python dari aplikasi Node.js saya untuk memanfaatkan kekuatan perpustakaan pembelajaran mesin?

Genjuro
sumber
4
simpul-python . Namun, tidak pernah menggunakannya sendiri.
univerio
23
Dua tahun kemudian, node-pythontampaknya menjadi proyek yang ditinggalkan.
imrek
Lihat juga github.com/QQuick/Transcrypt untuk mengkompilasi python ke dalam javascript dan memohonnya
Jonathan

Jawaban:

262

Cara termudah yang saya tahu adalah dengan menggunakan paket "child_process" yang dikemas dengan simpul.

Maka Anda dapat melakukan sesuatu seperti:

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

Maka yang harus Anda lakukan adalah memastikan bahwa Anda berada import sysdi skrip python Anda, dan kemudian Anda dapat mengakses arg1menggunakan sys.argv[1], arg2menggunakan sys.argv[2], dan sebagainya.

Untuk mengirim data kembali ke simpul, lakukan hal berikut dalam skrip python:

print(dataToSendBack)
sys.stdout.flush()

Dan kemudian node dapat mendengarkan data menggunakan:

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

Karena ini memungkinkan beberapa argumen untuk diteruskan ke skrip menggunakan spawn, Anda dapat merestrukturisasi skrip python sehingga salah satu argumen memutuskan fungsi mana yang akan dipanggil, dan argumen lain diteruskan ke fungsi itu, dll.

Semoga ini jelas. Beri tahu saya jika ada sesuatu yang perlu diklarifikasi.

NeverForgetY2K
sumber
17
@ PauloS.Abreu: Masalah yang saya miliki execadalah bahwa ia mengembalikan buffer alih-alih streaming, dan jika data Anda melebihi maxBufferpengaturan, yang defaultnya menjadi 200kB, Anda mendapatkan buffer melebihi pengecualian dan proses Anda terbunuh. Karena spawnmenggunakan stream, ini lebih fleksibel daripada exec.
NeverForgetY2K
2
Hanya sebuah catatan kecil, jika Anda menggunakan simpul Anda mungkin tidak boleh menggunakan kata kunci proses
alexvicegrab
2
Bagaimana saya harus menginstal dependensi pip eksternal? Saya perlu numpy untuk sebuah proyek dan tidak dapat menjalankannya karena tidak diinstal.
javiergarval
2
@javiergarval Itu akan lebih cocok sebagai pertanyaan baru daripada komentar.
NeverForgetY2K
3
apakah ada cara lain untuk mengembalikan data dari python selain dengan mencetak? Skrip python saya mengeluarkan banyak data log dan tampaknya ia mengalami kesulitan untuk membuang semua data itu
lxknvlk
112

Contoh untuk orang-orang yang berasal dari latar belakang Python dan ingin mengintegrasikan model pembelajaran mesin mereka dalam aplikasi Node.js:

Ini menggunakan child_processmodul inti:

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

Itu tidak memerlukan sysmodul dalam skrip Python Anda.

Di bawah ini adalah cara yang lebih modular untuk melakukan tugas menggunakan 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!'))
Amit Upadhyay
sumber
8
Saya terkejut ini belum mendapatkan suara lebih banyak. Sementara jawaban NeverForgetY2K baik-baik saja, jawaban ini berisi contoh yang lebih rinci termasuk mendengarkan port, dan dengan baik menggunakan konvensi JS yang lebih modern seperti const & janji.
Mike Williamson
2
Contoh yang bagus Janji satu baik untuk mendeteksi beberapa kesalahan yang saya miliki pada skrip python.
htafoya
38

The python-shellmodul olehextrabacon adalah cara sederhana untuk menjalankan script Python dari Node.js dengan dasar, tapi efisien antar-proses komunikasi dan penanganan kesalahan yang lebih baik.

Instalasi: npm install python-shell .

Menjalankan skrip Python sederhana:

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

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

Menjalankan skrip Python dengan argumen dan opsi:

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

Untuk dokumentasi lengkap dan kode sumber, periksa https://github.com/extrabacon/python-shell

Soumik Rakshit
sumber
3
Masalah ini menghentikan saya untuk menggunakannya - github.com/extrabacon/python-shell/issues/179
mhlavacka
1
Jika Anda mendapatkan kesalahan ini - TypeError: PythonShell.run bukan fungsi Maka pastikan Anda mengimpornya seperti ini var {PythonShell} = membutuhkan ('python-shell');
Mohammed
4

Anda sekarang dapat menggunakan pustaka RPC yang mendukung Python dan Javascript seperti zerorpc

Dari halaman depan mereka:

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

Server 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()
Geordie
sumber
Anda juga dapat menggunakan socket.io di sisi Node dan Python.
Bruno Gabuzomeu
3

Sebagian besar jawaban sebelumnya menyebut keberhasilan janji di atas ("data"), itu bukan cara yang tepat untuk melakukannya karena jika Anda menerima banyak data, Anda hanya akan mendapatkan bagian pertama. Sebaliknya Anda harus melakukannya di acara akhir.

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;

Panggilan:

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")
bormat
sumber
2

Saya berada di simpul 10 dan proses anak 1.0.2. Data dari python adalah array byte dan harus dikonversi. Hanya contoh cepat membuat permintaan http dengan python.

simpul

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 bukan contoh buat karena modul http node tidak memuat beberapa permintaan saya perlu membuat

1mike12
sumber
Saya memiliki backend server yang dibangun di atas nodejs dan saya memiliki beberapa mesin yang berhubungan dengan skrip python terkait yang saya spawn menggunakan proses anak menelurkan nodejs setiap kali saya menerima permintaan pada server nodejs saya. Seperti yang disarankan di utas ini. Pertanyaan saya adalah, apakah ini cara yang tepat untuk melakukannya atau saya dapat menjalankan skrip python saya seperti layanan labu diikat ke port menggunakan zmq dan menjalankan janji dari nodejs ke layanan ini. Apa yang saya maksud adalah, mana cara penghematan memori dan metode penghematan kecepatan?
Aswin
1
Anda mungkin ingin agar python berjalan secara independen. Anda tidak ingin dependensi kode keras, terutama untuk sesuatu yang lebih rumit sebagai layanan ml. Bagaimana jika Anda ingin menambahkan bagian lain ke arsitektur ini? Seperti lapisan caching di depan ml, atau cara menambahkan parameter ekstra ke model ml? Ini memori untuk menjalankan server python juga, tetapi Anda mungkin perlu fleksibilitas. Kemudian Anda dapat memisahkan dua potong menjadi dua server
1mike12
Dia bertanya apakah dia bisa memanggil fungsi , ini tidak menjawab pertanyaan.
K - Keracunan dalam SO tumbuh.
2
@ 1mike12 "karl_morrison_is_a_pedant ()" haha ​​suka sobat!
K - Keracunan dalam SO tumbuh.
0

Anda bisa mengambil python, mentranspilasikannya , dan kemudian menyebutnya seolah-olah itu javascript. Saya telah melakukan ini dengan sukses untuk screeps dan bahkan membuatnya berjalan di browser ala brython .

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

Ini berhasil untuk saya. Python.exe Anda harus ditambahkan ke variabel path Anda untuk cuplikan kode ini. Juga, pastikan skrip python Anda ada di folder proyek Anda.

nas_levente
sumber