使用 Node.js 模拟执行 JavaScript
在上一节中,我们了解了利用 Python 来模拟 JavaScript 调用的方法,使用的库是 PyExecJS,其执行环境我们选用的也是 Nodejs,但有时候在调用过程中我们会发现这还是有不太方便的地方,而且可能也会出现上一节提及的变量未定义的问题。有没有其他的解决思路呢?
我们模拟执行的是 JavaScript,而且依赖的是 Nodejs,为什么不直接用 NodeJs 来尝试 JavaScript 的执行呢?其实原理上来说这种方案是完全可行的。
本节中,我们就来了解使用 Node.js 来执行 JavaScript 的方法。
准备工作
本节中,我们需要使用 Node.js,请确保已经正确安装好了 Node.js,安装流程可以参考上一节的说明。
安装完成之后,我们应该可以正常使用 node 和 npm 两个命令,如不能使用,请检查 Node.js 的安装情况和环境变量的配置。
模拟执行
本节的案例和上一节完全一样, 我们想要的其实还是计算出每位星球所对应的加密字符串。所以 整体思路其实还是加载 Crypto 库并执行 getToken 方法, 这里我们直接基于 Node.js 来实现。
首先, 还是把 crypto-js.min.js 文件中的内容复制下来, 新建一个 crypto.js 文件并把内容粘贴进去。
然后新建一个 main.js 文件, 其内容如下:
const CryptoJS = require("./crypto");
function getToken(player) {
let key = CryptoJS.enc.utf8.parse("fipFfVsZSIda94hJNKjfloaqyqMZFfImwLt");
const { name, birthday, height, weight } = player;
let base64Name = CryptoJS.enc.Base64.stringify(CryptoJS.enc.utf8.parse(name));
let encrypted = CryptoJS.DES.encrypt(
`${base64Name}|${birthday}|${height}|${weight}`,
key,
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}
);
return encrypted.toString();
}
const player = {
name: "凯文-杜兰特",
image: "durant.png",
birthday: "1988-09-29",
height: "208cm",
weight: "108.9KG",
};
console.log(getToken(player));
这里我们直接使用 Node.js 中的 require 方法导入 crypto.js 这个文件, 然后将其赋值为 CryptoJS 对到这里, 其实就完成了 CryptoJS 的初始化了, 后面我们就可以正常使用 CryptoJS 对象了。
这时候读者可能有些疑惑: 上一节中我们用的 PyExecJS 的底层也是用 Node.js 模拟的呀? 在上一节中, 我们需要修改代码才能完成 CryptoJS 的初始化, 那这次为什么什么都不用修改就能完成 CryptoJS 的初始化呢?
继续回过头来观察 crypto.js 中最开头的定义:
! (function (t, e) {
"object" == typeof exports
? (module.exports = exports = e())
: "function" == typeof define && define.amd
? define([], e)
: (t.CryptoJS = e());
}(this,
function () {
//...
}));
通过上一节的说明我们知道, 在 Node.js 中定义了 exports 这个对象, 它用来将一些对象的定义 导出。这里 "object" == typeof exports 的结果其实就是 true, 所以就执行了 module.exports = exports = e() 这段代码, 这样就相当于把 e() 作为整体导出了, 而这个 e() 其实就对应这后面的整个 function。 function 里面又定义了加密相关的各个实现, 其实就指代整个加密算法库。
既然在 crypto.js 里面声明了两个导出, 那么怎么导入呢? require 就是导入的意思, 导入之后我 们把它赋值给了整个 CryptoJS 变量, 其实它就代表整个 CryptoJS 加密算法库了。
正是因为我们在 Node.js 中有和 exports 配合的 require 的调用并将其赋值给 CryptoJS 变量, 我们才完成了 CryptoJS 的初始化。因此, 后面我们就能调用 CryptoJS 里面的 DES、enc 等各个对象的 方法来进行一些加密和编码操作了。
CryptoJS 初始化完成了, 接下来 getToken 方法其实就是调用 CryptoJS 里面的各个对象的方法, 实现了整个加密流程, 整个逻辑和上一节是一样的。
最后, 传入 player 对象, 然后输出对应的加密字符串即可。
运行 main.js, 命令如下:
node main.js
得到的结果如下:
DG1UMMq1M7QEhHds7IH1ISHMoOI2TtFpwCB4ApPOocFVqptm1FKJFJg9R1UHo2w3Mkw
经过比对,结果和网站上的结果(如图 11-74 所示)是一致的。
图11-74 网站上的结果
这样我们就成功通过 Node.js 完成了整个 JavaScript 的模拟过程。
搭建服务
现在我们其实已经能够使用 Node.js 完成整个加密字符串的生成了,完全用 Node.js 编写爬虫就可以了。
但如果此时我们就想用 Python 来编写整个爬虫,怎么办呢?该怎么和 Node.js 对接呢?很简单,直接使用 Node.js 来把刚才的算法暴露成一个 HTTP 服务就好了,这样的话 Python 直接调用 Node.js 暴露的 HTTP 服务,通过 RequestBody 传入对应的球员信息,然后加密字符串通过 HTTP 的 Response 返回即可。
那么 HTTP 服务用什么来实现呢? Node.js 中最流行的 HTTP 服务框架当属 express 了, 所以这里 我们就选用它来作为 HTTP 服务器。
首先安装 express。在 main.js 所在目录下記运行如下命令:
npm i express
然后改写 main.js 为如下内容:
const cryptojs = require("./crypto");
const express = require("express");
const app = express();
const port = 3000;
app.use(express.json());
function getToken(player) {
let key = cryptojs.enc.utf8.parse("fipFfVsZSIda94hJNKjfloaqyqMZFfImwLt");
const { name, birthday, height, weight } = player;
let base64Name = cryptojs.enc.Base64.stringify(cryptojs.enc.utf8.parse(name));
let encrypted = cryptojs.DES.encrypt(
`${base64Name}|${birthday}|${height}|${weight}`,
key,
{
mode: cryptojs.mode.ECB,
padding: cryptojs.pad.Pkcs7,
}
);
return encrypted.toString();
}
app.post("/", (req, res) => {
const data = req.body;
res.send(getToken(data));
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}!`);
});
这里我们就使用 express 编写了一个服务, 它可以接收一个 POST 请求, Request Body 就是球员信息, 然后返回 getToken 的计算结果作为 Response 的内容。
接下来, 重新运行该脚本:
node main.js
这时候我们可以看到, express 就在本地 3000 端口上运行了。
如果要用 Python 调用的活, 直接使用 requests 调用该 API, 然后传入对应的球员数据即可, 示例如下:
import requests
data = {
"name": "凯文-杜兰特",
"image": "durant.png",
"birthday": "1988-09-29",
"height": "208cm",
"weight": "108.9KG"
}
url = 'http://localhost:3000'
response = requests.post(url, json=data)
print(response.text)
运行结果如下:
DG1uMMq1M7OEhHds7IH1SMHoOI2tFpwCB4ApPPOOcVFqptm1FKjFu9R1uHo2w3Muw
这样我们就成功实现了 Node.js 到 Python 调用的转换,这样爬取到数据之后,我们就可以使用 Python 进行后续的分析、处理操作了。