前言
今天教大家如何利用 firefox 分析百度翻译的接口,并用python爬取接口。大家学会后可以干一些有趣的事情,比如做一个自己的翻译软件。
具体内容
1 - 找到URL接口
进入百度翻译的网址,F12进入调式模式,点击Network,选择XHR
点击网页上的 翻 译我们会看见,多了两个XHR记录,这两个记录就是请求服务器获取翻译结果的请求。
我们点击第二条查看,点击Response (服务器返回的数据), 可以看见这个连接返回的东西就是我们想要的数据。接下来我们获取这个请求的url,模拟发送请求试试。
2 - 分析参数
我们可以看到这个记录请求的url便是图上这个,方法是post。我们接下来查看请求的参数
这个便是我们请求的参数了,可以看见有from:原语言 ,to:翻译的语言,query:翻译的字符串,
然后是transtype:翻译类型,simple_means_flag:意思尚不清楚,不过不重要,sign:这个我们后面在解释,token:这个是用来识别浏览器的,知道这些参数之后我们就可以开始写代码了。
2 - 写代码
import requests
url = "https://fanyi.baidu.com/v2transapi"
headers = {
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
queryString = "人生苦短,我用python"
data = {
'from': 'en',
'to': 'zh',
'query': queryString,
'simple_means_flag': 3,
'sign': "289133.35420",
'token': "2876e8473cf85fcd7fc00daf00aa0551",
}
r = requests.post(url, data, headers=headers)
print(r.text)
运行结果如下如
结果并没有出现我们想要的数据,并返回
错误997:sign值不对
。
其实这是因为百度翻译做了反爬处理,上述传过去的sign的值是js动态生成的,随时变化的。那么,目前有两个方案,一是通过手机百度翻译的接口爬取,因为手机端的翻译不需要传入sign值。二是通过分析js源码,得到这个sign值得生成方法,然后自己生成sign值。第一个方案目前百度有很多例子,我就不讲了,我主要讲讲第二个方案。
获取sign得生成方法,我们可以看看js代码
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
}
var C = null;
var token = function(r, _gtk) {
var o = r.length;
o > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(o / 2) - 5, 10) + r.substring(r.length, r.length - 10));
var t = void 0,
t = null !== C ? C: (C = _gtk || "") || "";
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 = "+-a^+6",
l = "+-3^+b+-f",
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)
}
函数token便是我们需要得sign值得生成方法。在python中调用js需要用到execjs 库。请同学们自己去安装一下pip install execjs。
我们可以看到这个js token 函数需要传入两个值queryString 和 gtk。那么什么是gtk呢?我们去源码里看看。
我们可以输入百度翻译的网址查看源码,crtl+f 搜索 gtk,可以得到一条记录
window.gtk = '320305.131321201'
那么我们在调用翻译接口之前,得请求百度翻译html,然后用正则匹配gtk这个值,改改我们得代码再试一试。
为了简便我们把代码封装成函数
我们运行看看
出现了{"error":998,"from":"zh","to":"en","query":"\u4f60\u597d"}
998错误是为什么呢?
998错误:token值不对
我们刚才看过了传过去的参数有个token,其实token也是根据浏览器动态生成的,不过这个和gtk 一样,在html 源码可以使用正则表达式找到
我们在更新一下我们的程序,将get_gtk重构了一下,编程get_params
运行看看
我们可以看到还是
998 错误
???这是为什么呢(黑人问号脸)
有大神说了,这样的token不是最新的,我们得加载两次首页才行。我们在改一次代码吧
只需要运行两次get_params() 访问两次首页,然后token和gtk去第二次的值就行了我们的结果也出现了! 搞了这么久,终于成功了!是不是有点激动呢。。。返回的结果是一个json格式的,我们只需要提取里面的信息就行了。
最后我们将这个程序用类封装一下:
这个我的字典程序就弄好了!
源码给大家
import requests.sessions
import re
import execjs
class BaiDuFanYi():
def __init__(self):
self.homeUrl = "https://fanyi.baidu.com/"
self.transUrl = "https://fanyi.baidu.com/v2transapi"
self.headers = {
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
self.gtk = None
self.token = None
self.s = requests.Session()
self.get_params()
self.get_params()
def get_sign(self, query_string):
JS_CODE = """
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
}
var C = null;
var token = function(r, _gtk) {
var o = r.length;
o > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(o / 2) - 5, 10) + r.substring(r.length, r.length - 10));
var t = void 0,
t = null !== C ? C: (C = _gtk || "") || "";
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 = "+-a^+6",
l = "+-3^+b+-f",
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)
}
"""
return execjs.compile(JS_CODE).call('token', query_string, self.gtk)
def get_params(self):
r = self.s.get(self.homeUrl, headers=self.headers)
self.gtk = re.findall(r"window.gtk = '(.*?)';", r.text)[0]
self.token = re.findall(r"token: '(.*?)',", r.text)[0]
def translate(self, query_string):
sign = self.get_sign(query_string)
data = {
'from': 'zh',
'to': 'en',
'query': query_string,
'simple_means_flag': 3,
'sign': sign,
'token': self.token,
}
res = self.s.post(url=self.transUrl, data=data, headers=self.headers)
return res.json()['trans_result']['data'][0]['dst']
if __name__ == '__main__':
words = input("请输入原文: ")
mydict = BaiDuFanYi()
print(f"翻译结果是: {mydict.translate(words)}")
总结
百度翻译错误
997 : sign值不对(动态生成js)
998: token值不是最新的或者没有(一般需要获取两次,取最第二次)