node項目中調用執(zhí)行python腳本的方法詳解
一、環(huán)境配置
Windows + PowerShell 命令:
- 在node項目中新建python文件夾,以及子文件夾scripts用于放置python腳本
- 創(chuàng)建虛擬環(huán)境
cd python->python -m venv venv - 進入虛擬環(huán)境并install腳本中的依賴:
venv\Scripts\Activate.ps1-> 單個安裝pip install pandas - 不要把 venv 提交到 git
// .gitignore python/venv/ __pycache__/ *.pyc
只提交依賴清單
執(zhí)行:pip freeze > requirements.txt 提交這個文件即可
服務器部署時重新創(chuàng)建環(huán)境
python -m venv venvpip install -r requirements.txt
二、運行python腳本
方法一:用spawn 子進程通信(不建議)
缺點:頻繁 spawn 會很慢,stdout 容易污染
注意:
- 絕對路徑的拼接,以及腳本里如果有讀取文件代碼時的文件路徑
- Python腳本里不能隨便 print,因為node必須自己保證輸出是 JSON
const { spawn } = require("child_process");
const path = require("path");
const rootPath = path.resolve(__dirname, "..");
// 1?? 定位 Python 解釋器路徑:E:\node\xxx\python\venv\Scripts\python.exe
const pythonPath = path.join(
rootPath,
"python",
"venv",
"Scripts",
"python.exe",
);
// 2?? 定位 Python 腳本路徑:E:\node\xxx\python\scripts\test.py
const scriptPath = path.join(
rootPath,
"python",
"scripts",
"test.py",
);
// ?? 全局執(zhí)行鎖
let isRunning = false;
// 注意腳本文件里訪問文件時的路徑,要用絕對路徑
exports.get_info = (req, res) => {
if (isRunning) {
return res.send({
status: 429,
message: "任務正在執(zhí)行中,請稍后再試",
});
}
isRunning = true;
// 第二個參數(shù)是數(shù)組,表示執(zhí)行哪個腳本,和傳給 Python 的參數(shù)
const py = spawn(pythonPath, [scriptPath]);
let error = "";
py.stderr.on("data", (data) => {
error += data.toString();
});
py.on("error", (err) => {
console.error("啟動失敗:", err);
isRunning = false;
return res.send({
status: 500,
message: "Python 啟動失敗",
error: err.message,
});
});
// 超時殺進程
const timeout = setTimeout(() => {
py.kill("SIGTERM");
}, 300000); // 300秒超時
py.on("close", (code) => {
clearTimeout(timeout);
isRunning = false;
if (code === 0) {
return res.send({
status: 200,
message: "Python腳本執(zhí)行成功",
});
}
console.error("Python執(zhí)行錯誤:", error);
return res.send({
status: 500,
message: "Python執(zhí)行失敗",
error: error || "未知錯誤",
});
});
};
每個請求都 spawn 一個 Python 進程,如果接口被并發(fā)打(比如前端連點多次請求、或者有壓測),就會同時起多個 python.exe 導致內存瞬間爆掉,所以這里限制了單進程:
①在腳本test.py里強制限制線程,放在所有import之前
import os os.environ["OPENBLAS_NUM_THREADS"] = "1" os.environ["OMP_NUM_THREADS"] = "1" os.environ["MKL_NUM_THREADS"] = "1"
②加單進程鎖(不允許并發(fā)執(zhí)行),以及加超時殺進程(防卡死)
同一時間只執(zhí)行一個 Python 腳本,后面的請求直接拒絕,在“單進程 + spawn 模式”下,只要 Python 進程正常退出,內存會被系統(tǒng)回收。
方法二:使用FastAPI(推薦)
用 Python 起一個 API 服務,Node 通過 HTTP 調用,穩(wěn)定性最高,避免:spawn 死鎖、buffer 溢出、JSON污染
Step1:進入虛擬環(huán)境安裝 FastAPI 和 uvicorn
cd python venv\Scripts\Activate.ps1 pip install fastapi "uvicorn[standard]"
Step2:Python 起 API 服務,新建入口文件 api_server.py
# api_server.py
import os
from fastapi import FastAPI
import sys
# 關鍵:讓 python 能識別 scripts 目錄
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(BASE_DIR)
# 從 scripts 引入
from scripts.test import get_test_info
app = FastAPI()
# 注冊路由:
@app.get("/aaa/bbb")
async def ccc():
result = await get_test_info()
return result
Step3:把腳本文件改成接口驅動模式的函數(shù)寫法
比如異步任務時 if __name__ == "__main__": 改成async 函數(shù) async def get_test_info(): 并return json:
return {
"status": 200,
"file": result_file
}
注意:FastAPI 本身已經運行在事件循環(huán)里,不能在腳本文件里 asyncio.run()
Step4:在不同端口啟動這個 FastAPI 應用實例:uvicorn api_server:app --host 0.0.0.0 --port 8000 --reload
Step5:不再 spawn,改成 axios 請求:
const axios = require("axios");
// 全局執(zhí)行鎖
let isRunning = false;
// 注意腳本文件里訪問文件時的路徑,要用絕對路徑
exports.get_info = async (req, res) => {
if (isRunning) {
return res.send({
status: 429,
message: "任務正在執(zhí)行中,請稍后再試",
});
}
isRunning = true;
try {
const response = await axios.get(
"http://127.0.0.1:8000/aaa/bbb",
{
timeout: 300000, // 5分鐘超時
}
);
isRunning = false;
return res.send({
status: 200,
message: "Python腳本執(zhí)行成功",
data: response.data,
});
} catch (error) {
isRunning = false;
return res.send({
status: 500,
message: "FastAPI調用失敗",
error: error.message,
});
}
};
到此這篇關于node項目中調用執(zhí)行python腳本的方法詳解的文章就介紹到這了,更多相關node調用python腳本內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Nodejs進階:如何將圖片轉成datauri嵌入到網(wǎng)頁中去實例
這篇文章主要介紹了Nodejs進階:如何將圖片轉成datauri嵌入到網(wǎng)頁中去,有興趣的可以了解一下。2016-11-11
解決前端報錯“opensslErrorStack:?[?'error:03000086:digital?
這篇文章主要介紹了前端報錯“opensslErrorStack:?[?'error:03000086:digital?envelope?routines::initialization?error'?]”的相關資料,文中將解決的辦法介紹的非常詳細,需要的朋友可以參考下2026-05-05
nodejs發(fā)布靜態(tài)https服務器的方法
這篇文章主要介紹了nodejs發(fā)布靜態(tài)https服務器的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09

