You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python替代无头浏览器获取HMAC/AES密钥的方案实现

替代无头浏览器高效提取HMAC-Secret和AES-key的解决方案

问题背景

原本通过Python脚本启动无头浏览器运行HTML片段,解析页面获取HMAC-Secret和AES-key,但浏览器启动耗时超过7秒,希望通过在Python内直接执行JavaScript的方式提升效率,最初尝试使用js2py。

js2py尝试中的问题

编写了如下Python代码,通过requests获取目标JS文件后用js2py执行:

import requests
import js2py

js2 = b""".then((r) => r.text())
            .then((b) => {
                const dfnm = /\x72\x65\x74\x75\x72\x6e\x20\x74\x79\x70\x65\x6f\x66\x20([a-z0-9]+)\[(\d+)\]\.([a-z0-9]+)\x20\x3d\x3d\x3d\x20\x27\x66\x75\x6e\x63\x74\x69\x6f\x6e\x27/gi.exec(b);
                const krm = /\x27\\x68\x27:[a-z0-9]+\.[a-z0-9]+\((\d+)\),'\\x61':[a-z0-9]+\.[a-z0-9]+\((\d+)\)/gi.exec(b);
                document.write(JSON.stringify({ HMAC_SECRET: window[dfnm[1]][dfnm[2]][dfnm[3]](krm[1]), AES_KEY: window[dfnm[1]][dfnm[2]][dfnm[3]](krm[2]) }));
            });"""

def get_hmac_aes_keys():
    response = requests.get('https://player.akamaized.net/html5player/core/html5-c5-player.js')
    if response.status_code == 200:
        js = response.text
        context = js2py.EvalJs()
        context.execute(js + js2)

get_hmac_aes_keys()

运行后出现两个核心问题:

  • 正则表达式中的转义字符引发SyntaxWarning: invalid escape sequence '\['警告
  • 执行时抛出KeyError: 3,原因是js2py无法模拟完整的浏览器环境,window、document等浏览器专属变量未定义,依赖这些变量的JS逻辑无法正常运行。

最终可行方案:Python调用Node.js+JSDOM

通过Python调用Node.js,结合JSDOM模拟完整浏览器环境,成功高效获取密钥,完整代码如下:

import subprocess
import json
import os

js_code = """
const { JSDOM } = require("jsdom");
const fs = require("fs");

async function simulateBrowser() {
    const fetch = await import('node-fetch').then(mod => mod.default);

    const dom = new JSDOM(`<!DOCTYPE html><body></body>`, {
        url: "https://player.akamaized.net/",
        resources: "usable",
        runScripts: "dangerously"
    });
    const window = dom.window;

    window.fetch = fetch;

    try {
        const response = await window.fetch("https://player.akamaized.net/html5player/core/html5-c5-player.js");
        const jsCode = await response.text();
        const scriptElement = window.document.createElement("script");
        scriptElement.textContent = jsCode;
        window.document.body.appendChild(scriptElement);

        const inlineScript = `
            fetch('https://player.akamaized.net/html5player/core/html5-c5-player.js')
                .then((r) => r.text())
                .then((b) => {
                    const dfnm = /\\\\x72\\\\x65\\\\x74\\\\x75\\\\x72\\\\x6e\\\\x20\\\\x74\\\\x79\\\\x70\\\\x65\\\\x6f\\\\x66\\\\x20([a-z0-9]+)\\\\[(\\\\d+)\\\\]\\\\.([a-z0-9]+)\\\\x20\\\\x3d\\\\x3d\\\\x3d\\\\x20\\\\x27\\\\x66\\\\x75\\\\x6e\\\\x63\\\\x74\\\\x69\\\\x6f\\\\x6e\\\\x27/gi.exec(b);
                    const krm = /\\\\x27\\\\\\\\x68\\\\x27:[a-z0-9]+\\\\.[a-z0-9]+\\\\((\\\\d+)\\\\),'\\\\\\\\x61':[a-z0-9]+\\\\.[a-z0-9]+\\\\((\\\\d+)\\\\)/gi.exec(b);
                    if (!dfnm || !krm) {
                        window.simulationResult = { error: 'Regex did not match any content', dfnm, krm };
                        return;
                    }
                    window.simulationResult = {
                        HMAC_SECRET: window[dfnm[1]][dfnm[2]][dfnm[3]](krm[1]),
                        AES_KEY: window[dfnm[1]][dfnm[2]][dfnm[3]](krm[2]),
                    };
                })
                .catch((err) => {
                    window.simulationResult = { error: err.message };
                });
        `;
        const inlineScriptElement = window.document.createElement("script");
        inlineScriptElement.textContent = inlineScript;
        window.document.body.appendChild(inlineScriptElement);

        await new Promise(resolve => setTimeout(resolve, 3000));

        if (window.simulationResult) {
            fs.writeFileSync("output.json", JSON.stringify(window.simulationResult));
        } else {
            fs.writeFileSync("output.json", JSON.stringify({ error: 'Unknown error' }));
        }
    } catch (err) {
        console.error('Error in simulateBrowser:', err);
        fs.writeFileSync("output.json", JSON.stringify({ error: err.message }));
    }
}

simulateBrowser().catch((err) => {
    console.error('Error in simulateBrowser:', err);
    fs.writeFileSync("output.json", JSON.stringify({ error: err.message }));
});
"""

with open("simulate_browser.js", "w") as f:
    f.write(js_code)

result = subprocess.run(["node", "simulate_browser.js"], capture_output=True, text=True)

if result.returncode != 0:
    print("Error executing JavaScript file")
    print(result.stderr)
    exit()

if not os.path.exists("output.json"):
    print("output.json file was not created")
    print("Node.js output:", result.stdout)
    exit()

with open("output.json", "r") as f:
    output = json.load(f)

if "error" in output:
    print("Error in JavaScript execution:", output["error"])
else:
    print(output)

方案说明

  1. 环境模拟:使用JSDOM创建包含window、document的完整浏览器环境,解决js2py环境简陋的问题
  2. fetch注入:Node.js原生无fetch,通过node-fetch包注入到模拟环境中
  3. 脚本加载:将目标JS文件以<script>标签形式插入到模拟DOM中,确保其在浏览器环境下执行
  4. 结果传递:将密钥提取结果写入JSON文件,Python读取该文件获取最终数据
  5. 效率提升:避免了无头浏览器的启动延迟,整体执行速度远快于原方案

内容的提问来源于stack exchange,提问作者Croques

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:50:56