本文只做技术交流,若有冒犯请告知我撤除
百度翻译不知道啥时候开始转向收费的形式,开发者可以免费享受200w字符/月的福利
废话少说,入主题
参数分析
参数名 | 参数解释 |
---|---|
from | 要翻译的原文语种 |
to | 翻译结果的目标语种 |
query | 要翻译的内容 |
transtype | 翻译类型(该参数目前发现有 realtime 和 translang 两种,我们使用 realtime 即可) |
simple_means_flag | 未知 |
接口更新后上述链接会返回如下内容
旧版返回结果出现错误码 998,这里暂时不对错误码进行解释
我们重新看看新的接口
通过 Chrome Dev Tools 可以看到,新的请求如下
除了原有的5个参数之外,新增了两个参数
参数名 | 参数解释 |
---|---|
sign | 签名(如:719145.924184) |
token | 请求令牌(如:3a1ae6d96bdd2a8e0f2eb367cb23cb83) |
对谷歌翻译接口有研究的大神应该不难发现,sign 的加密后的值与 Google 的翻译加密后的值非常类似,赶紧看看呗
回到 Chrome Dev Tools,观察新的请求发现翻译页面界面总共加载了 8 个 JavaScript 文件,其中最值得我们注意的就是 index_a6525c5.js 该文件了,不要问我为什么,男人的直觉
根据翻译接口 v2transapi 可以查到一段迷之代码
langIsDeteced: function(t, a, n, r) {
if (null !== t) {
var s = $(".select-from-language .language-selected").attr("data-lang"),
o = $(".select-to-language .language-selected").attr("data-lang"),
l = null;
r && !c.get("fromLangIsAuto") && s !== t ? l = i.processOcrLang(t, s, o) : (e.show(t, s), l = i.getLang(t, s, o)),
u.show();
var g = this,
a = this.processQuery(a),
d = {
from: l.fromLang,
to: l.toLang,
query: a,
transtype: n,
simple_means_flag: 3,
sign: h(a),
token: window.common.token
};
this.translateXHR && 4 !== this.translateXHR.readyState && this.translateXHR.abort(),
this.translateXHR = $.ajax({
type: "POST",
url: "/v2transapi",
cache: !1,
data: d
}).done(function(t) {
c.set("isInRtTransState", !0),
g.translateSuccess(t, l.fromLang, l.toLang, a)
})
}
}
很明显可以看出就是翻译的核心代码,其中 token 的值直接通过 window.common.token
获取即可,稍微要费点心思的就是 sign 的值,通过 h(a)
获取的
其中 a 是传入的待翻译的内容,那么很容易猜到 h()
这个函数会返回 719145.924184
类似这样的结果,并且多次尝试发现,a 的值固定的情况下,h()
的返回值也是固定的,这样也方便大家测试
因为代码加密过,需要男人的直觉给我们更多的指引..
看这段代码
define("translation:widget/translate/input/pGrab",
function(r, o, t) {
"use strict";
function a(r, o) {
for (var t = 0; t < o.length - 2; t += 3) {
var a = o.charAt(t + 2);
a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a),
a = "+" === o.charAt(t + 1) ? r >>> a: r << a,
r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a
}
return r
}
function n(r) {
var o = r.length;
o > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(o / 2) - 5, 10) + r.substr( - 10, 10));
var t = void 0,
n = "" + String.fromCharCode(103) + String.fromCharCode(116) + String.fromCharCode(107);
t = null !== C ? C: (C = window[n] || "") || "";
for (var e = t.split("."), h = Number(e[0]) || 0, i = Number(e[1]) || 0, d = [], f = 0, g = 0; g < r.length; g++) {
var m = r.charCodeAt(g);
128 > m ? d[f++] = m: (2048 > m ? d[f++] = m >> 6 | 192 : (55296 === (64512 & m) && g + 1 < r.length && 56320 === (64512 & r.charCodeAt(g + 1)) ? (m = 65536 + ((1023 & m) << 10) + (1023 & r.charCodeAt(++g)), d[f++] = m >> 18 | 240, d[f++] = m >> 12 & 63 | 128) : d[f++] = m >> 12 | 224, d[f++] = m >> 6 & 63 | 128), d[f++] = 63 & m | 128)
}
for (var S = h,
u = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), l = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(51) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(98)) + ("" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(102)), s = 0; s < d.length; s++) S += d[s],
S = a(S, u);
return S = a(S, l),
S ^= i,
0 > S && (S = (2147483647 & S) + 2147483648),
S %= 1e6,
S.toString() + "." + (S ^ h)
}
var C = null;
t.exports = n
});;
返回的结果中有一段 S.toString() + "." + (S ^ h)
,与我们期望的加密值结构非常类似
嗯,其实这段代码就是加密的核心代码,有兴趣的朋友可以详细理解,这里就不多说啦
我已经将加密的代码单独整理出来,大家直接调用即可
这里再啰嗦一下,加密过程中需要用到一个名为 gtk
的参数,该值直接通过 window.gtk 即可获取,可以获取网页源码后通过 regex 匹配该值,这个值是可以重复使用的,不需要每次调用,所以再初始化的时候获取到即可
好啦,不啰嗦了,加密源码奉上
https://www.devtool.top/upload/2018/01/u5kr8hdmtgikjqpi8ta7q00sqr.zip
后续会有更多有趣的内容和大家分享讨论,如果觉得对你有帮助的话点下关注~~
网友评论
function b(a, b) {
for (var d = 0; d < b.length - 2; d += 3) {
var c = b.charAt(d + 2),
c = "a" <= c ? c.charCodeAt(0) - 87 : Number(c),
c = "+" == b.charAt(d + 1) ? a >>> c : a << c;
a = "+" == b.charAt(d) ? a + c & 4294967295 : a ^ c
}
return a
}
function tk(a,TKK) {
for (var e = TKK.split("."), h = Number(e[0]) || 0, g = [], d = 0, f = 0; f < a.length; f++) {
var c = a.charCodeAt(f);
128 > c ? g[d++] = c : (2048 > c ? g[d++] = c >> 6 | 192 : (55296 == (c & 64512) && f + 1 < a.length && 56320 == (a.charCodeAt(f + 1) & 64512) ? (c = 65536 + ((c & 1023) << 10) + (a.charCodeAt(++f) & 1023), g[d++] = c >> 18 | 240, g[d++] = c >> 12 & 63 | 128) : g[d++] = c >> 12 | 224, g[d++] = c >> 6 & 63 | 128), g[d++] = c & 63 | 128)
}
a = h;
for (d = 0; d < g.length; d++) a += g[d], a = b(a, "+-a^+6");
a = b(a, "+-3^+b+-f");
a ^= Number(e[1]) || 0;
0 > a && (a = (a & 2147483647) + 2147483648);
a %= 1E6;
return a.toString() + "." + (a ^ h)
}
我用谷歌的算法 计算出来的数据和抓百度翻译包里面的sign是一样的
python 里生成 sign 可用 js2py 将 sign.js 转成 sign.py 作为 python 函数调用,比用 execjs 包直接调用 sign.js 快一秒的样子
因为我在写一个hexo的标题自动翻译插件,目前在百度翻译接口上遇到了这个问题,盼望解答,谢谢!
token = re.findall(r"token: '(.*)'", requests.get('http://fanyi.baidu.com/'.text))[0]
感谢九楼的提示,其实博主也提到过。
for (var t = 0; t < o.length - 2; t += 3) {
var a = o.charAt(t + 2);
a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a);
a = "+" === o.charAt(t + 1) ? r >>> a : r << a;
r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a;
}
return r;
}
function baiduTranslateSign(r, C) {
var o = r.length;
if(o > 30) {
r = "" + r.substr(0, 10) + r.substr(Math.floor(o / 2) - 5, 10) + r.substr(-10, 10);
}
var e = C.split("."), h = Number(e[0]) || 0, i = Number(e[1]) || 0;
var theEncoder = new TextEncoder()
var d = theEncoder.encode(r);
var u = "+-a^+6";
var l = "+-3^+b+-f";
for (var S = h, index = 0; index < d.length; ++index) {
S += d[index];
S = __SignHelper__(S, u);
}
S = __SignHelper__(S, l);
S ^= i;
if(0 > S) {
S = (2147483647 & S) + 2147483648;
}
S %= 1000000;
return S.toString() + "." + (S ^ h);
};