赞
踩
在去年下半年,网页版有道翻译上新,使得原来的爬虫接口失效,目前最新的接口用的是https://dict.youdao.com/webtranslate ,接口情况如下图所示:
接口的请求看似简单,但里面加密请求和返回值解密却非常复杂,本文将会介绍该新版有道翻译的爬虫破解过程。
打开F12开发者工具,查看请求的参数:
通过多次不同请求,我们可以发现,这些参数中需要进行特定修改的有以下几个:
其他的参数都比较容易处理,我们关键留意一下sign这个参数,显然,这个sign的参数是用来进行校验的,这个请求是一个带校验参数的请求,通过检验sign的值与请求中的其他参数的值是否对应来进行请求参数的校验,我们来看一下这个参数是怎么生成的。
通过逆向追踪,可以看到这个这个sign的参数是从app.68a22c0c.js里面的第2000多行的这个k函数里面生成的,k函数中的sign调用了上面的w函数,w函数又调用了再上面的A函数,然后可以看到,这里的A函数里面是对参数e进行了md5的哈希,并转化为十六进制,而这个e参数的生成,其实是在w函数中进行多参数字符组合而来的,这里的多参数包含了d、e、u、t,通过打断点的方式,可以跟踪到这四个参数分别其实就是对应一些固定的header参数,唯一有变化的参数正是对应上面的mysticTime时间戳。
核心代码:
- r = 'fanyideskweb'
- i = 'webfanyi'
- e = 'fsdsogkndfokasodnaso'
- t = int(round(time.time() * 1000))
-
- p = f"client={r}&mysticTime={t}&product={i}&key={e}"
- sign = hashlib.md5(p.encode('utf8')).hexdigest()
针对post请求返回数据进行解密,通过进行逆向解密,可以看到app.68a22c0c.js这个文件中有相应的解码过程
可以看到,这里用到的是AES加密算法中的CBC模型进行解密,点进去createDecipheriv函数:
其中,CBC解密的key为e参数,iv为n参数,然后我们回到前面的传参部分:
通过打断点进行分析,可以看到,参数a是一个16长度的列表,c参数也是一样,都由一个alloc函数进行构建,alloc函数的第二个参数为一个j函数,j函数中接受一个参数传入
o参数为
"ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
n参数为
"ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
另一方面,查看o参数和n参数的定义,这两个参数其实是固定的:
在知道了j函数参数的内容之后,再点进去j函数的定义:
和请求头的参数构建一样,这里也是将传入的参数进行md5的转换,不过没有转为16进制
就这样一步一步地进行挖掘,返回值部分的核心代码如下:
- def translate(text, key, iv):
- iv = hashlib.md5(iv).digest()
- key = hashlib.md5(key).digest()
-
- # CBC模式解密
- AES_decrypt = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
- # 返回数据转为byte
- t = base64.b64decode(text, b'-_')
- # 解码
- decrypted_data = AES_decrypt.decrypt(t)
- # 解码数据拆分
- unpadded_message = unpad(decrypted_data, AES.block_size).decode()
- # 解码数据转为json数据结构
- json_data = json.loads(unpadded_message)
- res = json_data['dictResult']['ce']['word']['trs'][0]['#text']
-
- return res
-
- decodekey = b'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl'
- decodeIv = b'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4'
- text = make_request('你好', to='')
- translate(text, decodekey, decodeIv)
完整代码如下:
- import hashlib
- import base64
- import time
- import json
- import js2py
- import requests
- from Crypto.Cipher import AES
- from Crypto.Util.Padding import unpad, pad
-
- headers = {
- 'Accept': 'application/json, text/plain, */*'
- ,'Accept-Encoding': 'gzip, deflate, br'
- ,'Accept-Language': 'zh-CN,zh;q=0.9'
- ,'Connection': 'keep-alive'
- ,'Content-Length': '252'
- ,'Content-Type': 'application/x-www-form-urlencoded'
- ,'Cookie': 'OUTFOX_SEARCH_USER_ID=-128580344@10.169.0.83; OUTFOX_SEARCH_USER_ID_NCOO=1344989105.8974342; _ntes_nnid=9b83b473d066e2e23751e2d758891587,1641266653081; search-popup-show=-1; __yadk_uid=Uby0XrclZI18dTgSjb55uEGqfHqTXOfo'
- ,'Host': 'dict.youdao.com'
- ,'Origin': 'https://fanyi.youdao.com'
- ,'Referer': 'https://fanyi.youdao.com/index.html'
- ,'Sec-Fetch-Dest': 'empty'
- ,'Sec-Fetch-Mode': 'cors'
- ,'Sec-Fetch-Site': 'same-site'
- ,'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'
- }
-
- def make_request(word, to = ''):
- form = {
- 'from': 'auto'
- ,'to': to
- ,'dictResult': 'true'
- ,'keyid': 'webfanyi'
- ,'client': 'fanyideskweb'
- ,'product': 'webfanyi'
- ,'appVersion': '1.0.0'
- ,'vendor': 'web'
- ,'pointParam': 'client,mysticTime,product'
- ,'keyfrom': 'fanyi.web'
- }
-
- r = 'fanyideskweb'
- i = 'webfanyi'
- e = 'fsdsogkndfokasodnaso'
- t = int(round(time.time() * 1000))
-
- p = f"client={r}&mysticTime={t}&product={i}&key={e}"
- sign = hashlib.md5(p.encode('utf8')).hexdigest()
-
- form['i'] = word
- form['sign'] = sign
- form['mysticTime'] = t
-
- url = 'https://dict.youdao.com/webtranslate'
-
- res = requests.post(url=url, headers=headers, data=form)
-
- return res.text
-
- def translate(text, key, iv):
- iv = hashlib.md5(iv).digest()
- key = hashlib.md5(key).digest()
-
- # CBC模式解密
- AES_decrypt = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
- # 返回数据转为byte
- t = base64.b64decode(text, b'-_')
- # 解码
- decrypted_data = AES_decrypt.decrypt(t)
- # 解码数据拆分
- unpadded_message = unpad(decrypted_data, AES.block_size).decode()
- # 解码数据转为json数据结构
- json_data = json.loads(unpadded_message)
- res = json_data['dictResult']['ce']['word']['trs'][0]['#text']
-
- return res
-
- if __name__ == '__main__':
- decodekey = b'ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl'
- decodeIv = b'ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4'
- text = make_request('你好', to='')
- translate(text, decodekey, decodeIv)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。