初识PyExecJS

基于已成功逆向后的JS代码,可以直接生成对应的加密参数,而PyExecJS则是可以在Python脚本中执行对应JS脚本并返回结果。

PyExecJS可以指定不同的解析环境,这里推荐使用Node.js

安装、配置PyExecJS

安装

pip install pyexecjs

指定解释环境

os.environ["NODE_PATH"] = os.getcwd()+"/node_modules"

将解释环境绑定为本地NODE包

print(execjs.get().name)

验证是否为Node,结果:

Node.js (V8)

调用DEMO

import execjs
import os

os.environ["NODE_PATH"] = os.getcwd() + "/node_modules"


def get_it(text: str) -> dict:
    with open('./yd.js') as f:
        js_code = f.read()

    js = execjs.compile(js_code)  # 这里同样可以使用cwd参数指定解释环境
    return js.call('getIt', text)


print(get_it('你好'))

实际应用

根据之前文章:有道翻译API逆向

代码如下:

import execjs
import os
import requests

os.environ["NODE_PATH"] = os.getcwd() + "/node_modules"


def get_it(text: str) -> dict:
    with open('./yd.js') as f:
        js_code = f.read()

    js = execjs.compile(js_code)  # 这里同样可以使用cwd参数指定解释环境
    return js.call('getIt', text)


def translate(text: str) -> str:
    api = 'https://fanyi.youdao.com/translate_o?smartresult=dict&smartresult=rule'

    header = {
        "Accept": "application/json, text/javascript, */*; q=0.01",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
        "Content-Length": "252",
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "Cookie": "OUTFOX_SEARCH_USER_ID=-1592407075@10.112.57.88; OUTFOX_SEARCH_USER_ID_NCOO=2043557013.325027;",
        "Host": "fanyi.youdao.com",
        "Origin": "https://fanyi.youdao.com",
        "Pragma": "no-cache",
        "Referer": "https://fanyi.youdao.com/",
        "sec-ch-ua": "\"Microsoft Edge\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"Windows\"",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "same-origin",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.56",
        "X-Requested-With": "XMLHttpRequest"
    }

    form = {
        "i": text,
        "from": "AUTO",
        "to": "AUTO",
        "smartresult": "dict",
        "client": "fanyideskweb",
        "doctype": "json",
        "version": "2.1",
        "keyfrom": "fanyi.web",
        "action": "FY_BY_REALTlME"
    }
    result = get_it(text)
    header['Cookie'] += f'___rl__test__cookies={result["lts"]}'
    form.update(result)

    res = requests.post(url=api, data=form, headers=header)
    res_json = res.json()
    try:
        return res_json['translateResult'][0][0]['tgt']
    except KeyError:
        return res_json


if __name__ == '__main__':
    print(translate('你好'))

输出结果:

hello

可能出现的问题

出现UnicodeEncodeError: 'gbk' codec...问题

在Python安装目录中Lib下找到subprocess.py文件然后通过搜索encoding方式找到类Popen,修改其__init__方法的encoding参数默认值为utf-8(原默认为None)即可。

    def __init__(self, args, bufsize=-1, executable=None,
                 stdin=None, stdout=None, stderr=None,
                 preexec_fn=None, close_fds=True,
                 shell=False, cwd=None, env=None, universal_newlines=None,
                 startupinfo=None, creationflags=0,
                 restore_signals=True, start_new_session=False,
                 pass_fds=(), *, user=None, group=None, extra_groups=None,
                 encoding='utf-8', errors=None, text=None, umask=-1, pipesize=-1):
        """Create new Popen instance."""