pyexecjs基本不可用,因其依赖已废弃的phantomjs或低版本node.js,不支持es6+语法、无法模拟浏览器全局对象(如window、location),且不兼容模块机制与现代加密所需特性。

PyExecJS 早已停止维护,execjs 库在 Python 3.8+ 上默认无法加载 Node.js 运行时,且不支持现代 JS 特性(如 let、const、箭头函数、模块语法),直接用于 JS 逆向大概率失败。
为什么 PyExecJS 在 JS 逆向中基本不可用
它依赖系统已安装的 JS 运行时(如 Node.js、PhantomJS),但:
- PhantomJS 已废弃,不支持 ES6+,遇到
async/await或class直接报SyntaxError - Node.js 版本若低于 v12,
BigInt、?.、??等常见加密逻辑中的语法会解析失败 -
execjs.eval()无法模拟浏览器环境(window、navigator、document缺失),而多数加密函数依赖这些对象或其属性(如navigator.userAgent、location.href) - 无法注入 polyfill 或 mock 全局变量,
require、module.exports等模块机制也完全不兼容
替代方案:用 execjs + node —— 但必须手动补环境
如果你坚持用 execjs(比如已有旧脚本),唯一可行路径是用 Node.js 后端 + 手动注入最小浏览器上下文。关键不是“调用 JS”,而是“让 JS 认为自己在浏览器里运行”:
- 先确保系统装有 Node.js(推荐 v16+),并能被
execjs自动发现:execjs.runtime.name应返回"Node" - 用
execjs.compile()加载 JS 前,把必要全局变量拼成字符串注入(不是靠execjs.exec()单独执行,而是混入源码) - 例如目标 JS 用了
location.href和Math.random(),需提前构造:"var location = {href: 'https://example.com'}; var Math = global.Math;" - 避免直接传入含
import/export的代码;用babel提前转成 CommonJS,或用eval包一层:(function(){/* 原始代码 */}).call(this)
更靠谱的做法:改用 PyMiniRacer 或 Node.js subprocess
PyMiniRacer 是基于 V8 的 Python 绑定,支持完整 ES2022,且可显式设置 window 对象:
from py_mini_racer import MiniRacer
ctx = MiniRacer().create_context()
ctx.eval("var window = {}; window.location = {href: 'https://a.com'};")
ctx.eval("function enc(s) { return btoa(s); }; enc('test')") # → "dGVzdA=="
或者绕过 Python JS 桥接,用 subprocess 调 Node.js 脚本:
- 把加密逻辑写成独立
encrypt.js,开头const { argv } = process;读取参数 - Python 中用
subprocess.run(['node', 'encrypt.js', 'input'], capture_output=True, text=True) - 这样完全复用真实 Node 环境,支持
crypto、Buffer、zlib等原生模块,也方便调试
真正卡住逆向的从来不是“怎么执行 JS”,而是“JS 依赖哪些隐式上下文”。PyExecJS 连 atob 都要手动 polyfill,不如一开始就切到可控环境。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











