【实战】Java使用 Jsoup 将浏览器书签 HTML 转换为 JSON(支持多级目录)

一、背景说明

浏览器(Chrome / Edge / Firefox)导出的书签文件,通常是 Netscape Bookmark HTML 格式

在实际开发中,我们可能会遇到以下需求:

  • 将浏览器书签导入到自己的系统
  • 对书签进行结构化存储(如数据库、JSON)
  • 做书签导航站、搜索、同步工具等

本文通过 Jsoup + Fastjson2 ,演示如何将书签 HTML 解析为层级 JSON 结构 ,并支持 递归子目录


二、书签 HTML 格式说明

浏览器导出的书签文件大致结构如下:

html 复制代码
<DL>
  <DT><H3>文件夹</H3>
  <DL>
    <DT><A HREF="https://www.baidu.com/">百度</A>
    <DT><H3>子文件夹</H3>
    <DL>
      <DT><A HREF="https://map.baidu.com/">百度地图</A>
    </DL>
  </DL>
</DL>

关键点:

  • <H3>:表示一个文件夹
  • <A>:表示一个书签链接
  • <DL>:表示当前文件夹的内容
  • 文件夹与其内容是 H3 → 紧跟的 DL

三、技术选型

技术 作用
Jsoup 解析 HTML DOM
Fastjson2 构建 JSON 数据
JUnit + SpringBootTest 测试运行

四、完整示例代码

下面给出 完整可运行代码,包括导入、类定义和递归方法:

java 复制代码
package com.nav.test;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest
@RunWith(SpringRunner.class)
public class Bookmark {

    @Test
    public void main() {
        // 模拟浏览器导出的书签 HTML 内容
        String bookmarkContent = "<!DOCTYPE NETSCAPE-Bookmark-file-1>\n" +
                "<!-- This is an automatically generated file. -->\n" +
                "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">\n" +
                "<TITLE>Bookmarks</TITLE>\n" +
                "<H1>Bookmarks</H1>\n" +
                "<DL><p>\n" +
                "    <DT><H3 ADD_DATE=\"1632971641\" LAST_MODIFIED=\"1689686797\" PERSONAL_TOOLBAR_FOLDER=\"true\">书签栏</H3>\n" +
                "    <DL><p>\n" +
                "        <DT><A HREF=\"https://www.baidu.com/\" ADD_DATE=\"1689686710\">百度一下,你就知道</A>\n" +
                "        <DT><H3 ADD_DATE=\"1689686747\" LAST_MODIFIED=\"1689686798\">子书签</H3>\n" +
                "        <DL><p>\n" +
                "            <DT><A HREF=\"https://map.baidu.com/\" ADD_DATE=\"1689686769\">百度地图</A>\n" +
                "        </DL><p>\n" +
                "    </DL><p>\n" +
                "</DL><p>";

        // 使用 Jsoup 解析 HTML
        Document doc = Jsoup.parse(bookmarkContent);

        // 找到书签栏(Chrome 的 PERSONAL_TOOLBAR_FOLDER)
        Element mainFolder = doc.select("h3[personal_toolbar_folder]").first();

        // 递归处理
        JSONObject result = processFolder(mainFolder);

        // 输出 JSON
        System.out.println(result.toJSONString());
    }

    /**
     * 递归处理文件夹
     *
     * @param folderElement 文件夹对应的 H3 元素
     * @return JSONObject 结构 {name, links, subFolders}
     */
    private static JSONObject processFolder(Element folderElement) {
        JSONObject folderJson = new JSONObject();

        // 文件夹名称
        folderJson.put("name", folderElement.text());

        // 当前文件夹对应的 <DL>
        Element dl = folderElement.nextElementSibling();

        // 当前目录下的链接
        JSONArray links = new JSONArray();
        for (Element a : dl.select("> dt > a")) {
            JSONObject linkJson = new JSONObject();
            linkJson.put("name", a.text());
            linkJson.put("url", a.attr("href"));
            links.add(linkJson);
        }
        folderJson.put("links", links);

        // 子文件夹
        JSONArray subFolders = new JSONArray();
        for (Element h3 : dl.select("> dt > h3")) {
            subFolders.add(processFolder(h3));
        }
        folderJson.put("subFolders", subFolders);

        return folderJson;
    }
}

五、输出 JSON 示例

运行上面的代码,控制台输出类似:

json 复制代码
{
  "name": "书签栏",
  "links": [
    {
      "name": "百度一下,你就知道",
      "url": "https://www.baidu.com/"
    }
  ],
  "subFolders": [
    {
      "name": "子书签",
      "links": [
        {
          "name": "百度地图",
          "url": "https://map.baidu.com/"
        }
      ],
      "subFolders": []
    }
  ]
}

六、实现思路总结

  1. H3 表示文件夹
  2. H3 后面的 DL 是内容
  3. 使用 nextElementSibling() 关联目录
  4. 递归解析子文件夹
  5. 通过 > dt > a> dt > h3 选择器分别获取当前目录的书签和子文件夹
相关推荐
plainGeekDev1 小时前
GreenDAO → Room
android·java·kotlin
亦暖筑序6 小时前
Java 8老系统AI Workflow实战:把一次性AI对话升级成可恢复工作流
java·后端
敲代码的彭于晏7 小时前
Bean 生命周期完全图解:前端同学也能看懂的 Spring 核心机制
java·前端·后端
plainGeekDev8 小时前
ButterKnife → ViewBinding
android·java·kotlin
像我这样帅的人丶你还1 天前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩1 天前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构
tntxia1 天前
Mybatis的日志输入
java
亦暖筑序1 天前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
用户298698530141 天前
Java 实现 Word 文档加密与权限解除
java·后端
Yeats_Liao1 天前
14:Servlet中的页面跳转-Java Web
java·后端·架构