python调用js脚本
我干过一件蠢事:用python爬一个加密接口,调用对方前端代码里有个AES解密函数,脚本我硬是调用用python重写了整个加密逻辑。折腾两小时,脚本最后发现少了个padding参数,调用数据全乱码。脚本后来学乖了——直接拿python调那段js,调用十行代码解决战斗。脚本
这事儿的调用本质是:python和js各有各的生态,很多加密、脚本签名逻辑已经写死在js里,调用你没必要用python再造轮子。脚本尤其是调用那些依赖浏览器环境的库(比如某些webpack打包后的模块),纯python模拟起来痛苦得要命。脚本
最粗暴的调用方式:subprocess + node
如果你机器上装了node,那最简单。把js脚本存成文件,用subprocess调node执行,传参靠stdin或命令行参数,输出靠stdout。
import subprocess, json result = subprocess.run( ['node', 'encrypt.js', 'hello'], capture_output=True, text=True, encoding='utf-8' ) print(result.stdout) # 拿到js打印的结果 这招适合一次性调用,但每次启动node进程开销不小。你要是循环调几百次,光进程启动时间就够你喝一壶的。我试过在循环里调node,1000次请求硬是跑了40秒,后来换方案才压到3秒。
正经办法:pyexecjs
pyexecjs是个老牌库,它帮你管理一个js运行时,不用频繁启进程。用法很直白:
import execjs ctx = execjs.compile(""" function add(a, b) { return a + b; } """) print(ctx.call("add", 3, 4)) # 7 默认它用的是系统里的js引擎(Windows上可能是JScript,Linux上没装node的话会报错)。所以你得先装node,然后execjs会自动优先用node。这货有个坑——它内部用eval执行js字符串,如果你的js代码里有中文注释或者特殊字符,可能编码会出问题。我的经验是:把js代码里所有中文注释删干净再喂给它,否则报错你根本看不懂。
进阶:py_mini_racer,快得离谱
后来我发现了py_mini_racer,这玩意儿直接内嵌了V8引擎,不用调外部进程,速度比pyexecjs快一个量级。安装也简单:
pip install py_mini_racer 用起来几乎一样:
from py_mini_racer import MiniRacer ctx = MiniRacer() ctx.eval(""" function decrypt(data) { return CryptoJS.AES.decrypt(data, 'secret').toString(CryptoJS.enc.Utf8); } """) # 但注意,它默认没有CryptoJS,你得自己把整个库的源码拼进去 这里有个关键点:py_mini_racer不提供浏览器环境,没有window、document、localStorage这些。你调用的js必须纯逻辑,不能依赖DOM。我遇到过最恶心的场景——某个加密库内部偷偷用了`window.btoa`,结果在V8里直接报undefined。解决办法是手动在js代码顶部垫一层polyfill:
globalThis.window = globalThis; globalThis.btoa = (str) => Buffer.from(str, 'binary').toString('base64'); // 然后才是你的业务js 这种补丁打多了你会觉得自己在给V8做移植手术,但确实管用。
最后聊个容易翻车的细节:异步js
很多js加密函数是异步的,比如用`async/await`或者返回Promise。python这边是同步的,直接调用会拿不到结果。py_mini_racer有个`call`方法支持传一个回调函数,但用起来别扭。我的土办法是——把异步函数包一层同步逻辑,用`deasync`或者`child_process`强行同步化。比如js里原本是`await getToken()`,你就改成用`child_process.spawnSync`去跑一个独立的node脚本,把异步结果通过文件或stdout传回来。丑是丑了点,但稳。
说到底,python调js不是什么高深技术,核心就一句话:找到你的js运行环境,把数据塞进去,把结果接出来。别想着在python里模拟一切,该用node就用node,该垫polyfill就垫。我现在的习惯是——凡是遇到复杂加密,先花10分钟试试py_mini_racer能不能直接跑,跑不通就退回subprocess方式,再不行就考虑用selenium直接驱动浏览器。反正能跑就行,别纠结优雅。
哦对了,最后提醒一句:调js之前先确认你确实有权限用那段js代码,别为了爬数据把自己搞进法律纠纷里。技术无罪,但用技术的人得长点心。
