【Python 使用 subprocess 调用本地 Node.js 脚本、互通数据完整教程】

Python 使用 subprocess 调用本地 Node.js 脚本、互通数据完整教程

核心原理

  1. Python 通过 subprocess 新建子进程执行 node xxx.js
  2. JS 脚本通过 标准输出 stdout 把数据传给 Python(推荐 JSON 序列化,支持对象 / 数组 / 数字)
  3. Python 读取 stdout 字符串,json.loads() 解析拿到结构化数据
  4. 双向传参:Python 可通过命令行参数标准输入 stdin 向 JS 传递数据

环境前提

  • 本地已安装 Node.js,终端输入 node -v 能正常输出版本号
  • Python 3.x,内置 subprocessjson 无需额外安装

下面使用python与javascript双向数据互通的示例,来演示本篇文章的学习:

demo.js

function test(a, b, c) {
    return a + b + c
};

// 接受subprocess传来的参数
arg1 = process.argv.slice(2)[0]
arg2 = process.argv.slice(2)[1]
arg3 = process.argv.slice(2)[2]

console.log(test(arg1, arg2, arg3))

demo.py

import subprocess

arg1 = 'python'
arg2 = '调用'
arg3 = 'js'

js_result = subprocess.run(['node', 'demo.js', arg1, arg2, arg3], capture_output=True, text=True, encoding='utf8')

# 手动判断退出码
if js_result.returncode == 0:
    print("标准输出:\n", js_result.stdout)
else:
    print("错误输出:\n", js_result.stderr)

在deomo.js和demo.py文件中,py文件向js文件中的代码传递了arg1,arg2,arg3三个参数,而js文件向py文件中的代码传递了console.log(......)数据。

image

看了以上示例,就知道这里面的关键传输渠道就是subprocess.run,所以,下面我们就来学习一下subprocess.run:

subprocess.run(
    args=["node", "xxx.js"],  # 命令列表,推荐列表形式避免shell注入漏洞
    capture_output=True,      # 捕获stdout和stderr
    stdout=subprocess.PIPE,   # 单独捕获标准输出
    stderr=subprocess.PIPE,   # 单独捕获错误输出
    text=True / universal_newlines=True, # 输出为字符串,否则是字节流
    input="xxx",              # 写入子进程标准输入
    timeout=10,                # 超时时间,秒
    shell=False,              # 默认False,安全;True会启动shell,有注入风险
    cwd="./scripts"           # 指定node脚本所在工作目录,文件路径可写相对路径
)

方法详解

  • ['node', 'demo.js', 'arg1', 'arg2', 'args3']: 命令和参数的列表。node 是命令,可以传node(会在系统变量中寻找node.exe),也可以传node.exe的所在路径,demo.js 是要执行的文件,arg1arg2arg3 是传递给 JS 脚本的参数,命令和参数的类型必须是字符串,否则报错。

  • capture_output=True: 捕获子进程的标准输出 (stdout) 和标准错误 (stderr)。而不是出错直接在控制台输出

  • text=True: 将捕获的输出从字节(bytes)解码为字符串(str)

  • check

    • True:若子进程返回非零退出码(表示错误),自动抛出 CalledProcessError 异常。
    • False(默认):不检查退出码,需手动判断 result.returncode
  • timeout:设置超时时间(秒),超时后终止子进程并抛出异常。

  • input:传递给子进程的输入内容(需配合 text=True 使用字符串,否则用 bytes)。

capture_output=True 等价于同时设置 stdout=subprocess.PIPE 和 stderr=subprocess.PIPE,所以即使你没手动写 stdout=subprocess.PIPE,底层也自动开启了标准输出捕获,自然也能拿到 JS 的 console.log 内容。

进阶 1:Python 传参数给 JS(命令行传参)

 

1、核心原理

subprocess 执行命令时,参数放在列表 [],列表第二个元素是执行程序,后面全部都是传给 Node 的参数,会被 process.argv 读取。
 
process.argv 说明:

 

process.argv[0] → node 程序路径
process.argv[1] → 执行的js文件路径
process.argv[2]、[3]... → Python 传入的自定义参数

2、标准语法格式

Python 传参格式

 

subprocess.run(["node", "脚本.js", "参数1", "参数2", "数字3"])

多类型通用:字符串、数字全部转字符串传入。

3、示例 1:基础字符串 + 数字传参

js 文件 run.js

 

// 获取命令行传入参数
let name = process.argv[2];
let age = Number(process.argv[3]);

// 返回结果
const res = {
  user: name,
  age: age,
  total: age * 10
};
// 输出JSON给Python
console.log(JSON.stringify(res));

Python 调用 main.py 

 

import subprocess
import json

# 向js传递参数:姓名、年龄
cmd = [
    "node",
    "run.js",
    "张三",
    "25"
]

# 执行并读取输出
result = subprocess.run(cmd, capture_output=True, text=True)
data = json.loads(result.stdout)
print(data)
输出结果:
{"user": "张三", "age": 25, "total": 250}

 

4、示例 2:一次性传递多个参数(数组批量传参)

Python 多参数

import subprocess, json

# 多参数统一打包
params = ["小明", "18", "北京", "99"]
cmd = ["node", "run.js", *params]

res = subprocess.run(cmd, capture_output=True, text=True)
print(json.loads(res.stdout))
 

js 读取多个参数

let name = process.argv[2];
let age = process.argv[3];
let city = process.argv[4];
let score = process.argv[5];

let obj = { name, age, city, score };
console.log(JSON.stringify(obj));

进阶 2:传递复杂对象(stdin 标准输入传 JSON)

命令行参数只能传字符串,复杂数组 / 对象建议通过标准输入传给 JS
核心原理:进程间标准输入输出只支持字符串,复杂对象统一序列化为 JSON 字符串传输
 
两种交互场景:
  1. JS 作为主进程调用 Python 子进程(JS 写 stdin,读 stdout)
  2. Python 作为主进程调用 JS 子进程(Python 写 stdin,读 stdout)

基础 API 总览

1. Python stdin/stdout 操作

模块

  • sys.stdin:标准输入(读)
  • sys.stdout:标准输出(写)
  • json:序列化 / 反序列化复杂对象(字典、列表、嵌套对象)
  • subprocess:创建子进程调用 Node/Python

 在 Python 中操作标准输入(stdin)、标准输出(stdout)和标准错误(stderr)是日常开发中非常基础且重要的技能。

读取 stdin

import sys

# 1. 逐行读取(适合处理大文件或管道输入)
for line in sys.stdin:
    print(f"收到: {line.strip()}")
    if line.strip() == "exit":
        break

# 2. 读取所有内容(直到 EOF,适合管道 `cat file.txt | python script.py`)
# content = sys.stdin.read()

# 3. 读取指定字节数
# chunk = sys.stdin.read(1024)

写入 stdout / stderr

import sys

# 写入标准输出(注意:需要手动加 \n,且写入的必须是字符串)
sys.stdout.write("这是一条标准输出\n")
sys.stdout.flush()  # 强制刷新缓冲区(在重定向到文件时尤为重要)

# 写入标准错误(通常用于报错信息,避免污染正常的输出流)
sys.stderr.write("发生了一个错误!\n")

2. Node.js stdin/stdout 操作

模块

  • process.stdin:标准输入流
  • process.stdout:标准输出流
  • JSON.parse / JSON.stringify:序列化对象
process.stdin.on 是 Node.js 中用于监听标准输入流(Readable Stream)事件的方法。因为 process.stdin 本质上是一个可读流,你可以通过它来监听不同的生命周期事件,从而以事件驱动的方式处理输入数据。
以下是 process.stdin.on 最常监听的几个核心事件及其实战用法:

1. 监听 data 事件(最常用)

当你需要实时接收并处理输入数据块时,监听 data 事件是最直接的方式。注意: 一旦绑定了 data 事件,流会自动从暂停模式切换到流动模式。
javascript
// 建议先设置编码,否则读取到的是 Buffer 对象
process.stdin.setEncoding('utf8');

process.stdin.on('data', (chunk) => {
  console.log(`接收到数据块: ${chunk}`);
});

2. 监听 end 事件

当输入流结束时触发(例如在终端中按下 Ctrl+D,或管道输入完毕)。通常与 data 事件配合使用,用于执行清理操作或输出最终结果。
javascript
let fullData = '';

process.stdin.setEncoding('utf8');

process.stdin.on('data', (chunk) => {
  fullData += chunk;
});

process.stdin.on('end', () => {
  console.log('输入结束,完整数据为:', fullData);
});

3. 监听 readable 事件(手动拉取模式)

如果你不想使用自动的流动模式,而是希望精确控制何时读取数据,可以监听 readable 事件,并在内部手动调用 read() 方法。
javascript
process.stdin.on('readable', () => {
  let chunk;
  // 使用 while 循环确保把缓冲区内的数据全部读完
  while ((chunk = process.stdin.read()) !== null) {
    console.log(`手动读取到: ${chunk.toString()}`);
  }
});

4. 监听 error 事件

用于捕获输入流在读取过程中发生的异常,防止程序意外崩溃。
javascript
process.stdin.on('error', (err) => {
  console.error('标准输入发生错误:', err.message);
});

 

 

 

 

 

 

 

 

 

 

 

posted @ 2026-06-16 18:30  chenlight  阅读(12)  评论(0)    收藏  举报