我们之前已经讲过了ajax的相关知识,现在我们来试试模拟js,破解有道翻译做一个桌面级的翻译机;
开发环境:
- Python3.6
- 谷歌浏览器
首先我们还是先分析一下网站:
- 首先我们先打开网站,调出“检查”-“Network”,然后先把所有的数据先清空,然后选中 XHR,这就是我们异步请求交换的数据包:
- 我们往网站输入我们想翻译的内容,看看会出现什么:
- 我们发现出现了两个数据,我们先把第一个数据点开看一下,发现里面就有我们刚才输入的内容,那么这个就是我们需要的,数据进行交互的地方(一般来讲都是第一个,如果不是一个个找一下,看开头,有经验之后很快会找到的),我们看看他的Headers里面有什么:
从这里我们可以看到两点:
1)实际请求的网址是:http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule
2)请求方式是 POST - 那么有了这两点,我们就可以发出请求了,但是还有一点,数据,没有数据哪来的交互:
我们往下来,发现有这么一块数据,这就是我们需要发送给服务器的数据,一堆东西,还有什么神奇的字符,根本不知道是什么意思,那我们干脆再发送一个请求,看看有什么变量:
通过两次的请求,我们发现中间的:salt、sign、ts,有变化,其他的都没变,那么这三项的值是怎么得来的呢?我们又怎么知道他们怎么变化的呢? - 我们点右上角的三点,选择 “Search”,进行全局搜索,搜索上面三个变量的任意一个,看看他们在什么地方出现过:
我们发现有一个js文件里面有相关的字段,我们单机一下,看看这个js文件的内容(点击花括号格式化内容,我们可以看得舒服一点) - 我们在这里面再搜索一下salt,看看会发现什么端倪:
我们发现查找到了四处,我们一个一个看过去,看看那里是这些值生成出来的地方,我们发现第二个貌似是的,我们详细看一下; - 这么一坨东西,没有基础的话不一定能看得懂,那么我们鼠标在行号那里点一下,打一个断点,重新发送一个请求,我们看看每个对应着什么:
我们发现:
e 对应我们想要翻译的内容
t 是什么我们暂且不知道,但是能看出来的是,它进行了md5加密
r 我们可以看出来,它获取了当前的时间,是一个时间戳
i 我们看见它是在r的基础上添加了一个随机数
最后 r->ts , t->bv , i->salt , sign这里它还对字符串重新组合再进行了加密 - 现在我们已经知道了对应的关系,现在我们看下每一项是怎么生成的:
上面我们还不知道t是怎么来的,我们把鼠标移到md5()里面的参数上,看看这个参数是个啥:
我们发现,这就是我们的数据头里面用户代理的信息,它就是对这个信息进行了加密,因为我们两次都是同一台机器发出的请求,所以在上面两次的值都是一样的;
其他的我们貌似已经全部分析出来了,接下来我们就开始写代码,一步一步实现它;
代码实现:
import requests # 发出访问请求
import hashlib # md5加密
import time # 生成时间戳
import random # 生成随机数
i = input('请输入你想要翻译的内容:') # 我们通过input来动态输入需要翻译的内容
# 定义md5加密方法
def get_md5(str):
str = str.encode('utf-8') # 首先确定编码格式
md5 = hashlib.md5(str).hexdigest() # 然后调用方法对数据进行加密
return md5 # 返回加密结果
bv = get_md5('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36')
# ts = time.time()
# 1576496918.7001328 生成的时间戳是这样的
# 1576495638700 这是我们需要的
ts = str(int(time.time()*1000)) # 将小数点往后移三位然后取整,最后转换成字符串
# 1576497033075
salt = ts + str(random.randint(0,9)) # 在ts的基础上,添加一位随机数
# 15764971520279
# sign是一个组合字符串的md5加密
sign = get_md5('fanyideskweb{}{}n%A-rKaT5fb[Gy?;N5@Tj'.format(i,salt))
# 发送的数据字典
data ={
'i':i,
'from':'AUTO',
'to':'AUTO',
'smartresult':'dict',
'client':'fanyideskweb',
'salt':salt,
'sign':sign,
'ts':ts,
'bv':bv,
'doctype':'json',
'version':'2.1',
'keyfrom':'fanyi.web',
'action':'FY_BY_REALTlME',
}
# 头信息,不然会判断是爬虫,直接不给访问
header = {
'Accept':'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding':'gzip, deflate',
'Accept-Language':'zh-CN,zh;q=0.9',
'Connection':'keep-alive',
'Content-Length':'251',
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie':'OUTFOX_SEARCH_USER_ID=678086963@10.108.160.17; JSESSIONID=aaalVecYWkv0U69kSgo8w; OUTFOX_SEARCH_USER_ID_NCOO=1719213539.8631594; UM_distinctid=16f0e1ae29842c-027d1b22b4a1bb-2393f61-1fa400-16f0e1ae299441; ___rl__test__cookies=1576489665039',
'Host':'fanyi.youdao.com',
'Origin':'http://fanyi.youdao.com',
'Referer':'http://fanyi.youdao.com/',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
'X-Requested-With':'XMLHttpRequest',
}
result = requests.post('http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule',headers=header,data=data).json() # 发出请求,返回json格式数据
# 请输入你想要翻译的内容:天气
# {'translateResult': [[{'tgt': 'The weather', 'src': '天气'}]], 'errorCode': 0, 'type': 'zh-CHS2en', 'smartResult': {'entries': ['', '[气象] weather\r\n'], 'type': 1}}
# 下面我们就是需要对获得的数据进行提取:
print(result['translateResult'][0][0]['tgt'])