核心思想
Python 和 JavaScript 是两种完全不同的语言,运行在不同的环境(Python解释器 vs. JavaScript引擎如V8),要在Python中运行JS,需要一个“桥梁”或“翻译官”来连接两者,这个桥梁通常是一个封装了JavaScript引擎(如Google的V8)的Python库。

使用 PyExecJS (最简单、通用)
PyExecJS 是一个非常流行的库,它像一个“适配器”,可以让你使用多种后端来执行JavaScript代码,包括:
- Node.js: 如果你系统里安装了Node.js,这是最快、功能最全的选择。
- PyV8: 一个纯Python的V8引擎绑定,无需Node.js,但安装可能稍复杂。
- PhantomJS / SlimerJS: 无头浏览器环境,可以执行包含DOM操作的JS。
- JavaScriptCore: macOS系统自带。
- JScript: Windows系统自带。
优点:
- 安装简单:
pip install PyExecJS - 使用简单:API非常直观。
- 通用性强:不依赖特定环境,只要有可用的JS引擎就行。
缺点:
- 性能不是最优:相比直接调用V8,它多了一层封装,会有一定的性能开销。
- 数据类型转换:在Python对象和JS对象之间转换时需要小心。
示例代码
安装

pip install PyExecjs
基本使用
import execjs
# 1. 直接执行一段JS代码
ctx = execjs.compile("""
function add(a, b) {
return a + b;
}
function greet(name) {
return 'Hello, ' + name + '!';
}
""")
# 调用JS函数并传入参数
result_add = ctx.call("add", 10, 5)
result_greet = ctx.call("greet", "Python")
print(f"JS add(10, 5) 的结果是: {result_add}") # 输出: JS add(10, 5) 的结果是: 15
print(f"JS greet('Python') 的结果是: {result_greet}") # 输出: JS greet('Python') 的结果是: Hello, Python!
从文件加载JS代码
假设你有一个 my_utils.js 文件:
// my_utils.js
function calculateTax(price, taxRate) {
return price * (taxRate / 100);
}
在Python中调用它:
import execjs
# 从文件加载JS代码
with open('my_utils.js', 'r', encoding='utf-8') as f:
js_code = f.read()
ctx = execjs.compile(js_code)
# 调用函数
price = 100
tax_rate = 8
tax = ctx.call("calculateTax", price, tax_rate)
print(f"价格 {price} 税率为 {tax_rate}% 的税费是: {tax}") # 输出: 价格 100 税率为 8% 的税费是: 8.0
使用 node.js 直接调用 (功能最全、性能最好)
如果你的项目中已经安装了 Node.js,或者你不介意安装它,那么直接通过Python的 subprocess 模块来调用 node 命令是功能最强大、性能最好的方式,这相当于让Python启动一个独立的Node.js进程来执行JS脚本。

优点:
- 性能最佳:直接使用原生的V8引擎,没有中间层损耗。
- 功能最全:可以使用Node.js生态中所有的包(
npm install的包)。 - 调试方便:可以直接用Node.js的调试工具。
缺点:
- 依赖Node.js:必须在系统上安装Node.js。
- 进程开销:每次调用都会启动一个新的Node.js进程,对于高频、轻量的调用可能会有延迟。
示例代码
确保 Node.js 已安装
在终端运行 node -v 和 npm -v 检查是否安装。
编写一个JS脚本 (run.js)
// run.js
// 从命令行参数获取参数
const args = process.argv.slice(2);
// 第一个参数是函数名,后面是函数的参数
const funcName = args[0];
const params = args.slice(1);
// 一个简单的函数库
const functions = {
add: (a, b) => a + b,
toUpperCase: (str) => str.toUpperCase(),
sumArray: (arr) => arr.reduce((sum, num) => sum + num, 0)
};
// 执行对应的函数
if (functions[funcName]) {
const result = functions[funcName](...params.map(arg => {
// 尝试解析数字,否则保持字符串
const num = parseFloat(arg);
return isNaN(num) ? arg : num;
}));
console.log(JSON.stringify(result)); // 输出结果为JSON字符串,方便Python解析
} else {
console.log(JSON.stringify({ error: `Function ${funcName} not found` }));
}
在Python中调用
import subprocess
import json
def run_node_script(func_name, *args):
"""
调用Node.js脚本
:param func_name: 要执行的JS函数名
:param args: 传递给JS函数的参数
:return: JS函数执行后的结果
"""
try:
# 构建命令
command = ['node', 'run.js', func_name] + list(map(str, args))
# 执行命令并捕获输出
result = subprocess.run(command, capture_output=True, text=True, check=True)
# 解析JS输出的JSON字符串
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
print(f"Error executing Node.js script: {e}")
print(f"Stderr: {e.stderr}")
return None
except json.JSONDecodeError:
print("Failed to parse JSON output from Node.js script.")
return None
# --- 调用示例 ---
# 调用 add 函数
result_sum = run_node_script('add', 20, 22)
print(f"JS add(20, 22) 的结果是: {result_sum}") # 输出: JS add(20, 22) 的结果是: 42
# 调用 toUpperCase 函数
result_upper = run_node_script('toUpperCase', 'hello python')
print(f"JS toUpperCase('hello python') 的结果是: {result_upper}") # 输出: JS toUpperCase('hello python') 的结果是: HELLO PYTHON
# 调用 sumArray 函数
# 注意:JS函数需要自己处理数组参数,这里我们通过字符串传递
result_sum_arr = run_node_script('sumArray', 1, 2, 3, 4, 5)
print(f"JS sumArray(1, 2, 3, 4, 5) 的结果是: {result_sum_arr}") # 输出: JS sumArray(1, 2, 3, 4, 5) 的结果是: 15
使用 PyV8 (底层、高性能)
PyV8 是Google V8引擎的Python绑定,它不依赖Node.js,直接将V8引擎嵌入到Python中,因此性能非常高,适合对性能要求苛刻的场景。
优点:
- 性能极高:直接与V8交互,没有中间层。
- 独立运行:不依赖Node.js环境。
缺点:
- 安装复杂:编译安装过程可能很麻烦,尤其是在Windows上。
- 维护不活跃:项目更新可能较慢,与最新V8版本可能有兼容性问题。
- 功能较少:不包含Node.js的API(如
fs,path等模块)。
示例代码
安装 (以Linux/macOS为例)
# 安装依赖 sudo apt-get install python-dev # or python3-dev sudo apt-get install build-essential # 安装PyV8 (通常通过pip,但可能需要编译) pip install PyV8 # 或者从源码编译,请参考其官方文档
使用
import PyV8
# 创建一个JS上下文
with PyV8.JSContext() as env:
# 执行JS代码
env.eval("""
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
""")
# 获取JS函数并调用
factorial_func = env.locals.factorial
result = factorial_func(5)
print(f"JS factorial(5) 的结果是: {result}") # 输出: JS factorial(5) 的结果是: 120
在浏览器环境中运行 (用于Web Scraping/测试)
如果你需要在浏览器环境中执行JS(为了获取经过JS渲染后的页面内容),可以使用 Selenium 或 Playwright 这类自动化测试工具,它们可以驱动一个真实的浏览器(如Chrome, Firefox)或一个无头浏览器(Headless Browser)。
优点:
- 环境真实:可以执行任何浏览器中的JS,包括DOM操作、AJAX请求等。
- 功能强大:适用于复杂的Web自动化任务。
缺点:
- 重量级:需要安装浏览器驱动,启动和运行速度慢。
- 不适合计算:对于纯JS计算任务来说,这是“杀鸡用牛刀”,效率极低。
示例代码 (使用 Selenium + ChromeDriver)
安装
pip install selenium # 下载与你的Chrome浏览器版本匹配的ChromeDriver,并将其添加到系统PATH
使用
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
# 初始化WebDriver (这里使用无头模式)
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
# driver = webdriver.Chrome(service=Service('/path/to/chromedriver'), options=options)
# 为了简化,假设chromedriver在PATH中
driver = webdriver.Chrome(options=options)
try:
# 打开一个包含JS的网页
driver.get("https://example.com") # 或者你自己的本地HTML文件
# 执行一段JS代码,获取页面标题= driver.execute_script("return document.title;")
print(f"通过JS获取到的页面标题是: {title}")
# 执行JS并修改页面内容
driver.execute_script("document.body.innerHTML = '<h1>Hello from Python!</h1>';")
time.sleep(2) # 暂停2秒以便观察
print("页面内容已被JS修改。")
finally:
# 关闭浏览器
driver.quit()
总结与选择建议
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
PyExecJS |
简单、通用、安装方便 | 性能一般,有封装开销 | 快速原型、简单脚本、不想安装Node.js的用户 |
调用 node.js |
性能最好、功能最全 | 依赖Node.js,有进程开销 | 生产环境、需要高性能、使用npm包的复杂项目 |
PyV8 |
性能极高、独立 | 安装复杂,维护不活跃 | 对性能有极致要求的计算密集型任务 |
| Selenium/Playwright | 环境真实、功能强大 | 重量级、速度慢 | Web自动化、爬取JS渲染的页面、端到端测试 |
如何选择?
- 如果你只是想在Python里偶尔跑一小段JS,或者快速验证一个想法:首选
PyExecJS,它最省心。 - 如果你正在开发一个Web服务,后端是Python,但需要用到一些复杂的JS库(比如加密、压缩、或某个特定的算法实现):强烈推荐调用
node.js,这是最稳定、最高效的方案。 - 如果你正在做一个科学计算或数据分析项目,其中一部分算法用JS实现,并且对速度要求极高:可以考虑
PyV8,但要做好安装困难的准备。 - 如果你的目标是获取网页上最终显示的内容(比如新闻、商品价格):使用
Selenium或Playwright。
