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 jsonurl = '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)
运行结果为:
注:仅供学习,切莫用于商业活动!
上述代码,如果在请求多次的情况下,个人觉得可能会出现封ip的情况,于是可以给它加个代理,上网找了一圈,虽然有免费的代理,但是可用的很少,于是找到了价格还算优惠的ip代理网站,这个:一连代理,价格如下:
可以发现,价格还是挺优惠的,而且可以免费领取3天代理服务哈!确认订单之后,访问这个网址,即可获取对应代理ip,如下:http://api.yilian.top/v2/proxy/proxies?num=1&type=1&duration=1&token=(你的token值,购买成功之后生成的token值)
运行结果:
这个proxies中的参数参考文档来写哈!文档地址为:文档地址