杰瑞科技汇

如何在Python中运行JavaScript代码?

核心思想

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

如何在Python中运行JavaScript代码?-图1
(图片来源网络,侵删)

使用 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对象之间转换时需要小心。

示例代码

安装

如何在Python中运行JavaScript代码?-图2
(图片来源网络,侵删)
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脚本。

如何在Python中运行JavaScript代码?-图3
(图片来源网络,侵删)

优点:

  • 性能最佳:直接使用原生的V8引擎,没有中间层损耗。
  • 功能最全:可以使用Node.js生态中所有的包(npm install的包)。
  • 调试方便:可以直接用Node.js的调试工具。

缺点:

  • 依赖Node.js:必须在系统上安装Node.js。
  • 进程开销:每次调用都会启动一个新的Node.js进程,对于高频、轻量的调用可能会有延迟。

示例代码

确保 Node.js 已安装 在终端运行 node -vnpm -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渲染后的页面内容),可以使用 SeleniumPlaywright 这类自动化测试工具,它们可以驱动一个真实的浏览器(如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,但要做好安装困难的准备。
  • 如果你的目标是获取网页上最终显示的内容(比如新闻、商品价格)使用 SeleniumPlaywright
分享:
扫描分享到社交APP
上一篇
下一篇