当前位置:   article > 正文

爬虫案例之网易有道翻译JS代码复杂版

require('crypto-js') youdao

网易有道翻译逆向案例

【一】分析网站

(1)网站页面如图

(2)抓包

(3)分析抓到的包

  • 逐个查看每个包的标头和载荷
  • webtranslate
    • 这个包的请求头中发现其为post请求
    • 这个包的载荷中发现了其携带有很多参数

(4)分析载荷

  1. i: run
  2. from: auto
  3. to:
  4. dictResult: true
  5. keyid: webfanyi
  6. sign: a9856197613117e6524edc4b5076bd55
  7. client: fanyideskweb
  8. product: webfanyi
  9. appVersion: 1.0.0
  10. vendor: web
  11. pointParam: client,mysticTime,product
  12. mysticTime: 1683545856511
  13. keyfrom: fanyi.web
  • 在载荷中发现了很多参数

  • 对网站二次请求抓到的该包携带的参数进行对比

  1. i: rain
  2. from: auto
  3. to:
  4. domain: 0
  5. dictResult: true
  6. keyid: webfanyi
  7. sign: 60d4d3ab8995ecacee1767824036d8a2
  8. client: fanyideskweb
  9. product: webfanyi
  10. appVersion: 1.0.0
  11. vendor: web
  12. pointParam: client,mysticTime,product
  13. mysticTime: 1683546118762
  14. keyfrom: fanyi.web
  • 通过对比可以发现,其中有两个参数发生了变化
    • sign : 猜测这是一个加密后的数据
    • mysticTime : 这是一个时间戳生成的

【二】分析sign并逆向补充

(1)分析sign 的由来

  • 全局搜索sign值:

  • 通过搜索后的结果猜测其可能存在过的文件

    • 其最可能是js文件通过代码生成,所以排除css、html等文件

  • 进入第一个文件进行尝试分析

  • 可以看到当前有一个sign函数,有传参进去,t为时间戳,e为参数。

    • 将此处打断点,重新请求,看其请求是否会被卡主

  • 可以很明显的发现其被卡主

(2)参数补充 - t ,逆向生成sign值

  • 将鼠标放到参数t上查看

  • 发现其生成就在上一行代码

    const t = (new Date).getTime();
  • 继续向上查看代码

  • 发现有两行代码很可疑,将其拷贝并补充代码

    1. function g(e) {
    2. return r.a.createHash("md5").update(e).digest()
    3. }
    4. function v(e) {
    5. return r.a.createHash("md5").update(e.toString()).digest("hex")
    6. }
    7. function h(e, t) {
    8. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    9. }
    • 将第一个传e为参的函数调用删除,因为第二个传e为参数的函数也进行同样类似的更新操作
    1. function v(e) {
    2. return r.a.createHash("md5").update(e.toString()).digest("hex")
    3. }
    4. function h(e, t) {
    5. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    6. }
    • 查看第二和 h 函数,补全参数 。
  • 已经有的参数 e 、 t 。需要补全 d 、 u

  • 向上翻看代码

    1. const d = "fanyideskweb"
    2. , u = "webfanyi"
    3. , m = "client,mysticTime,product"
    4. , p = "1.0.0"
    5. , b = "web"
    6. , f = "fanyi.web";
  • 其中含有固定参数 d 、 u

  • 补全后的代码

    1. // t 参数的声明
    2. const t = (new Date).getTime();
    3. function v(e) {
    4. return r.a.createHash("md5").update(e.toString()).digest("hex")
    5. }
    6. var d = "fanyideskweb"
    7. var u = "webfanyi"
    8. function h(e, t) {
    9. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    10. }
    11. h()
    12. //ReferenceError: r is not defined
    • 这里运行后会报错,错误显示 r 没有被定义

    • 这里采用的办法是 利用 crypto-js 补全环境

  • 先声明 调用 该模块

    var Cry = require('crypto-js')
  • 再修改该部分代码

    1. //修改前
    2. function v(e) {
    3. return r.a.createHash("md5").update(e.toString()).digest("hex")
    4. }
    5. //前面代码的大概意思是运用MD5加密算法将 e 加密混淆后转换为字符串
    6. //修改后
    7. function v(e) {
    8. return Cry.MD5(e).toString()
    9. }
    10. //修改后用 crypto-js(在js代码中的crypto算法) 这个 模块将 e 加密混淆后转换为字符串
  • 补全后的代码

    1. var Cry = require('crypto-js')
    2. // t 参数的声明
    3. const t = (new Date).getTime();
    4. // function v(e) {
    5. // return r.a.createHash("md5").update(e.toString()).digest("hex")
    6. // }
    7. function v(e) {
    8. return Cry.MD5(e).toString()
    9. }
    10. var d = "fanyideskweb"
    11. var u = "webfanyi"
    12. function h(e, t) {
    13. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    14. }
    15. h()
    16. // 无报错

(3)参数补充 - e ,逆向生成sign值

  • 观察发现 e 为固定值

    e = "fsdsogkndfokasodnaso"
  • 补全代码

    1. var Cry = require('crypto-js')
    2. // t 参数的声明
    3. const t = (new Date).getTime();
    4. // function v(e) {
    5. // return r.a.createHash("md5").update(e.toString()).digest("hex")
    6. // }
    7. function v(e) {
    8. return Cry.MD5(e).toString()
    9. }
    10. var d = "fanyideskweb"
    11. var u = "webfanyi"
    12. function h(e, t) {
    13. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    14. }
    15. function sign(){
    16. var e = "fsdsogkndfokasodnaso"
    17. return h(e,t)
    18. }
    19. console.log(sign())
    20. //调用sigh函数,查看其生成代码
    21. //b73eba683a8cafb121cbf292d122e628

(4)如何检验代码是否改写成功?

  • 将参数 t 写死 ,观察生成值是否相同

    1. var Cry = require('crypto-js')
    2. // t 参数的声明
    3. // const t = (new Date).getTime();
    4. var t = '1683546118762'
    5. // function v(e) {
    6. // return r.a.createHash("md5").update(e.toString()).digest("hex")
    7. // }
    8. function v(e) {
    9. return Cry.MD5(e).toString()
    10. }
    11. var d = "fanyideskweb"
    12. var u = "webfanyi"
    13. function h(e, t) {
    14. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    15. }
    16. function sign(){
    17. var e = "fsdsogkndfokasodnaso"
    18. return h(e,t)
    19. }
    20. console.log(sign())
    21. // 二者 相同

【三】Python 部分代码

(1)定义函数 ---- 获取sign值部分

  • 模块的导入

    1. # requests 请求模块
    2. import requests
    3. # 随机UA模块
    4. from fake_useragent import UserAgent
    5. # 这里是执行js代码的相关模块
    6. import subprocess
    7. from functools import partial
    8. # 必须先定义这个变量 再引入 execis模块 否则会报错
    9. subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
    10. import execjs
  • 定义 get_sign() 部分代码

    1. def get_sign():
    2. # 创建node对象
    3. node = execjs.get()
    4. # 读取到 js 代码,并以 utf-8编码方式打开
    5. with open('01.js', encoding='utf-8') as f:
    6. # JS 源文件编译
    7. ctx = node.compile(f.read())
    8. # 调用函数
    9. sign = ctx.eval('run()')
    10. # sign[0]:列表中的sign值, sign[1]:列表中的t值
    11. return sign[0], sign[1]
  • 这里还需要重写 01.js 文件,定义 run() 函数

    1. var Cry = require('crypto-js')
    2. // t 参数的声明
    3. // const t = (new Date).getTime();
    4. var t = '1683546118762'
    5. // function v(e) {
    6. // return r.a.createHash("md5").update(e.toString()).digest("hex")
    7. // }
    8. function v(e) {
    9. return Cry.MD5(e).toString()
    10. }
    11. var d = "fanyideskweb"
    12. var u = "webfanyi"
    13. function h(e, t) {
    14. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    15. }
    16. function sign(){
    17. var e = "fsdsogkndfokasodnaso"
    18. // 返回 sign 值
    19. return h(e,t)
    20. }
    21. function run(){
    22. // 调用 sign() 函数,返回 sign 值和 t 值
    23. return [sign(),t]
    24. }

(2)伪装请求头

  • webtranslate 文件中我们可以发现请求标头(如果不知道那个不需要就全都写上)

    1. Accept: application/json, text/plain, */*
    2. Accept-Encoding: gzip, deflate, br
    3. Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
    4. Cache-Control: no-cache
    5. Connection: keep-alive
    6. Content-Length: 247
    7. Content-Type: application/x-www-form-urlencoded
    8. Cookie: OUTFOX_SEARCH_USER_ID=-2102182500@10.110.96.154; OUTFOX_SEARCH_USER_ID_NCOO=1723343714.3489342
    9. Host: dict.youdao.com
    10. Origin: https://fanyi.youdao.com
    11. Pragma: no-cache
    12. Referer: https://fanyi.youdao.com/
    13. sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"
    14. sec-ch-ua-mobile: ?0
    15. sec-ch-ua-platform: "Windows"
    16. Sec-Fetch-Dest: empty
    17. Sec-Fetch-Mode: cors
    18. Sec-Fetch-Site: same-site
    19. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
    • 这里 headers 中的 "User-Agent"采用了fake随机获取

    • 这里的cookie进行了单独提取

      1. def spider(eng):
      2. # 请求头中的部分参数
      3. headers = {
      4. 'Accept': 'application/json, text/plain, */*',
      5. 'Accept-Encoding': 'gzip, deflate, br',
      6. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
      7. 'Cache-Control': 'no-cache',
      8. 'Connection': 'keep-alive',
      9. 'Content-Length': '239',
      10. 'Content-Type': 'application/x-www-form-urlencoded',
      11. 'Host': 'dict.youdao.com',
      12. 'Origin': 'https://fanyi.youdao.com',
      13. 'Pragma': 'no-cache',
      14. 'Referer': 'https://fanyi.youdao.com/',
      15. 'sec-ch-ua': "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"",
      16. "sec-ch-ua-mobile": "?0",
      17. "sec-ch-ua-platform": "Windows",
      18. "Sec-Fetch-Dest": "empty",
      19. "Sec-Fetch-Mode": "cors",
      20. "Sec-Fetch-Site": "same-site",
      21. "User-Agent": UserAgent().random,
      22. }
      23. # 请求头中的cookie
      24. cookies = {
      25. 'OUTFOX_SEARCH_USER_ID': '-2102182500@10.110.96.154',
      26. 'OUTFOX_SEARCH_USER_ID_NCOO': '1723343714.3489342',
      27. }
      28. # 需要请求的请求页 url
      29. page_url = 'https://dict.youdao.com/webtranslate'
      30. # 因为是携带参数的请求,所以是post请求
      31. # post请求需要携带请求参数(分析已经得知 sign 和 t 为变量,已经做了逆向获取)
      32. # 从 get_sign() 函数中获取到返回值 sign,mysticTime
      33. sign, mysticTime = get_sign()
      34. # 将 sign,mysticTime 添加到请求携带参数中
      35. data = {
      36. "i": str(eng),
      37. "from": "auto",
      38. "to": "",
      39. "dictResult": "true",
      40. "keyid": "webfanyi",
      41. "sign": sign,
      42. "client": "fanyideskweb",
      43. "product": "webfanyi",
      44. "appVersion": "1.0.0",
      45. "vendor": "web",
      46. "pointParam": "client,mysticTime,product",
      47. "mysticTime": mysticTime,
      48. "keyfrom": "fanyi.web",
      49. }
      50. # 对请求页发起请求
      51. response = requests.post(url=page_url, headers=headers, data=data, cookies=cookies)
      52. # 返回请求响应内容
      53. return response.text

(3)定义主函数入口

  1. if __name__ == '__main__':
  2. res = spider('rain')
  3. print(res)

【四】逆向解密密文数据

(1)查找 sign 值的去向

  • 从刚才的断点依次执行代码,找到加密后的密文数据,即 webtranslate 的响应内容

    • 一般特征是 json.parse()

  • 在这行代码我们发现了这个方法

  • 并且也发现了其上面获取到的数据 o

    o = "Z21kD9ZK1ke6ugku2ccWu-MeDWh3z252xRTQv-wZ6jddVo3tJLe7gIXz4PyxGl73nSfLAADyElSjjvrYdCvEP4pfohVVEX1DxoI0yhm36ytQNvu-WLU94qULZQ72aml6x7-sEgf3E8xxPy8fNgUR0PtLyLVXDnp0W_hhc-8PxqHNoVtmgMHMW8tBEi7Xh66zR8dDM3Ga-WTkqp4bVDIJHqVq7sbwGnCYrLIE-UQQNHZPv0XjhUoKPsJcfHXQzoNiGzoWtcYwV_z2Pwc9VS_vUfwlpfboIjBuCpnQ7QaUUL3cvi2pHSzznsXHIwr3B9RQCkiGZ5hW1_etmLOma7LtBnWLzWm9vj9qHHoAXTW6ZSXKvSaKWpI4owfVpffytuYADNG7n7H0Vg5P93mfAMNOMRKN4dNm0HkeWIKHFPLNLgH0SbZWk3kVnuJHw0Xsa32GLb3kcmjui6srexVN1PI3KpyCIL29fSPyLM9p5IHw8pZ5rSOAe6Z-pHwrcnpdnLhf8d-xRcxy2k6Aq_OLh4pw-DpoS53nPPBW6xIlFXH4itoLTlDjXq3Q64mujS7UTjuy8XME5aegFhrkDL86D18K0TeOJQtlZYtQNJMvqRcyVakDBEjph-_8r6jBhfLVr2aSM5BEs_cS_rh4tLXp48vte1P2YkuDF2_GQlh7fkJpSuSko9cObi8xvxULk1heIKOxpkRlwK187vD-E1VNMrulR5YmZXtQcS9E1g40f8qLHByyUULfY41SCepWQgvrwI3n4KAd6Pui5INE3iK-n9_unJ4L9H0HUugLoEmJA9F5ylal-pvhafyHlunzfv3os2lzccP-e04GrL_MZdqnGa_c019lEFqZKist2PxIIdkM3QpGedOXsx-guqfAjXaycFpQJn7DH32VDTVEbRGdcNaMZvh0lS4-ExynCiPLvYSX8Xvxpl4lffiknNZ7gYf56S8uKMAsGP3gS040ZV7mLo90wtlXe3cZekKNEpTR5OzqPeL6_CUTvjaE75o7TWE6BjmtGvA10fcuTZuZep73PgnvkU-HP447QGc_SqxuD98ZoITYkf11HsX9WeEgIZYkN-CCAQ1DcNiyzcJNIB3rdrYj9_5hFAwrKIPU1kYPnBQVsqb3p2YVXARZK7LqNBQfDHIZ5k3_boqWP2ZbRfr_Sdy5Yw=="
  • 同时看到了我们想要的一串数据

    "{"code":0,"dictResult":{"ec":{"exam_type":["初中","高中","CET4","CET6","考研"],"word":{"usphone":"reɪn","ukphone":"reɪn","ukspeech":"rain&type=1","trs":[{"pos":"n.","tran":"雨,雨水;(热带地区的)雨季(the rains);(降雨般的)一阵,(大量的)降落物"},{"pos":"v.","tran":"下雨;(使)大量降落,雨点般落下;(使)如雨般地猛击"},{"tran":"【名】 (Rain)(法)兰,(英)雷恩,(罗、捷)赖恩(人名)"}],"wfs":[{"wf":{"name":"复数","value":"rains"}},{"wf":{"name":"第三人称单数","value":"rains"}},{"wf":{"name":"现在分词","value":"raining"}},{"wf":{"name":"过去式","value":"rained"}},{"wf":{"name":"过去分词","value":"rained"}}],"return-phrase":"rain","usspeech":"rain&type=2"}}},"translateResult":[[{"tgt":"","src":"rain","tgtPronounce":""}]],"type":"en2zh-CHS"}"

(2)逆向补全代码

  • 首先 将这部分代码 扣出来

    const n = an["a"].decodeData(o, sn["a"].state.text.decodeKey, sn["a"].state.text.decodeIv)
  • 由此我们可以看到,我们需要两个 固定值 decodeKey (秘钥)和 decodeIv(偏移量)

  • 将鼠标悬停在其上面就可以看到两个相应的值

    • decodeKey (解密秘钥)
    "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
    • decodeIv(偏移量)
    "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
  • 我们还需要知道解密的算法

  • 将鼠标悬停在 an["a"].decodeData 上查看其解密js代码

  • 发现一个箭头函数

    1. S = (t, o, n) => {
    2. if (!t)
    3. return null;
    4. const a = e.alloc(16, g(o))
    5. , c = e.alloc(16, g(n))
    6. , i = r.a.createDecipheriv("aes-128-cbc", a, c);
    7. let s = i.update(t, "base64", "utf-8");
    8. return s += i.final("utf-8"),
    9. s
    10. }
    • 格式化

      1. function decode(t, o, n) {
      2. if (!t)
      3. return null;
      4. const a = e.alloc(16, g(o))
      5. , c = e.alloc(16, g(n))
      6. , i = r.a.createDecipheriv("aes-128-cbc", a, c);
      7. let s = i.update(t, "base64", "utf-8");
      8. return s += i.final("utf-8"),
      9. s
      10. }
    • 由此,分析

      • 我们需要知道 e.alloc
      • 还需要知道 r.a.

(3)解决办法

  • e.alloc

  • 由Buffer替换即可

    1. const a = Buffer.alloc(16, g(o))
    2. , c = Buffer.alloc(16, g(n))
  • r.a.

  • 生成加密算法对象进行加密

    1. // 导入crypto模块
    2. const crypto = require('crypto')
    3. // 创建算法对象
    4. function g(e) {
    5. return crypto.createHash("md5").update(e).digest()
    6. }
    7. function decode(t, o, n) {
    8. if (!t)
    9. return null;
    10. const a = Buffer.alloc(16, g(o))
    11. , c = Buffer.alloc(16, g(n))
    12. , i = crypto.createDecipheriv("aes-128-cbc", a, c);
    13. let s = i.update(t, "base64", "utf-8");
    14. return s += i.final("utf-8"),
    15. s
    16. }

(4)拼接数据

  1. // 导入crypto模块
  2. const crypto = require('crypto')
  3. // 创建算法对象
  4. function g(e) {
  5. return crypto.createHash("md5").update(e).digest()
  6. }
  7. function decode(t, o, n) {
  8. if (!t)
  9. return null;
  10. const a = Buffer.alloc(16, g(o))
  11. , c = Buffer.alloc(16, g(n))
  12. , i = crypto.createDecipheriv("aes-128-cbc", a, c);
  13. let s = i.update(t, "base64", "utf-8");
  14. return s += i.final("utf-8"),
  15. s
  16. }
  17. // 声明 变量 解密秘钥和偏移量
  18. var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
  19. var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
  20. // 定义 加密后的密文数据
  21. var n = o = "Z21kD9ZK1ke6ugku2ccWu-MeDWh3z252xRTQv-wZ6jddVo3tJLe7gIXz4PyxGl73nSfLAADyElSjjvrYdCvEP4pfohVVEX1DxoI0yhm36ytQNvu-WLU94qULZQ72aml6x7-sEgf3E8xxPy8fNgUR0PtLyLVXDnp0W_hhc-8PxqHNoVtmgMHMW8tBEi7Xh66zR8dDM3Ga-WTkqp4bVDIJHqVq7sbwGnCYrLIE-UQQNHZPv0XjhUoKPsJcfHXQzoNiGzoWtcYwV_z2Pwc9VS_vUfwlpfboIjBuCpnQ7QaUUL3cvi2pHSzznsXHIwr3B9RQCkiGZ5hW1_etmLOma7LtBnWLzWm9vj9qHHoAXTW6ZSXKvSaKWpI4owfVpffytuYADNG7n7H0Vg5P93mfAMNOMRKN4dNm0HkeWIKHFPLNLgH0SbZWk3kVnuJHw0Xsa32GLb3kcmjui6srexVN1PI3KpyCIL29fSPyLM9p5IHw8pZ5rSOAe6Z-pHwrcnpdnLhf8d-xRcxy2k6Aq_OLh4pw-DpoS53nPPBW6xIlFXH4itoLTlDjXq3Q64mujS7UTjuy8XME5aegFhrkDL86D18K0TeOJQtlZYtQNJMvqRcyVakDBEjph-_8r6jBhfLVr2aSM5BEs_cS_rh4tLXp48vte1P2YkuDF2_GQlh7fkJpSuSko9cObi8xvxULk1heIKOxpkRlwK187vD-E1VNMrulR5YmZXtQcS9E1g40f8qLHByyUULfY41SCepWQgvrwI3n4KAd6Pui5INE3iK-n9_unJ4L9H0HUugLoEmJA9F5ylal-pvhafyHlunzfv3os2lzccP-e04GrL_MZdqnGa_c019lEFqZKist2PxIIdkM3QpGedOXsx-guqfAjXaycFpQJn7DH32VDTVEbRGdcNaMZvh0lS4-ExynCiPLvYSX8Xvxpl4lffiknNZ7gYf56S8uKMAsGP3gS040ZV7mLo90wtlXe3cZekKNEpTR5OzqPeL6_CUTvjaE75o7TWE6BjmtGvA10fcuTZuZep73PgnvkU-HP447QGc_SqxuD98ZoITYkf11HsX9WeEgIZYkN-CCAQ1DcNiyzcJNIB3rdrYj9_5hFAwrKIPU1kYPnBQVsqb3p2YVXARZK7LqNBQfDHIZ5k3_boqWP2ZbRfr_Sdy5Yw=="
  22. // 利用解密函数进行解密
  23. console.log(decode(n, k, iv))
  24. /*{"code":0,"dictResult":{"ec":{"exam_type":["初中","高中","CET4","CET6","考研"],"word":{"usphone":"reɪn","ukphone":"reɪn","ukspeech":"rain&type=1","trs":[{"pos":"n.","tran":"雨,雨水
  25. ;(热带地区的)雨季(the rains);(降雨般的)一阵,(大量的)降落物"},{"pos":"v.","tran":"下雨;(使)大量降落,雨点般落下;(使)如雨般地猛击"},{"tran":"【名】 (Rain)(法)兰,
  26. (英)雷恩,(罗、捷)赖恩(人名)"}],"wfs":[{"wf":{"name":"复数","value":"rains"}},{"wf":{"name":"第三人称单数","value":"rains"}},{"wf":{"name":"现在分词","value":"raining"}},{"wf"
  27. :{"name":"过去式","value":"rained"}},{"wf":{"name":"过去分词","value":"rained"}}],"return-phrase":"rain","usspeech":"rain&type=2"}}},"translateResult":[[{"tgt":"雨","src":"rain","tg
  28. tPronounce":"yŭ"}]],"type":"en2zh-CHS"}
  29. */
  • 通过打印结果 可以看到我们的解密函数正常运行

【五】Python部分代码

(1)定义 tran_data() 函数

  • 其参数为,加密后的密文数据
  1. def tran_data(data):
  2. # 创建 node 对象
  3. node = execjs.get()
  4. # 读取到 js 代码,并以 utf-8编码方式打开
  5. with open('02.js', encoding='utf-8') as f:
  6. # JS 源文件编译
  7. ctx = node.compile(f.read())
  8. # 调用函数 , 并向其传参 ,获得响应数据
  9. t = ctx.eval(f'run("{data}")')
  10. return t

(2)在 js 文件中定义主函数 run()

  1. // 导入crypto模块
  2. const crypto = require('crypto')
  3. // 创建算法对象
  4. function g(e) {
  5. return crypto.createHash("md5").update(e).digest()
  6. }
  7. function decode(t, o, n) {
  8. if (!t)
  9. return null;
  10. const a = Buffer.alloc(16, g(o))
  11. , c = Buffer.alloc(16, g(n))
  12. , i = crypto.createDecipheriv("aes-128-cbc", a, c);
  13. let s = i.update(t, "base64", "utf-8");
  14. return s += i.final("utf-8"),
  15. s
  16. }
  17. // 声明 变量 解密秘钥和偏移量
  18. var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
  19. var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
  20. // 参数为加密后的密文数据
  21. function run(encode_data) {
  22. return (decode(encode_data, k, iv))
  23. }

【六】代码整合

(1)Python代码部分

  1. # requests 请求模块
  2. import requests
  3. # 随机UA模块
  4. from fake_useragent import UserAgent
  5. # json模块
  6. import json
  7. # 这里是执行js代码的相关模块
  8. import subprocess
  9. from functools import partial
  10. # 必须先定义这个变量 再引入 execis模块 否则会报错
  11. subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
  12. import execjs
  13. def get_sign():
  14. node = execjs.get()
  15. with open('sign.js', encoding='utf-8') as f:
  16. ctx = node.compile(f.read())
  17. sign = ctx.eval('run()')
  18. return sign[0], sign[1]
  19. def tran_data(data):
  20. # 创建 node 对象
  21. node = execjs.get()
  22. # 读取到 js 代码,并以 utf-8编码方式打开
  23. with open('decode.js', encoding='utf-8') as f:
  24. # JS 源文件编译
  25. ctx = node.compile(f.read())
  26. # 调用函数 , 并向其传参 ,获得响应数据
  27. t = ctx.eval(f'run("{data}")')
  28. return t
  29. def spider(eng):
  30. headers = {
  31. 'Accept': 'application/json, text/plain, */*',
  32. 'Accept-Encoding': 'gzip, deflate, br',
  33. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
  34. 'Cache-Control': 'no-cache',
  35. 'Connection': 'keep-alive',
  36. 'Content-Length': '239',
  37. 'Content-Type': 'application/x-www-form-urlencoded',
  38. 'Host': 'dict.youdao.com',
  39. 'Origin': 'https://fanyi.youdao.com',
  40. 'Pragma': 'no-cache',
  41. 'Referer': 'https://fanyi.youdao.com/',
  42. 'sec-ch-ua': "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"",
  43. "sec-ch-ua-mobile": "?0",
  44. "sec-ch-ua-platform": "Windows",
  45. "Sec-Fetch-Dest": "empty",
  46. "Sec-Fetch-Mode": "cors",
  47. "Sec-Fetch-Site": "same-site",
  48. "User-Agent": UserAgent().random,
  49. }
  50. cookies = {
  51. 'OUTFOX_SEARCH_USER_ID': '-2102182500@10.110.96.154',
  52. 'OUTFOX_SEARCH_USER_ID_NCOO': '1723343714.3489342',
  53. }
  54. page_url = 'https://dict.youdao.com/webtranslate'
  55. sign, my_time = get_sign()
  56. data = {
  57. "i": str(eng),
  58. "from": "auto",
  59. "to": "",
  60. "dictResult": "true",
  61. "keyid": "webfanyi",
  62. "sign": sign,
  63. "client": "fanyideskweb",
  64. "product": "webfanyi",
  65. "appVersion": "1.0.0",
  66. "vendor": "web",
  67. "pointParam": "client,mysticTime,product",
  68. "mysticTime": my_time,
  69. "keyfrom": "fanyi.web",
  70. }
  71. response = requests.post(url=page_url, headers=headers, data=data, cookies=cookies)
  72. return response.text
  73. # print(response.text)
  74. # print(f'response:::{response}')
  75. if __name__ == '__main__':
  76. while True:
  77. eng = input(f"请输入英文单词::")
  78. encode_text = spider(eng)
  79. # print(encode_text)
  80. res = json.loads(tran_data(encode_text))["dictResult"]["ec"]["word"]["trs"]
  81. for i in res:
  82. print(i)

(2)sign代码部分

  1. var Cry = require('crypto-js')
  2. // t 参数的声明
  3. // const t = (new Date).getTime();
  4. var t = '1683546118762'
  5. // function v(e) {
  6. // return r.a.createHash("md5").update(e.toString()).digest("hex")
  7. // }
  8. function v(e) {
  9. return Cry.MD5(e).toString()
  10. }
  11. var d = "fanyideskweb"
  12. var u = "webfanyi"
  13. function h(e, t) {
  14. return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
  15. }
  16. function sign(){
  17. var e = "fsdsogkndfokasodnaso"
  18. return h(e,t)
  19. }
  20. function run(){
  21. return [sign(),t]
  22. }

(3)decode代码部分

  1. // 导入crypto模块
  2. const crypto = require('crypto')
  3. // 创建算法对象
  4. function g(e) {
  5. return crypto.createHash("md5").update(e).digest()
  6. }
  7. function decode(t, o, n) {
  8. if (!t)
  9. return null;
  10. const a = Buffer.alloc(16, g(o))
  11. , c = Buffer.alloc(16, g(n))
  12. , i = crypto.createDecipheriv("aes-128-cbc", a, c);
  13. let s = i.update(t, "base64", "utf-8");
  14. return s += i.final("utf-8"),
  15. s
  16. }
  17. // 声明 变量 解密秘钥和偏移量
  18. var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
  19. var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
  20. // 参数为加密后的密文数据
  21. function run(encode_data) {
  22. return (decode(encode_data, k, iv))
  23. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/855684
推荐阅读
相关标签
  

闽ICP备14008679号