Skip to main content

Command Palette

Search for a command to run...

如何快速解析HTML里的WikiLink?

Published
•2 min read•View as Markdown
如何快速解析HTML里的WikiLink?

如何快速解析HTML里的WikiLink?

2024-09-12

#编码

前几天在 你好Astro! 中提到想把Obsidian的文章集成在博客中来,并实现CMS和Obsidian之间通过Wikilink的互相引用。

不过因为Ghost的API只提供了文章的HTML字符串,Remark衍生的WikiLink插件肯定都是无法使用的,而且目前Github上能找到的WikiLink相关的案例基本上都是整对于Markdown语法去解析的,对于我来说基本上没什么参考价值。

所以我只能走另外的路子了,不过方法无非就是以下几种

  • 正则过滤替换
  • 生成虚拟DOM处理

正则过滤

昨天晚上我倒是找到了 loveminimal 大佬写的一个姑且能用方案,但是这段代码有个不大不小的缺陷,就是正则会将所有标签内的 [[]] 内容都进行解析转换,所以 pre 代码块内的内容也会被解析成Wikilink,这意味着会无法控制的出现在任何你可能不想他出现的地方,所以目前这种方案暂时待选。

const wikilinks = (innerHtml) => { let _innerHtml = innerHtml; if (_innerHtml.indexOf('[[') > -1) { let _re = /![[(([\/-.*\$\&]|\w|\s|[^\x00-\xff]).\w+)\s|?\s(\d)]]/g; let _str = _innerHtml.replace(_re, '$1');

// 2. 后匹配替换链接 let _reLink = /[[(([\/-.*\$\:#]|\w|\s|[^\x00-\xff]))|?(([\/-.\\$]|\w|\s|[^\x00-\xff]))]]/g; // let _strLink = _str.match(_reLink); // let _strLink = _str.replace(_reLink, '$3'); let _strLink = _str.replace(_reLink, (val) => { val = val.replace(/[[]]/g, ''); let _arr = val.split(/\s|\s*/); let _relLink = _arr[0]; let _desc = _arr[1] ? _arr[1] : _arr[0];

// 检查链接描述是否包含 #锚点,形式有(我们假设当前文章名称为 test ,它有一个章节 ttt): // - 2.1. 孙子兵法#军争篇 - 此类可以正常识别 // - 2.2. cpu-是如何制造出来的#18.-等级测试 - https://example.com/cpu-是如何制造出来的#18.-等级测试 , // 此类锚点中包含特殊符号 . ,在新标签中打开,且无法正确定位到锚点 // - 2.3 test#ttt - https://example.com/test#ttt 默认会在新标签页中打开,需要优化为在当前页面滚动 // - 2.4 #ttt - 不能正常,会翻译为 https://example.com/#ttt ,丢失了当前页面路径 let _idx = _desc.indexOf('#'); if (_idx > -1) { // 2.4 if (_idx == 0) { _relLink = location.pathname.slice(1) + _desc; } else { // 2.3 _relLink = _desc.replace('#', '/#');

// 2.2 _relLink = _relLink.replace(/[.\、]/g, ''); } }

// console.log(_arr); // console.log(_desc); // return <a href="${_arr[0]}">${_desc}</a> return <a href="/${_relLink.replace(/\s/g, '-').toLowerCase()}">${_desc}</a>; // }); });

return _strLink; } };

虚拟化DOM

这个方式就是在Nodejs里将HTML字符串虚拟化成DOM,再去对DOM进行操作,虽然理论上可行的,但是效率应该是极低的,这个我是不愿意使用的,所以直接排除掉了。

没有其他方案了吗?

真的没有其他方案了吗?

我今天这样想着,抱着试一试的心态换着各种关键字在Github上搜索,运气不错的让我找到了这个个库:html-parse-stringify ,他可以将html字符串快速抽象成一个AST语法树,我的想法是通过遍历这个语法树来找出文本内容,再反序列化成html字符串不就好了?

所以我试着按着我的思路写了个Demo,好像的确可行,而且还可以配置那些情况下包含的 [[]] 不进行渲染。

import HTML from 'html-parse-stringify'; var ast = HTML.parse(page?.html); let fatherList = ''; function findTextNodes(node, text) { let result = []; fatherList += text; // 记录节点路径

// 检查当前节点,并做wikilink的识别和路径排除,这里做排除名单应该很方便 if (node.type === 'text' && node.content.startsWith('[[') && !fatherList.endsWith('precode')) // [!code highlight]{ node.content = node.content.replace('[[', 'test').replace(']]', ''); result.push(node); }

// 如果有子节点,递归遍历 if (node.children) { node.children.forEach((child) => { result = result.concat(findTextNodes(child, node.name)); }); }

return result; }

const result = [];

ast.forEach((node) => { const temp = findTextNodes(node, node.name); let fatherList = ''; });

const html = HTML.stringify(ast);

这是我目前能找到的最优方案了,不过这个方案应该也是偏向于解析成dom,不过这个库没有进行更多消耗资源的操作,应该是比传统的解析库性能更优秀的。

各种标签测试

note1

[[note2]]

note3

note4

  • note5

note6

note7

note8

[

@1900’Blog

All work and no play makes Jack a dull boy

@1900'Blog

](https://cms.1900.live/ni-hao-astro/)

https://cms.1900.live/ni-hao-astro/

测试一下文字包裹note9后的解析情况

note9

实际引用测试

已经初步完成联动,目前的考虑的策略顺序如下,

博客中

  1. 优先从CMS中去匹配文章 Title 一致的文章
  2. 如果博客内没有一直的则去Obsidian中匹配文件Title字段一致的文章
  3. 如果有多条数据只返回最前面一条
  4. 如果都没有则不进行转换

Obsidian中

  1. 优先匹配Obsidian中Title一致的文章
  2. 如果没有则去博客内匹配Title一致的文章
  3. 如果有多条数据只返回最前面一条
  4. 如果都没有则不进行转换

如Obsidian中的 About 这篇文章,在博客中以 [[About]] 写入,最终文章内就会解析成 About 。

因为Astro的本地路由都是基于网站根目录,所以我们只要将博客和Obsidian的文件分别进行静态生成,并在生成过程干涉 [[]] 的转换成 <a href=''><a/> 标签,然后给出正确的slug,便可以很方便的实现SPA形式的页面跳转。

完。

More from this blog

我的miniFlux主题

我的miniFlux主题 2024-10-06 #主题 , #分享 , #编码 最近Follow大火,虽然也在使用,不过我还是更喜欢miniFlux这种简单的阅读器。 之前从 yarr 切换到 miniflux 是因为 木木 大佬推荐的 miniflux 主题 Miniflux-Theme-Reeder,不过当时用了一段时间后发现各种不适,最后还是切换会默认主题,用了一段时间发现其实默认的miniflux已经非常好用了,不过需要做一点美化,所以简单做了一下调整,这里分享一下我的miniflux...

Oct 6, 20244 min read
我的miniFlux主题

MacType正确注册表全局热替换、渲染尝试

MacType正确注册表全局热替换、渲染尝试 2024-10-06 #Windows11 , #MacType , #折腾 前言 因为Windows系统字体渲染太垃圾,而我又没需求换4k,所以需要用到其他工具对字体进行渲染。而MacType自然是Windows平台的不二选择,我使用的时间估计已经有10年了把? 以前在XP、Win7上都能很完美的渲染,但是Windows10开始陆续出现了一些程序渲染不上的问题, 比如大部分UWP程序 而Chrome也因为安全策略更改也无法正常进行渲染,虽然百分...

Oct 6, 20243 min read
MacType正确注册表全局热替换、渲染尝试

两款CMS系统体验

两款CMS系统体验 2024-09-14 #编码 , #分享 , #工具箱 因为Ghost只提供文章的 HTML 代码,不提供 Markdown 的问题导致我曾经一度想更换CMS系统。但是将近800篇文章的转移工作十分麻烦,而且之前也尝试过用一些工具处理,无一都失败了,问题多是因为古早之前的很多文章存在格式问题,无法通过一些 HTML to Markdown 工具的转换规则,所以这个事也就一直没下定决定弄。 不过不影响我寻找下一位有动力让我这么做的CMS继任者。 最近发现了两款CMS工具,分别...

Sep 14, 20241 min read
两款CMS系统体验

🤣JUSTFUN 周刊:第14期

🤣JUSTFUN 周刊:第14期 2024-09-12 #周刊 一图 骑行路上 1. 种的辣椒 2. 骑行路上 流水账 2024年9月2日:忽然对Astro的语法有了新的理解,也许我可以做到? 2024年9月2日:如果地球是个学校,各个国家是上学的孩子,那么老鹰就是校园霸凌的恶霸。 2024年9月2日:自由的含意是:我能自由的干你,你不能还手。 2024年9月2日:我真是个傻逼,之前吐槽说Ghost文章丢失了,刚刚忽然发现特么是我写成Page了,压根不在Posts列表里。 2024...

Sep 12, 20241 min read
🤣JUSTFUN 周刊:第14期

1900's Blog

10 posts