光说不练瞎把式,今天开始我们的逆向实战第一站,逆向有道翻译接口
抓包
当我们有道翻译左侧输入框中输入英文的时候,右侧自动出现了翻译后的结果,但是网页并没有发生刷新,初步判断此处为 AJAX 请求
打开 Chrome调试工具,切换至 Network
/ xhr
,重新输入单词,观察抓包结果,果然是 AJAX 请求
根据返回结果,确认了该条请求就是我们需要的有道翻译接口,切换至 headers,观察其请求体内容
分析
可以发现这是一个 post 请求,且请求参数很多
i: as
from: AUTO
to: AUTO
smartresult: dict
client: fanyideskweb
salt: 16152774048879
sign: 0e7979669e4005d1b87f9ca2cd20c440
lts: 1615277404887
bv: d9e85942912a84a2c1b8691553d518d7
doctype: json
version: 2.1
keyfrom: fanyi.web
action: FY_BY_REALTlME
修改要翻译的单词,观察参数的变化,发现只有如下 4个参数发生了变化
i: as 搜索的单词
salt: 16152774844242 貌似是 lts + 一位数字
sign: b3666f65ed9efb543163f97ff825005c 不清楚
lts: 1615277484424 毫秒时间戳
其余三个参数我们都有了大致的猜测,但是 sign 参数却不知道是怎么生成的。此时我们可以通过切换至 Initiator
选项卡,观察其堆栈调用
点击 ajax 右侧的 JS文件路径,切入其源码内部,格式化代码后,在 send()
处打上断点,重新输入单词后进入断点位置,但是此处已经到了ajax请求发送的位置,我们点击右侧 call stack
进入上一个堆栈调用,查看 Ajax 参数信息
进入后发现,ajax请求被包裹在一个函数中,而请求的参数则是通过 函数的形参e
传递过来的,于是我们继续向上切换堆栈,来到函数调用的位置
此时,可以发现我们需要的三个参数都是包含在 r
对象内部的。同时呢,可以发现 r
对象是由 generateSaltSign
参数返回的。因此,我们切换至 generateSaltSign
函数内部分析 r
的生成逻辑
进入函数内部后,代码并没有任何混淆,我们可以清晰的看出三个参数的生成方式
- lts:毫秒时间戳
- salt:lts + 一位随机数字
- sign:
fanyideskweb" + 搜索词 + 时间戳 + "Tbh5E8=q6U3EXe+&L[4c@
的 md5 结果
Python代码实现
# @Time : 2021-03-09
# @Author : zzzzls
# @Github : https://github.com/zzzzls
# @Blog : https://blog.csdn.net/qq_36078992
import time
import random
import hashlib
import requests
def md5Encrypt(obj):
"""MD5加密"""
md5 = hashlib.md5()
md5.update(obj.encode())
return md5.hexdigest()
def youdao_translate(search_key: str) -> str:
"""
有道翻译 API
:param search_key: 将要翻译的词
:return: str or None, 翻译结果
"""
# 构建加密参数
ts = int(time.time() * 1000)
salt = str(ts) + str(random.randint(1, 9))
bv = md5Encrypt(
'5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36')
sign = md5Encrypt("fanyideskweb" + search_key + salt + "Tbh5E8=q6U3EXe+&L[4c@")
url = 'http://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'
headers = {
'Host': 'fanyi.youdao.com',
'Origin': 'http://fanyi.youdao.com',
'Referer': 'http://fanyi.youdao.com/',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'OUTFOX_SEARCH_USER_ID=-1644523005@10.108.160.100; OUTFOX_SEARCH_USER_ID_NCOO=857549368.4207594; JSESSIONID=aaahQdbLzSjY3dSU_V1Fx; DICT_UGC=be3af0da19b5c5e6aa4e17bd8d90b28a|; JSESSIONID=abcXf1BO34WgqbzPvg3Fx; _ntes_nnid=550e7be268d446cac20d6f763fbdc8c7,1614758394523; ___rl__test__cookies=1615258741826'
}
data = {
"i": search_key,
"from": "AUTO",
"to": "AUTO",
"smartresult": "dict",
"client": "fanyideskweb",
"salt": salt,
"sign": sign,
"lts": ts,
"bv": bv,
"doctype": "json",
"version": "2.1",
"keyfrom": "fanyi.web",
"action": "FY_BY_REALTlME"
}
try:
res = requests.post(url, headers=headers, data=data).json()
result = res['translateResult'][0][0]['tgt']
except:
print('请求失败,请检查参数!')
result = None
return result
if __name__ == '__main__':
kw = input('请输入待翻译内容:')
result = youdao_translate(kw)
print(result)
Github 仓库:zzzzls-Github