myzony

目的

因为某些原因需要将存放在 Google Chrome 内的书签导出到本地,所幸 Google Chrome 提供了导出书签的功能。

分析

首先在 Google Chrome 浏览器当中输入 chrome://bookmarks 来到书签管理页面,找到最右侧的三个点,选择导出书签,导出的文件是一个 HTML 文件,里面包含了所有书签的层级结构等信息。
使用 Notepad++ 打开该文件之后可以看到里面的内容如下:
https://i.loli.net/2018/06/24/5b2eb28ad92da.png
粗略一看貌似没什么问题,其实在里面的 <DT><P> 都缺少了闭合标签,所以在解析的时候需要将其去除掉。去除掉之后的 HTML 文件结构大概像这样:

<DL>
    <H3>文件夹标题</H3>
    <DL>
        <H3>子文件夹标题</H3>
        <A HREF="书签地址">子文件夹书签1</A>
        <A HREF="书签地址">子文件夹书签2</A>
    </DL>
    <A HREF="书签地址">书签1</A>
    <A HREF="书签地址">书签2</A>
</DL>

可以很明显看到这里是有一个层级关系的,所以我们可以通过递归来生成一个树形模型,生成之后,再遍历这个模型来根据这个树形结构来创建 MHTML 文件,并且进行归类。

实现

操作 HTML 文件在 .Net 下有一个很方便的第三方库,名字叫做 HtmlAgilityPack,通过这个库我们可以很方便地操作 HTML 文档,就跟 DOM 一样方便,而且它支持 XPath 选取。

项目地址:http://html-agility-pack.net/
GitHub 地址:https://github.com/zzzprojects/html-agility-pack
Nuget 地址:https://www.nuget.org/packages/HtmlAgilityPack/
通过 Nuget 安装该包到项目当中,引入 HtmlAgilityPack 命名空间,就可以开始编写代码了。

1.编写 HtmlResolver 解析器

建立一个 HtmlResolver 类,该类用于解析 Chrome 导出的书签:

public class HtmlResolver
{
    private HtmlDocument _htmlDocument = new HtmlDocument();
    /// <summary>
    /// 初始化 HTML 解析器
    /// </summary>
    /// <param name="htmlPath">Google Chrome 导出的书签 HTML 路径</param>
    public HtmlResolver(string htmlPath)
    {
        using (FileStream htmlFileStream = File.Open(htmlPath, FileMode.Open))
        {
            using (StreamReader htmlReader = new StreamReader(htmlFileStream))
            {
                // 移除干扰标签
                string htmlStr = htmlReader.ReadToEnd();
                htmlStr = htmlStr.Replace(@"<DT>", string.Empty).Replace(@"<p>", string.Empty);
                // 加载 HTML
                _htmlDocument.LoadHtml(htmlStr);
            }
        }
    }
}

在对象初始化的时候要求提供 Google Chrome 导出的书签 HTML 文件路径,并且读入 HTML 文件数据的时候移除掉之前所说的 <DT><P> 标签,方便后面 HtmlAgilityPack 进行解析,移除之后,HtmlDocument 通过 HTML String 初始化。

2.创建书签模型

当我们递归完成之后需要将数据存储在书签模型当中,方便后面生成 MHTML 文件的时候使用。

/// <summary>
/// 书签模型
/// </summary>
public class BookMarkModel
{
    /// <summary>
    /// 初始化书签模型
    /// </summary>
    /// <param name="name">书签名称</param>
    /// <param name="path">书签路径</param>
    /// <param name="url">绑定的 URL</param>
    /// <param name="childNodes">子节点集合</param>
    public BookMarkModel(string name, string path, string url = null, List<BookMarkModel> childNodes = null)
    {
        Name = name;
        Url = url;
        ChildNodes = childNodes;
        Path = path;
    }
    /// <summary>
    /// 书签名称
    /// </summary>
    public string Name { get; set; }
    /// <summary>
    /// 绑定的 URL
    /// </summary>
    public string Url { get; set; }
    /// <summary>
    /// 书签路径
    /// </summary>
    public string Path { get; set; }
    /// <summary>
    /// 子节点集合,如果没有则为 NULL
    /// </summary>
    public List<BookMarkModel> ChildNodes { get; set; }
}

该模型是一个典型的树形结构,之后我们就开始递归生成书签模型了。

3.递归生成树形模型

递归算法自己一直不太会写,写好这一个递归方法基本都花费了半天的时间

分类:

技术点:

相关文章: