逆向
页面 URL:https://fanyi.youdao.com/index.html?keyfrom=baiduvipdc#/TextTranslate
翻译的内容不一样,sign
就不一样,还有 mysticTime
也不一样。由于还没有逆向,所以这里的 mysticTime
可以猜测是标准时间。
解密
接下来看调用栈:
搜一下 URL 就可以找到地方了,这里看起来像个 lambda 表达式,我不知道这个在 JS 里叫啥,暂且这么叫他把,我把他写成一个函数,这里有很多逗号表达式,只要取后面的值就好了:
function fn(e, t){
n.H("https://dict.youdao.com/webtranslate", o.A(o.A({}, e), k(t)), {
headers: {"Content-Type": "application/x-www-form-urlencoded"}
}
)
}
然后需要把缺少的函数补全:
n.H
函数如上图,这里可以看到是一个 axios 的 post 请求。这里就是一个标准的 Promise
的异步写法:
n = new Promise(((resolve, reject) => {
post().then(
... // 这里就是resolve,表示请求成功执行
)
catch(...){
... // 这里就是reject,表示请求失败执行
}
}))
有了上面的 axios 前置知识,就可以知道,请求成功了会走 o(e.data)
,所以这里大概率就是解密的地方:
这里其实是请求来的两个值。
加密
在之前请求的地方,组装 post 请求,可以看到组装 payload 的地方,组装的函数中有 sign
的生成函数:
sign
的生成函数如下,注意这里的 _
其实是个函数:
挺好找的,就在上面:
那么这里就可以来实现一下发送请求的 payload:
尝试发送请求,可以得到结果:
有道很多东西都是写死的,最后 js 如下:
const crypto = require('crypto');
// 加密
function _(e) {
return crypto.createHash("md5").update(e.toString()).digest("hex")
}
function S(e) {
return _(`client=fanyideskweb&mysticTime=${e}&product=webfanyi&key=Vy4EQ1uwPkUoqvcP1nIu6WiAjxFeA3Y2`)
}
function k(e, t) {
const a = (new Date).getTime();
return {
sign: S(a, e),
client: 'fanyideskweb',
product: 'webfanyi',
appVersion: '1.0.0',
vendor: 'web',
pointParam: 'client,mysticTime,product',
mysticTime: a,
keyfrom: 'fanyi.web',
mid: 1,
screen: 1,
model: 1,
network: 'wifi',
abtest: 0,
yduuid: t || "abcdefg"
}
}
function a(e, t, n) {
return t = o(t),
t in e ? Object.defineProperty(e, t, {
value: n,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[t] = n,
e
}
function i(e, t) {
var n = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var r = Object.getOwnPropertySymbols(e);
t && (r = r.filter((function(t) {
return Object.getOwnPropertyDescriptor(e, t).enumerable
}
))),
n.push.apply(n, r)
}
return n
}
function o(e) {
for (var t = 1; t < arguments.length; t++) {
var n = null != arguments[t] ? arguments[t] : {};
t % 2 ? i(Object(n), !0).forEach((function(t) {
a(e, t, n[t])
}
)) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : i(Object(n)).forEach((function(t) {
Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t))
}
))
}
return e
}
function fn(word) {
e = {
"dictResult": true,
"from": "auto",
"i": word,
"keyid": "webfanyi",
"to": "",
"useTerm": false
}
return o(o({}, e), k("Vy4EQ1uwPkUoqvcP1nIu6WiAjxFeA3Y2"))
}
// jiemi
function T(e) {
return crypto.createHash("md5").update(e).digest()
}
function _jiemi(e,t,a){
if (!e)
return null;
// 这里查阅官方文档可以知道 createDecipheriv接收的参数,所以可以用Buffer初始化
const o = Buffer.alloc(16, T(t))
, n = Buffer.alloc(16, T(a))
, r = crypto.createDecipheriv("aes-128-cbc", o, n);
let s = r.update(e, "base64", "utf-8");
return s += r.final("utf-8"), s
}
function jiemi(o) {
// da.A.cancelLastGpt();
const a = _jiemi(o, 'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl',
'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4')
, word = a ? JSON.parse(a) : {};
return a;
}
爬虫
import json
import requests
import execjs
f = open("youdao.js", "r", encoding="utf-8")
js_code = f.read()
f.close()
js = execjs.compile(js_code)
headers = {
"Accept": "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Length": "314",
"Content-Type": "application/x-www-form-urlencoded",
"Cookie": "hb_MA-B0D8-94CBE089C042_source=www.baidu.com; OUTFOX_SEARCH_USER_ID=-414266408@39.185.201.22; OUTFOX_SEARCH_USER_ID_NCOO=1106084277.0425632; _uetsid=bba76df0032311f0bfb71bca4d641cc2; _uetvid=bba77a20032311f08d2cd70d07ef4757; DICT_DOCTRANS_SESSION_ID=YzdjYmE4MmItY2I5NC00MDQ5LWI0NzUtOTRmMTIwNGZhYzQ0",
"Host": "dict.youdao.com",
"Origin": "https://fanyi.youdao.com",
"Pragma": "no-cache",
"Referer": "https://fanyi.youdao.com/",
"Sec-Ch-Ua": "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Google Chrome\";v=\"122\"",
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": "\"Windows\"",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36"
}
word = input("输入需要搜索的单词>>")
resp = requests.post("https://dict.youdao.com/webtranslate", headers=headers, data=js.call("fn", word))
res = js.call("jiemi", resp.text)
print(res)
评论