起因
看到优秀公众号文章希望保存下来,每次都要选中网页内容然乎粘贴到富文本编辑器中比较麻烦! 于是想到通过脚本的方式把文章保存下来。
文章通过Google浏览器插件,油猴脚本(注入js)导出md文件
准备工作: 下载浏览器插件(fehelper类似油猴的插件)
编写脚本
借助turndown实现html转md,这个库对图片和代码块的处理不是很好,需要自定义图片和代码块的处理规则。
油猴脚本主要由四部分组成
- 脚本名
- 脚本生效范围(对域名进行匹配)
- 第三方js
- 开发者js
下面的编写的js脚本 启用规则后,浏览器打开微信公众号文章地址就可以看到提示+导出文件了。
效果如下:
源码如下:
// ==FeHelperMonkey==
// @reminder 请不要删除这部分代码注释,这是FeHelper油猴脚本能正常工作的基本条件!当然,你可以按需修改这里的内容!
// @id mf_1750233765244
// @name 公众号文章转md
// @url-pattern *://mp.weixin.qq.com/*
// @enable true
// @require-js https://sslstatic.liaohuabiao.com/picgo/docs/turndown.3fcd9cc931bbe7fb.js
// @auto-refresh 0
// @updated 2025-07-13 15:21:21
// ==/FeHelperMonkey==
// 显示一个Toast,提示消息
var toast = (content, time) => {
return new Promise((resolve, reject) => {
let elAlertMsg = document.querySelector("#fehelper_alertmsg");
if (!elAlertMsg) {
let elWrapper = document.createElement('div');
elWrapper.innerHTML =
'<div id="fehelper_alertmsg" style=" position: fixed;top: 50%;left: 50%;z-index: 100;transform: translate(-50%, -50%);">' +
'<p style="background:#000;display:inline-block;color:#fff;text-align:center;' +
'padding:10px 10px;margin:0 auto;font-size:14px;border-radius:4px;">' + content +
'</p></div>';
elAlertMsg = elWrapper.childNodes[0];
document.body.appendChild(elAlertMsg);
} else {
elAlertMsg.querySelector('p').innerHTML = content;
elAlertMsg.style.display = 'block';
}
window.setTimeout(function () {
elAlertMsg.style.display = 'none';
resolve && resolve();
}, time || 1000);
});
};
// 简单的sleep实现
var sleep = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
// 下面开始这个简单的Demo...
((global) => {
toast('注入脚本成功', 3000)
setTimeout(() => {
var wx2md = () => {
var turndownService = new TurndownService()
// 覆盖默认的 img 规则
turndownService.addRule('image', {
filter: ['img'], // 只作用于 img 标签
replacement: function (content, node) {
const src = node.getAttribute('data-src') || '';
const alt = node.getAttribute('alt') || '';
return ``;
}
});
turndownService.addRule("pre", {
filter: ["pre"], // 只作用于 img 标签
replacement: function (content, node) {
console.log('node', node.childNodes,Object.keys(node))
let allText = []
const codeNode = [...node.childNodes].find(item=>{
return item.tagName === 'CODE'
})
if(codeNode){
allText = [...codeNode.childNodes].map(item=>{
let text = item.innerText
if(item.innerHTML.indexOf('<br>') > -1){
text = "\n"
}
return text ? text : ' '
})
}
return "```js \n" + allText.join('') + "\n```";
},
});
var markdown = turndownService.turndown($('#js_content')[0])
return markdown
};
global.wx2md = wx2md;
console.log('wxmd======>');
console.clear();
// 创建Blob对象
const blob = new Blob([wx2md()], {
type: 'text/markdown'
});
// 创建下载链接
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = $('#activity-name')[0].innerHTML + '.md';
// 模拟点击下载
a.click();
// 释放URL对象
URL.revokeObjectURL(url);
})
})(window);