实现功能
- 添加按钮
- 获取想要添加的位置
- 画按钮
- 提取知识库页面的标题和超链接
- 支持筛选标题 / 超链接
- 筛选使用for的情况须有:break或continue
- 给筛选后超链接
- 追加拼接参数“
/markdown?attachment=true&latexcode=false&anchor=true&linebreak=true
”拼接为指定类型资源链接
- 追加拼接参数“
- 将3中的URL数据导出超链接数据到本地文件(要求为一行一个的txt文件)
- 下载3中指定类型资源(参考《前端下载文件的几种方式》)
JavaScript脚本
```javascript // ==UserScript== // @name 😎建瓯最坏🐷导出指定语雀知识库指定文档的标题和链接并下载MarkDown // @namespace https:// www.yuque.com/jianouzuihuai // @version 22.05.20 // @description 通过油猴脚本导出语雀知识库中带锚点和换行的MarkDown文件 // @author 😎建瓯最坏🐷 // @include .yuque.com/lwb6kc/ // @exclude .yuque.com/// // @icon https:// cdn.nlark.com/yuque/0/2021/png/1632223/1635923818567-7f54e0a1-144e-4fd0-9856-a24e5b2664c5.png // ==/UserScript==
(function() { “use strict”
setTimeout(function()
{
// alert("设置按钮")
var buttonExport = document.createElement("button"); // 创建一个按钮
buttonExport.textContent = "🖨️ 导出知识库 🖨️"; // 按钮内容
buttonExport.style.width = '138px'; // 按钮宽度
buttonExport.style.height = '32px'; // 按钮高度
buttonExport.style.marginLeft = '16px'; // 距离左边16px
buttonExport.style.color = '#BA7CFF'; // 按钮文字颜色
buttonExport.style.border = '2px #BA7CFF solid'; // 边框
buttonExport.style.font = 'bold'; // 文字加粗
buttonExport.style.background = '#EEC6FF'; // 按钮底色
buttonExport.style.borderRadius = '4px'; // 按钮四个角弧度
var eleButton = document.getElementsByClassName("favor-wrapper")[0]
eleButton.appendChild(buttonExport)
// 过滤的数组
var list2Filter = []
// 保留的数组
var listLinkReport = []
// 当前知识库链接
var strBaseURI = document.baseURI
// 定位到文档树形列表中
var eleTreeList = document.getElementsByClassName("ant-tree-list-holder-inner")[0]
// 文档树形列表中的<a>标签
var arrTagA = eleTreeList.getElementsByTagName('a')
var nTagA = arrTagA.length
var iTitle, iLink
var arrReport = []
// 遍历子元素
for(var i = 0; i < nTagA; i++)
{
var iTagA = arrTagA[i]
// 2. 无“target”属性
// var bTarget = iTagA.getAttribute('target')
// if (bTarget == null)
// or 有“draggable” / “title”属性 的<a>标签
var bTitle = iTagA.getAttribute('title')
if (bTitle)
{
// 获取报错<a>标签中的“title”属性
iTitle = iTagA.getAttribute('title')
if (iTitle.indexOf("#") != -1)
{
// 井号会导致导出的数据被截断
iTitle = iTitle.replace("#", "-")
}
// 获取报错<a>标签中的“href”属性
iLink = iTagA.getAttribute('href')
arrReport.push([iTitle, iLink])
}
}
/*
console.log(listLinkReport)
console.log(list2Filter)
*/
// 遍历链接进行筛选
arrReport.forEach(FilterLinks)
// 按键点击事件
buttonExport.onclick = function ()
{
// 指定下载组织列表
var arrAPT = ['APT32', 'APT37', 'DarkHotel', 'Kimsuky', 'Lazarus', '白象', '毒云藤', '蓝宝菇', '蔓灵花', '旺刺', '响尾蛇']
var nLink = listLinkReport.length
var nAPT = arrAPT.length
// ——————————导出文件——————————
// Data2File("所有APT报告的标题和URL.csv", listLinkReport)
// Data2File("过滤的URL.csv", list2Filter)
// ——————————下载文件——————————
var iTitle, iURL, iExport
var listLink2Down = []
for(var d = 0; d < nLink; d++)
{
iTitle = listLinkReport[d][0]
iURL = listLinkReport[d][1]
// 遍历是否是下载列表中的组织
Loop2FilterAPT2DownLoad:
for(var p = 0; p < nAPT; p++)
{
var strAPT = arrAPT[p]
if (iTitle.startsWith(strAPT) == true)
{
listLink2Down.push([iTitle, iURL])
// console.log(iTitle, "\r\n", iURL)
iExport = iURL + "/markdown?attachment=true&latexcode=false&anchor=true&linebreak=true"
// console.log("下载链接:", iExport)
// 下载代码
// window.open(iExport)// 弹窗体验感不好
// setTimeout(window.location.href = iExport, 999)
// window.location.href = iExport// 多文件不延时下载会重置
// selfSleep(999)
break Loop2FilterAPT2DownLoad
}
}
}
Data2File("11个APT报告的标题和URL.csv", listLink2Down)
}
// alert("脚本结束")
function FilterLinks(iArr)
{
/*
for 过滤
1. if 过滤
1.1 for 保留
1.1.1 if (保留){保留后break outer}
1.1.2 if (不保留){不保留后break outer}
2. if 不过滤
2.1 if (次数和数组个数一样){保留}
*/
var iTitle = iArr[0]
var iHref = iArr[1]
// 排除的文章
var arrURI2Filter = ['toc?tempStore=1', 'toc', 'introduce']
// 排除的字符串(前缀)
var arrStr2Filter = ['area', 'group', 'theme', 'summary', 'classify']
// 不排除的字符串(前缀)
var arrStr2Save = ['theme_', 'summary_']
// 取最后一个“/”后的字符串
var arrSplit = iHref.split("/");
var strURI = arrSplit[arrSplit.length-1];
if (strURI.length)
{
// alert("切分后元素:" + strURI)
var strURL = strBaseURI + "/" + strURI;
// URI字符串是否在过滤【文章】数组中
var bURIinArr = (arrURI2Filter.indexOf(strURI) != -1)
if (bURIinArr)
{
// 在字符串数组中,直接过滤
list2Filter.push([iTitle, strURL])
}
else
{
// 不在,继续判断
// 是否在【过滤前缀】数组中
var bInArry = 0
var nFilterStr = arrStr2Filter.length
Loop2Filter:
// 排除的字符串(前缀)
for(var i = 0; i < nFilterStr; i++)
{
var iStr2Filter = arrStr2Filter[i]
if (strURI.startsWith(iStr2Filter))
{
// 如果前缀需要过滤
// 继续判断是否在【不过滤前缀】数组中
var nSaveStr = arrStr2Save.length
var bInArrySave = 0
Loop2Save:
for(var j = 0; j < nSaveStr; j++)
{
var iStr2Save = arrStr2Save[j]
if (strURI.startsWith(iStr2Save))
{
// 保留的路径
listLinkReport.push([iTitle, strURL])
// 跳出外层循环(outer改为Loop)
break Loop2Filter
}
else
{
bInArrySave += 1
}
}
if (bInArrySave == arrStr2Save.length)
{
// 过滤的路径
list2Filter.push([iTitle, strURL])
// 跳出外层循环(outer改为Loop)
break Loop2Filter
}
}
else
{
// 不匹配次数
bInArry += 1
}
// 不匹配次数和数组元素个数一致时,才是需要保留的URL
if (bInArry == nFilterStr)
{
// 保留的路径
listLinkReport.push([iTitle, strURL])
}
}
}
}
}
}, 1666)// 1.666秒后执行
//自定义js延时
function selfSleep(nTime)
{
var timeNow = Date.now();
while((Date.now() - timeNow) <=parseInt(nTime)){};
}
// 数据保存为文件
function Data2File(filename, data)
{
var data2Write = data.join("\n")
var elementSaveFile = document.createElement('a')
/*
以Json格式
// elementSaveFile.setAttribute('href', 'data:text/plaincharset=utf-8, ' + JSON.stringify(data))
以CSV格式
elementSaveFile.setAttribute('href', 'data:text/csv;charset=utf-8,' + data2Write)
*/
// 以原格式(数组)
elementSaveFile.setAttribute('href', 'data:text/plaincharset=utf-8, ' + data2Write)
// alert("导出文件开始")
// 利用A标签的download属性完成下载文件功能
elementSaveFile.setAttribute('download', filename)
// 设置为不显示的元素
elementSaveFile.style.display = 'none'
// 添加A标签元素
document.body.appendChild(elementSaveFile)
// 触发A标签元素的点击事件
elementSaveFile.click()
// A标签元素利用后释放
document.body.removeChild(elementSaveFile)
// alert("导出文件结束")
}
}
)()
```
建议先从单个导出开始写:
JavaScript - 通过油猴插件添加脚本功能给指定域的URL添加参数 | 一键语雀(单个)文档导出
运行效果
升级
22.03.25
为了能导出别人的知识库,修改脚本执行页面的“@include/@exclude/@match
”。
由于在别人的知识库处,无ClassName = “doc-*”的元素,改为在客主知识库页面都有的收藏按键“favor-wrapper
”旁。
去掉按钮旁边的边界“.style.border = 'none'
”:
去掉前:
22.03.28
越来越漂亮噜🤗🤗🤗