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

网易有道翻译逆向案例

【一】分析网站

(1)网站页面如图

(2)抓包

(3)分析抓到的包

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

(4)分析载荷

i: run
from: auto
to: 
dictResult: true
keyid: webfanyi
sign: a9856197613117e6524edc4b5076bd55
client: fanyideskweb
product: webfanyi
appVersion: 1.0.0
vendor: web
pointParam: client,mysticTime,product
mysticTime: 1683545856511
keyfrom: fanyi.web
  • 在载荷中发现了很多参数

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

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

【二】分析sign并逆向补充

(1)分析sign 的由来

  • 全局搜索sign值:

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

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

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

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

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

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

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

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

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

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

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

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

  • 向上翻看代码

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

  • 补全后的代码

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

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

  • 先声明 调用 该模块

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

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

    var Cry = require('crypto-js')
    
    // t 参数的声明
    const t = (new Date).getTime();
    
    // function v(e) {
    //     return r.a.createHash("md5").update(e.toString()).digest("hex")
    // }
    
    function v(e) {
        return Cry.MD5(e).toString()
    }
    
    var d = "fanyideskweb"
    var u = "webfanyi"
    
    function h(e, t) {
        return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    }
    
    h()
    // 无报错

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

  • 观察发现 e 为固定值

    e = "fsdsogkndfokasodnaso"
  • 补全代码

    var Cry = require('crypto-js')
    
    // t 参数的声明
    const t = (new Date).getTime();
    
    // function v(e) {
    //     return r.a.createHash("md5").update(e.toString()).digest("hex")
    // }
    
    function v(e) {
        return Cry.MD5(e).toString()
    }
    
    var d = "fanyideskweb"
    var u = "webfanyi"
    
    function h(e, t) {
        return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    }
    
    function sign(){
        var e = "fsdsogkndfokasodnaso"
        return h(e,t)
    }
    
    console.log(sign())
    
    //调用sigh函数,查看其生成代码
    //b73eba683a8cafb121cbf292d122e628

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

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

    var Cry = require('crypto-js')
    
    // t 参数的声明
    // const t = (new Date).getTime();
    var t = '1683546118762'
    // function v(e) {
    //     return r.a.createHash("md5").update(e.toString()).digest("hex")
    // }
    
    function v(e) {
        return Cry.MD5(e).toString()
    }
    
    var d = "fanyideskweb"
    var u = "webfanyi"
    
    function h(e, t) {
        return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    }
    
    function sign(){
        var e = "fsdsogkndfokasodnaso"
        return h(e,t)
    }
    
    console.log(sign())
    
    // 二者 相同

【三】Python 部分代码

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

  • 模块的导入

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

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

    var Cry = require('crypto-js')
    
    // t 参数的声明
    // const t = (new Date).getTime();
    var t = '1683546118762'
    // function v(e) {
    //     return r.a.createHash("md5").update(e.toString()).digest("hex")
    // }
    
    function v(e) {
        return Cry.MD5(e).toString()
    }
    
    var d = "fanyideskweb"
    var u = "webfanyi"
    
    function h(e, t) {
        return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
    }
    
    function sign(){
        var e = "fsdsogkndfokasodnaso"
        // 返回 sign 值
        return h(e,t)
    }
    
    function run(){
        // 调用 sign() 函数,返回 sign 值和 t 值
        return [sign(),t]
    }

(2)伪装请求头

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

    Accept: application/json, text/plain, */*
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
    Cache-Control: no-cache
    Connection: keep-alive
    Content-Length: 247
    Content-Type: application/x-www-form-urlencoded
    Cookie: OUTFOX_SEARCH_USER_ID=-2102182500@10.110.96.154; OUTFOX_SEARCH_USER_ID_NCOO=1723343714.3489342
    Host: dict.youdao.com
    Origin: https://fanyi.youdao.com
    Pragma: no-cache
    Referer: https://fanyi.youdao.com/
    sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"
    sec-ch-ua-mobile: ?0
    sec-ch-ua-platform: "Windows"
    Sec-Fetch-Dest: empty
    Sec-Fetch-Mode: cors
    Sec-Fetch-Site: same-site
    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进行了单独提取

      def spider(eng):
          # 请求头中的部分参数
          headers = {
              'Accept': 'application/json, text/plain, */*',
              'Accept-Encoding': 'gzip, deflate, br',
              'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
              'Cache-Control': 'no-cache',
              'Connection': 'keep-alive',
              'Content-Length': '239',
              'Content-Type': 'application/x-www-form-urlencoded',
      
              'Host': 'dict.youdao.com',
              'Origin': 'https://fanyi.youdao.com',
              'Pragma': 'no-cache',
              'Referer': 'https://fanyi.youdao.com/',
              'sec-ch-ua': "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"",
              "sec-ch-ua-mobile": "?0",
              "sec-ch-ua-platform": "Windows",
              "Sec-Fetch-Dest": "empty",
              "Sec-Fetch-Mode": "cors",
              "Sec-Fetch-Site": "same-site",
              "User-Agent": UserAgent().random,
          }
          # 请求头中的cookie
          cookies = {
              'OUTFOX_SEARCH_USER_ID': '-2102182500@10.110.96.154',
              'OUTFOX_SEARCH_USER_ID_NCOO': '1723343714.3489342',
          }
          # 需要请求的请求页 url
          page_url = 'https://dict.youdao.com/webtranslate'
          # 因为是携带参数的请求,所以是post请求
          # post请求需要携带请求参数(分析已经得知 sign 和 t 为变量,已经做了逆向获取)
          # 从 get_sign() 函数中获取到返回值 sign,mysticTime
          sign, mysticTime = get_sign()
          # 将 sign,mysticTime 添加到请求携带参数中
          data = {
              "i": str(eng),
              "from": "auto",
              "to": "",
              "dictResult": "true",
              "keyid": "webfanyi",
              "sign": sign,
              "client": "fanyideskweb",
              "product": "webfanyi",
              "appVersion": "1.0.0",
              "vendor": "web",
              "pointParam": "client,mysticTime,product",
              "mysticTime": mysticTime,
              "keyfrom": "fanyi.web",
          }
          # 对请求页发起请求
          response = requests.post(url=page_url, headers=headers, data=data, cookies=cookies)
          # 返回请求响应内容
          return response.text

(3)定义主函数入口

if __name__ == '__main__':
    res = spider('rain')
    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":"yŭ"}]],"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代码

  • 发现一个箭头函数

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

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

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

(3)解决办法

  • e.alloc

  • 由Buffer替换即可

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

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

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

(4)拼接数据

// 导入crypto模块
const crypto = require('crypto')

// 创建算法对象
function g(e) {
    return crypto.createHash("md5").update(e).digest()
}

function decode(t, o, n) {
    if (!t)
        return null;
    const a = Buffer.alloc(16, g(o))
        , c = Buffer.alloc(16, g(n))
        , i = crypto.createDecipheriv("aes-128-cbc", a, c);
    let s = i.update(t, "base64", "utf-8");
    return s += i.final("utf-8"),
        s
}

// 声明 变量 解密秘钥和偏移量
var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"
// 定义 加密后的密文数据
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=="
// 利用解密函数进行解密
console.log(decode(n, k, iv))

/*{"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","tg
tPronounce":"yŭ"}]],"type":"en2zh-CHS"}
*/
  • 通过打印结果 可以看到我们的解密函数正常运行

【五】Python部分代码

(1)定义 tran_data() 函数

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

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

// 导入crypto模块
const crypto = require('crypto')

// 创建算法对象
function g(e) {
    return crypto.createHash("md5").update(e).digest()
}

function decode(t, o, n) {
    if (!t)
        return null;
    const a = Buffer.alloc(16, g(o))
        , c = Buffer.alloc(16, g(n))
        , i = crypto.createDecipheriv("aes-128-cbc", a, c);
    let s = i.update(t, "base64", "utf-8");
    return s += i.final("utf-8"),
        s
}

// 声明 变量 解密秘钥和偏移量
var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"

// 参数为加密后的密文数据
function run(encode_data) {
    return (decode(encode_data, k, iv))
}

【六】代码整合

(1)Python代码部分

# requests 请求模块
import requests
# 随机UA模块
from fake_useragent import UserAgent
# json模块
import json
# 这里是执行js代码的相关模块
import subprocess
from functools import partial

# 必须先定义这个变量 再引入 execis模块 否则会报错
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
import execjs


def get_sign():

    node = execjs.get()
    with open('sign.js', encoding='utf-8') as f:
        ctx = node.compile(f.read())
    sign = ctx.eval('run()')
    return sign[0], sign[1]


def tran_data(data):
    # 创建 node 对象
    node = execjs.get()
    # 读取到 js 代码,并以 utf-8编码方式打开
    with open('decode.js', encoding='utf-8') as f:
        # JS 源文件编译
        ctx = node.compile(f.read())
    # 调用函数 , 并向其传参 ,获得响应数据
    t = ctx.eval(f'run("{data}")')
    return t


def spider(eng):
    headers = {
        'Accept': 'application/json, text/plain, */*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Content-Length': '239',
        'Content-Type': 'application/x-www-form-urlencoded',

        'Host': 'dict.youdao.com',
        'Origin': 'https://fanyi.youdao.com',
        'Pragma': 'no-cache',
        'Referer': 'https://fanyi.youdao.com/',
        'sec-ch-ua': "\"Chromium\";v=\"110\", \"Not A(Brand\";v=\"24\", \"Google Chrome\";v=\"110\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "Windows",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-site",
        "User-Agent": UserAgent().random,
    }
    cookies = {
        'OUTFOX_SEARCH_USER_ID': '-2102182500@10.110.96.154',
        'OUTFOX_SEARCH_USER_ID_NCOO': '1723343714.3489342',
    }

    page_url = 'https://dict.youdao.com/webtranslate'

    sign, my_time = get_sign()
    data = {

        "i": str(eng),
        "from": "auto",
        "to": "",
        "dictResult": "true",
        "keyid": "webfanyi",
        "sign": sign,
        "client": "fanyideskweb",
        "product": "webfanyi",
        "appVersion": "1.0.0",
        "vendor": "web",
        "pointParam": "client,mysticTime,product",
        "mysticTime": my_time,
        "keyfrom": "fanyi.web",
    }

    response = requests.post(url=page_url, headers=headers, data=data, cookies=cookies)

    return response.text
    # print(response.text)
    # print(f'response:::{response}')


if __name__ == '__main__':
    while True:
        eng = input(f"请输入英文单词::")
        encode_text = spider(eng)
        # print(encode_text)
        res = json.loads(tran_data(encode_text))["dictResult"]["ec"]["word"]["trs"]
        for i in res:
            print(i)

(2)sign代码部分

var Cry = require('crypto-js')

// t 参数的声明
// const t = (new Date).getTime();
var t = '1683546118762'
// function v(e) {
//     return r.a.createHash("md5").update(e.toString()).digest("hex")
// }

function v(e) {
    return Cry.MD5(e).toString()
}

var d = "fanyideskweb"
var u = "webfanyi"

function h(e, t) {
    return v(`client=${d}&mysticTime=${e}&product=${u}&key=${t}`)
}

function sign(){
    var e = "fsdsogkndfokasodnaso"
    return h(e,t)
}

function run(){
    return [sign(),t]
}

(3)decode代码部分

// 导入crypto模块
const crypto = require('crypto')

// 创建算法对象
function g(e) {
    return crypto.createHash("md5").update(e).digest()
}

function decode(t, o, n) {
    if (!t)
        return null;
    const a = Buffer.alloc(16, g(o))
        , c = Buffer.alloc(16, g(n))
        , i = crypto.createDecipheriv("aes-128-cbc", a, c);
    let s = i.update(t, "base64", "utf-8");
    return s += i.final("utf-8"),
        s
}

// 声明 变量 解密秘钥和偏移量
var k = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"
var iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"

// 参数为加密后的密文数据
function run(encode_data) {
    return (decode(encode_data, k, iv))
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值