Files
netdisk-fast-download/parser/doc/JAVASCRIPT_PARSER_GUIDE.md
q c8a4ca7f16 feat: 添加getNoRedirect方法支持302重定向处理
- 在JsHttpClient中添加getNoRedirect方法,支持不自动跟随重定向的HTTP请求
- 修改baidu-photo.js解析器,使用getNoRedirect获取真实的下载链接
- 更新测试用例断言,验证重定向处理功能正常工作
- 修复百度一刻相册解析器302重定向问题,现在能正确获取真实下载链接
2025-10-21 17:47:59 +08:00

11 KiB
Raw Blame History

JavaScript解析器扩展开发指南

概述

本指南介绍如何使用JavaScript编写自定义网盘解析器支持通过JavaScript代码实现网盘解析逻辑无需编写Java代码。

快速开始

1. 创建JavaScript脚本

./custom-parsers/ 目录下创建 .js 文件,使用以下模板:

// ==UserScript==
// @name         我的解析器
// @type         my_parser
// @displayName  我的网盘
// @description  使用JavaScript实现的网盘解析器
// @match        https?://example\.com/s/(?<KEY>\w+)
// @author       yourname
// @version      1.0.0
// ==/UserScript==

// 使用require导入类型定义仅用于IDE类型提示
var types = require('./types');
/** @typedef {types.ShareLinkInfo} ShareLinkInfo */
/** @typedef {types.JsHttpClient} JsHttpClient */
/** @typedef {types.JsLogger} JsLogger */
/** @typedef {types.FileInfo} FileInfo */

/**
 * 解析单个文件下载链接
 * @param {ShareLinkInfo} shareLinkInfo - 分享链接信息
 * @param {JsHttpClient} http - HTTP客户端
 * @param {JsLogger} logger - 日志对象
 * @returns {string} 下载链接
 */
function parse(shareLinkInfo, http, logger) {
    var url = shareLinkInfo.getShareUrl();
    var response = http.get(url);
    return response.body();
}

// 导出函数
exports.parse = parse;

2. 重启应用

重启应用后JavaScript解析器会自动加载并注册。

元数据格式

必填字段

  • @name: 脚本名称
  • @type: 解析器类型标识(唯一)
  • @displayName: 显示名称
  • @match: URL匹配正则必须包含 (?<KEY>...) 命名捕获组)

可选字段

  • @description: 描述信息
  • @author: 作者
  • @version: 版本号

示例

// ==UserScript==
// @name         蓝奏云解析器
// @type         lanzou_js
// @displayName  蓝奏云(JS)
// @description  使用JavaScript实现的蓝奏云解析器
// @match        https?://.*\.lanzou[a-z]\.com/(?<KEY>\w+)
// @match        https?://.*\.lanzoui\.com/(?<KEY>\w+)
// @author       qaiu
// @version      1.0.0
// ==/UserScript==

API参考

ShareLinkInfo对象

提供分享链接信息的访问接口:

// 获取分享URL
var shareUrl = shareLinkInfo.getShareUrl();

// 获取分享Key
var shareKey = shareLinkInfo.getShareKey();

// 获取分享密码
var password = shareLinkInfo.getSharePassword();

// 获取网盘类型
var type = shareLinkInfo.getType();

// 获取网盘名称
var panName = shareLinkInfo.getPanName();

// 获取其他参数
var dirId = shareLinkInfo.getOtherParam("dirId");
var paramJson = shareLinkInfo.getOtherParam("paramJson");

// 检查参数是否存在
if (shareLinkInfo.hasOtherParam("customParam")) {
    var value = shareLinkInfo.getOtherParamAsString("customParam");
}

JsHttpClient对象

提供HTTP请求功能

// GET请求
var response = http.get("https://api.example.com/data");

// POST请求
var response = http.post("https://api.example.com/submit", {
    key: "value",
    data: "test"
});

// 设置请求头
http.putHeader("User-Agent", "MyBot/1.0")
    .putHeader("Authorization", "Bearer token");

// 发送表单数据
var formResponse = http.sendForm({
    username: "user",
    password: "pass"
});

// 发送JSON数据
var jsonResponse = http.sendJson({
    name: "test",
    value: 123
});

JsHttpResponse对象

处理HTTP响应

var response = http.get("https://api.example.com/data");

// 获取响应体(字符串)
var body = response.body();

// 解析JSON响应
var data = response.json();

// 获取状态码
var status = response.statusCode();

// 获取响应头
var contentType = response.header("Content-Type");
var allHeaders = response.headers();

// 检查请求是否成功
if (response.isSuccess()) {
    logger.info("请求成功");
} else {
    logger.error("请求失败: " + status);
}

JsLogger对象

提供日志功能:

// 不同级别的日志
logger.debug("调试信息");
logger.info("一般信息");
logger.warn("警告信息");
logger.error("错误信息");

// 带参数的日志
logger.info("用户 {} 访问了 {}", username, url);

// 检查日志级别
if (logger.isDebugEnabled()) {
    logger.debug("详细的调试信息");
}

实现方法

JavaScript解析器支持三种方法对应Java接口的三种同步方法

parse方法必填

解析单个文件的下载链接对应Java的 parseSync() 方法:

function parse(shareLinkInfo, http, logger) {
    var shareUrl = shareLinkInfo.getShareUrl();
    var password = shareLinkInfo.getSharePassword();
    
    // 发起请求获取页面
    var response = http.get(shareUrl);
    var html = response.body();
    
    // 解析HTML获取下载链接
    var regex = /downloadUrl["']:\s*["']([^"']+)["']/;
    var match = html.match(regex);
    
    if (match) {
        return match[1]; // 返回下载链接
    } else {
        throw new Error("无法解析下载链接");
    }
}

parseFileList方法可选

解析文件列表目录对应Java的 parseFileListSync() 方法:

function parseFileList(shareLinkInfo, http, logger) {
    var dirId = shareLinkInfo.getOtherParam("dirId") || "0";
    
    // 请求文件列表API
    var response = http.get("/api/list?dirId=" + dirId);
    var data = response.json();
    
    var fileList = [];
    for (var i = 0; i < data.files.length; i++) {
        var file = data.files[i];
        
        var fileInfo = {
            fileName: file.name,
            fileId: file.id,
            fileType: file.isDir ? "folder" : "file",
            size: file.size,
            sizeStr: formatSize(file.size),
            createTime: file.createTime,
            parserUrl: "/v2/redirectUrl/my_parser/" + file.id
        };
        
        fileList.push(fileInfo);
    }
    
    return fileList;
}

parseById方法可选

根据文件ID获取下载链接对应Java的 parseByIdSync() 方法:

function parseById(shareLinkInfo, http, logger) {
    var paramJson = shareLinkInfo.getOtherParam("paramJson");
    var fileId = paramJson.fileId;
    
    // 请求下载API
    var response = http.get("/api/download?fileId=" + fileId);
    var data = response.json();
    
    return data.downloadUrl;
}

同步方法支持

JavaScript解析器的方法都是同步执行的对应Java接口的三种同步方法

方法对应关系

JavaScript方法 Java同步方法 说明
parse() parseSync() 解析单个文件下载链接
parseFileList() parseFileListSync() 解析文件列表
parseById() parseByIdSync() 根据文件ID获取下载链接

使用示例

// 在Java中调用JavaScript解析器
IPanTool tool = ParserCreate.fromType("my_js_parser")
    .shareKey("abc123")
    .createTool();

// 使用同步方法调用JavaScript函数
String downloadUrl = tool.parseSync();           // 调用 parse() 函数
List<FileInfo> files = tool.parseFileListSync(); // 调用 parseFileList() 函数  
String fileUrl = tool.parseByIdSync();           // 调用 parseById() 函数

注意事项

  • JavaScript方法都是同步执行的无需处理异步回调
  • 如果JavaScript方法抛出异常Java同步方法会抛出相应的异常
  • 建议在JavaScript方法中添加适当的错误处理和日志记录

导出方式

支持三种导出方式:

方式1分别导出推荐

function parse(shareLinkInfo, http, logger) { }
function parseFileList(shareLinkInfo, http, logger) { }
function parseById(shareLinkInfo, http, logger) { }

exports.parse = parse;
exports.parseFileList = parseFileList;
exports.parseById = parseById;

方式2直接挂载

exports.parse = function(shareLinkInfo, http, logger) { };
exports.parseFileList = function(shareLinkInfo, http, logger) { };
exports.parseById = function(shareLinkInfo, http, logger) { };

方式3module.exports

function parse(shareLinkInfo, http, logger) { }
function parseFileList(shareLinkInfo, http, logger) { }
function parseById(shareLinkInfo, http, logger) { }

module.exports = {
    parse: parse,
    parseFileList: parseFileList,
    parseById: parseById
};

VSCode配置

1. 安装JavaScript扩展

安装 "JavaScript (ES6) code snippets" 扩展。

2. 配置jsconfig.json

custom-parsers 目录下创建 jsconfig.json

{
  "compilerOptions": {
    "checkJs": true,
    "target": "ES5",
    "lib": ["ES5"],
    "allowJs": true,
    "noEmit": true
  },
  "include": ["*.js", "types.d.ts"],
  "exclude": ["node_modules"]
}

3. 使用类型提示

// 引用类型定义
var types = require('./types');
/** @typedef {types.ShareLinkInfo} ShareLinkInfo */
/** @typedef {types.JsHttpClient} JsHttpClient */

// 使用类型注解
/**
 * @param {ShareLinkInfo} shareLinkInfo
 * @param {JsHttpClient} http
 * @returns {string}
 */
function parse(shareLinkInfo, http, logger) {
    // VSCode会提供代码补全和类型检查
}

调试技巧

1. 使用日志

function parse(shareLinkInfo, http, logger) {
    logger.info("开始解析: " + shareLinkInfo.getShareUrl());
    
    var response = http.get(shareLinkInfo.getShareUrl());
    logger.debug("响应状态: " + response.statusCode());
    logger.debug("响应内容: " + response.body().substring(0, 100));
    
    // 解析逻辑...
}

2. 错误处理

function parse(shareLinkInfo, http, logger) {
    try {
        var response = http.get(shareLinkInfo.getShareUrl());
        
        if (!response.isSuccess()) {
            throw new Error("HTTP请求失败: " + response.statusCode());
        }
        
        var data = response.json();
        return data.downloadUrl;
        
    } catch (e) {
        logger.error("解析失败: " + e.message);
        throw e; // 重新抛出异常
    }
}

3. 启用调试模式

设置系统属性启用详细日志:

-Dnfd.js.debug=true

常见问题

Q: 如何获取分享密码?

A: 使用 shareLinkInfo.getSharePassword() 方法。

Q: 如何处理需要登录的网盘?

A: 使用 http.putHeader() 设置认证头,或使用 http.sendForm() 发送登录表单。

Q: 如何解析复杂的HTML

A: 使用正则表达式或字符串方法解析HTML内容。

Q: 如何处理异步请求?

A: 当前版本使用同步API所有HTTP请求都是同步的。

Q: 如何调试JavaScript代码

A: 使用 logger.debug() 输出调试信息,查看应用日志。

示例脚本

参考 parser/src/main/resources/custom-parsers/example-demo.js 文件,包含完整的示例实现。

限制说明

  1. JavaScript版本: 仅支持ES5.1语法Nashorn引擎限制
  2. 同步执行: 所有HTTP请求都是同步的
  3. 内存限制: 长时间运行可能存在内存泄漏风险
  4. 安全限制: 无法访问文件系统或执行系统命令

更新日志

  • v1.0.0: 初始版本支持基本的JavaScript解析器功能