Python爬虫:通过js逆向分析某翻译网站的原理
- 1. 网站实现原理
- 2. 抓取接口
- 3. 参考代码和运行结果
1. 网站实现原理
首先,说一下爬取的网站:百度翻译。网站实现翻译的效果是通过接口实现的,也就是各位听到的ajax技术(只需要更换对应参数,就可以实现网站部分刷新的效果,不需要刷新整个网页)。
下面演示的是 中文 到 英文的翻译。
这个接口的响应数据经过一定的解析,最后就是我们网页上看到的那种翻译结果了。
2. 抓取接口
请求接口为:https://fanyi.baidu.com/v2transapi?from=zh&to=en
请求参数如下:
from: zh
to: en
query: 笑
transtype: realtime
simple_means_flag: 3
sign: 852202.648155
token: ab7c42fced834521400ca9c95a3b5bde
domain: common
ts: 1701589808240
请求参数中需要注意的参数有sign、query、ts,其他参数的值可以说均是固定的,或者说因为浏览器的原因或者其他原因可能有所不同。
query的值也就是我们输入的需要翻译的词,sign的值需要根据query的值进行一定处理,ts就是当前时间的时间戳而已。通过js逆向可以发现,这些值产生的js代码在这:
其中b为一个js函数,如下:
经过进一步处理,最终得到js代码如下:
当然,上述js代码简化可能存在一定的问题吧!但是对于简单词句的翻译,达到正确翻译效果应该没有问题。
3. 参考代码和运行结果
没有的模块需要提前下载,比如requests、execjs、json。关于请求头的字段,这里讲述一下,User-Agent用于把当前请求模仿成一个正常的浏览器访问行为,Content-Type只是说一下请求参数的格数而已,Cookie和User-Agent效果一样,不添加这个字段访问会失败的哈!
# -*- coding: utf-8 -*-
import requests
import execjs
import time
import json
url = 'https://fanyi.baidu.com/v2transapi?from=zh&to=en'
headers = {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Cookie": "BDUSS=UZkdDdSLXBHeFNoQVJ4OHZmVUxYbGYxQ1NmNzB-fnVXZklSVGRZWVRZNXIzcFJoRVFBQUFBJCQAAAAAAAAAAAEAAADQ51LqX7PW1q7S1LrjX2xpdQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtRbWFrUW1hd; BDUSS_BFESS=UZkdDdSLXBHeFNoQVJ4OHZmVUxYbGYxQ1NmNzB-fnVXZklSVGRZWVRZNXIzcFJoRVFBQUFBJCQAAAAAAAAAAAEAAADQ51LqX7PW1q7S1LrjX2xpdQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtRbWFrUW1hd; PSTM=1642905086; BIDUPSID=23BF0A276A7CC6017B58F438A11C7155; REALTIME_TRANS_SWITCH=1; BAIDUID=003C5186D83049E7851C40AFB3AC0BED:SL=0:NR=10:FG=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; H_WISE_SIDS_BFESS=39671_39672_39664_39676_39713_39741_39780_39789_39704_39682_39678_39818_39837_39843; ZFY=2QTXGhhFbz:B51omwOyXYTQSuCe0aEOR8YRpzrM8oc:Bs:C; BAIDUID_BFESS=003C5186D83049E7851C40AFB3AC0BED:SL=0:NR=10:FG=1; H_PS_PSSID=39676_39713_39741_39780_39789_39704_39682_39678_39818_39837_39843; H_WISE_SIDS=39676_39713_39741_39780_39789_39704_39682_39678_39818_39837_39843; ET_WHITELIST=etwhitelistintwodays; BA_HECTOR=a0ag2h2g2105a1208h2lah0m1imlmus1r; BDRCVFR[S_ukKV6dOkf]=mk3SLVN4HKm; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1701152555,1701310864,1701485544,1701572131; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1701572139; ab_sr=1.0.1_YzA0MjVlNDQyMGJmMmVjMTJmYjUyOWI2ODU1MWUzNzA5ZGY5NTc2YzI5MzE4MWFiY2UzMTkzNDUzMzFlZDYwYjVlZTgwNzRmMzZkMGNlZTJlZjU1MTQwMGNmM2U2NmMwZGVmODZlZTM5YzhiZjgzOGVkNzBjNDNjOWYwNjFjYTJhMWYxNWFhZmYzMDVkNTc2Mzc0YzI2MzQ0ZTc0ZDM5ODAzNzM0YjY3ZTk2MjRiOWVjMzA4YmNlNzJlZWZmMWFm",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.202.400 QQBrowser/11.9.5355.400",
}
_js = '''
function fun1(t) {
var o;
var r = '320305.131321201';
var a = t.length;
a > 30 && (t = "".concat(t.substr(0, 10)).concat(t.substr(Math.floor(a / 2) - 5, 10)).concat(t.substr(-10, 10)))
for (var d = 'gtk', h = r.split("."), f = Number(h[0]) || 0, m = Number(h[1]) || 0, g = [], y = 0, v = 0; v < t.length; v++) {
var _ = t.charCodeAt(v);
_ < 128 ? g[y++] = _ : (_ < 2048 ? g[y++] = _ >> 6 | 192 : (55296 == (64512 & _) && v + 1 < t.length && 56320 == (64512 & t.charCodeAt(v + 1)) ? (_ = 65536 + ((1023 & _) << 10) + (1023 & t.charCodeAt(++v)),
g[y++] = _ >> 18 | 240,
g[y++] = _ >> 12 & 63 | 128) : g[y++] = _ >> 12 | 224,
g[y++] = _ >> 6 & 63 | 128),
g[y++] = 63 & _ | 128)
}
console.log(g);
for (var b = f, w = '+-a^+6', k = '+-3^+b+-f', x = 0; x < g.length; x++)
b = n(b += g[x], w);
return b = n(b, k),
(b ^= m) < 0 && (b = 2147483648 + (2147483647 & b)),
"".concat((b %= 1e6).toString(), ".").concat(b ^ f)
}
function n(t,e){
for (var n = 0; n < e.length - 2; n += 3) {
var r = e.charAt(n + 2);
r = "a" <= r ? r.charCodeAt(0) - 87 : Number(r),
r = "+" === e.charAt(n + 1) ? t >>> r : t << r,
t = "+" === e.charAt(n) ? t + r & 4294967295 : t ^ r
}
return t
}
'''
# js代码
key_word = input('输入:')
ctx = execjs.compile(_js)
sign = ctx.call('fun1', key_word)
data = {
"from": "zh",
"to": "en",
"query": key_word,
"transtype": "realtime",
"simple_means_flag": "3",
"sign": sign,
"token": "ab7c42fced834521400ca9c95a3b5bde",
"domain": "common",
"ts": f'{int(time.time()*1000)}'
}
rsp = requests.post(url=url, data=data, headers=headers)
map = json.loads(rsp.text)
print(map)
运行结果为:
注:仅供学习,切莫用于商业活动!