Cách gọi hàm Python từ Node.js


206

Tôi có một ứng dụng Express Node.js, nhưng tôi cũng có một thuật toán học máy để sử dụng trong Python. Có cách nào tôi có thể gọi các hàm Python từ ứng dụng Node.js của mình để tận dụng sức mạnh của các thư viện máy học không?


4
nút-trăn . Không bao giờ sử dụng nó bản thân mình, mặc dù.
univerio

21
Hai năm sau, node-pythondường như là một dự án bị bỏ hoang.
imrek


Xem thêm github.com/QQuick/Transcrypt để biên dịch python vào javascript và sau đó gọi nó
Jonathan

Câu trả lời:


260

Cách dễ nhất mà tôi biết là sử dụng gói "child_ process" đi kèm với nút.

Sau đó, bạn có thể làm một cái gì đó như:

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

Sau đó, tất cả những gì bạn phải làm là đảm bảo rằng bạn import systrong tập lệnh python của bạn, và sau đó bạn có thể truy cập arg1bằng cách sử dụng sys.argv[1], arg2sử dụng sys.argv[2], v.v.

Để gửi dữ liệu trở lại nút, chỉ cần thực hiện các thao tác sau trong tập lệnh python:

print(dataToSendBack)
sys.stdout.flush()

Và sau đó nút có thể lắng nghe dữ liệu bằng cách sử dụng:

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

Vì điều này cho phép nhiều đối số được chuyển đến một tập lệnh bằng cách sinh ra, bạn có thể cấu trúc lại một tập lệnh python để một trong các đối số quyết định hàm nào sẽ gọi và đối số khác được chuyển đến hàm đó, v.v.

Hy vọng điều này là rõ ràng. Hãy cho tôi biết nếu một cái gì đó cần làm rõ.


16
@ PauloS.Abreu: Tôi có vấn đề với execlà nó trả về một bộ đệm thay vì một dòng suối, và nếu dữ liệu của bạn vượt quá maxBufferthiết lập, mặc định là 200KB, bạn sẽ có được một bộ đệm vượt ngoại lệ và quá trình của bạn sẽ bị chết. Kể từ khi spawnsử dụng luồng, nó linh hoạt hơn exec.
NeverForgetY2K

2
Chỉ cần một lưu ý nhỏ, nếu bạn sử dụng nút, có lẽ bạn không nên sử dụng từ khóa process
alexvicegrab 28/07/17

2
Làm thế nào tôi nên cài đặt phụ thuộc pip bên ngoài? Tôi cần numpy cho một dự án và không thể làm cho nó chạy vì nó không được cài đặt.
javiergarval

2
@javiergarval Điều đó sẽ phù hợp hơn khi là một câu hỏi mới thay vì một bình luận.
NeverForgetY2K

3
Có cách nào khác để trả lại dữ liệu từ python ngoài việc in không? Kịch bản python của tôi xuất ra rất nhiều dữ liệu nhật ký và rõ ràng nó gặp sự cố khi
xóa

111

Ví dụ cho những người từ nền tảng Python và muốn tích hợp mô hình học máy của họ trong ứng dụng Node.js:

Nó sử dụng child_processmô-đun lõi:

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

Nó không yêu cầu sysmô-đun trong tập lệnh Python của bạn.

Dưới đây là một cách thức mô-đun hơn để thực hiện nhiệm vụ bằng cách sử dụng 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
Tôi ngạc nhiên vì điều này đã không nhận được nhiều phiếu hơn. Mặc dù câu trả lời của @ NeverForgetY2K là tốt, nhưng câu trả lời này chứa một ví dụ chi tiết hơn bao gồm cả việc nghe cổng và sử dụng độc đáo các quy ước JS hiện đại hơn như const & hứa.
Mike Williamson

2
Ví dụ tuyệt vời. Promise một là tốt để phát hiện một số lỗi tôi có trên kịch bản python.
htafoya

38

Các python-shellmô-đun bằng extrabaconmột cách đơn giản để chạy script Python từ Node.js với cơ bản, nhưng hiệu quả quá trình liên lạc và xử lý lỗi tốt hơn.

Cài đặt : npm install python-shell .

Chạy một kịch bản Python đơn giản:

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

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

Chạy một kịch bản Python với các đối số và tùy chọn:

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

Để có tài liệu đầy đủ và mã nguồn, hãy xem https://github.com/extrabacon/python-shell


3
Vấn đề này đang ngăn tôi sử dụng nó - github.com/extrabacon/python-shell/issues/179
mhlavacka

1
Nếu bạn gặp phải lỗi này - TypeError: PythonShell.run không phải là một hàm thì hãy đảm bảo bạn nhập nó như thế này var {PythonShell} = quiries ('python-shell');
Mohammed

4

Bây giờ bạn có thể sử dụng các thư viện RPC hỗ trợ Python và Javascript như zerorpc

Từ trang đầu của họ:

Máy khách 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);
});

Máy chủ 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()

Bạn cũng có thể sử dụng socket.io trên cả hai mặt Node và Python.
Bruno Gabuzomeu

3

Hầu hết các câu trả lời trước gọi sự thành công của lời hứa trong phần "dữ liệu", đó không phải là cách thích hợp để thực hiện vì nếu bạn nhận được nhiều dữ liệu, bạn sẽ chỉ nhận được phần đầu tiên. Thay vào đó bạn phải làm điều đó vào sự kiện kết thúc.

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;

Gọi:

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

trăn:

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

2

Tôi đang ở nút 10 và tiến trình con 1.0.2. Dữ liệu từ python là một mảng byte và phải được chuyển đổi. Chỉ là một ví dụ nhanh khác về việc thực hiện một yêu cầu http trong python.

nút

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 không phải là một ví dụ giả định vì mô-đun http của nút không tải một vài yêu cầu tôi cần thực hiện


Tôi có một bản dựng phụ trợ máy chủ trên nodejs và tôi có một vài tập lệnh python liên quan đến máy học mà tôi sinh ra bằng cách sử dụng tiến trình con sinh ra thông qua nodejs bất cứ khi nào tôi nhận được yêu cầu trên máy chủ nodejs của mình. Theo đề xuất trong chủ đề này. Câu hỏi của tôi là, đây có phải là cách làm đúng hay tôi có thể để tập lệnh python của tôi chạy như một dịch vụ bình được liên kết với một cổng bằng zmq và thực hiện lời hứa từ nodejs với dịch vụ này. Theo đúng ý tôi, đó là cách tiết kiệm bộ nhớ và phương pháp tiết kiệm tốc độ?
Aswin

1
Bạn có thể muốn các công cụ python chạy độc lập. Bạn không muốn phụ thuộc mã cứng, đặc biệt đối với dịch vụ ml phức tạp hơn. Điều gì nếu bạn muốn thêm một phần khác vào kiến ​​trúc này? Giống như một lớp bộ đệm ở phía trước của ml, hoặc một cách để thêm các tham số bổ sung cho mô hình ml? Đó cũng là bộ nhớ để chạy máy chủ python, nhưng có lẽ bạn sẽ cần sự linh hoạt. Sau này, bạn có thể tách hai phần thành hai máy chủ
1mike12

Anh ta hỏi nếu anh ta có thể gọi một chức năng , điều này không trả lời câu hỏi.
K - Độc tính trong SO đang tăng lên.

2
@ 1mike12 "karl_morrison_is_a_pedant ()" haha ​​thích nó bạn đời!
K - Độc tính trong SO đang tăng lên.

0

Bạn có thể lấy con trăn của mình, dịch nó và sau đó gọi nó như thể nó là javascript. Tôi đã thực hiện thành công này cho các screeps và thậm chí có nó để chạy trong trình duyệt một 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);
}); 

Điều này làm việc cho tôi. Python.exe của bạn phải được thêm vào cho bạn các biến đường dẫn cho đoạn mã này. Ngoài ra, hãy chắc chắn rằng tập lệnh python của bạn nằm trong thư mục dự án của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.