微信公众号文章秒转Markdown

508 阅读2分钟

起因

看到优秀公众号文章希望保存下来,每次都要选中网页内容然乎粘贴到富文本编辑器中比较麻烦! 于是想到通过脚本的方式把文章保存下来。

文章通过Google浏览器插件,油猴脚本(注入js)导出md文件

准备工作: 下载浏览器插件(fehelper类似油猴的插件)

fehelper.com/

image.png

image.png

编写脚本

借助turndown实现html转md,这个库对图片和代码块的处理不是很好,需要自定义图片和代码块的处理规则。

油猴脚本主要由四部分组成

  • 脚本名
  • 脚本生效范围(对域名进行匹配)
  • 第三方js
  • 开发者js

下面的编写的js脚本 启用规则后,浏览器打开微信公众号文章地址就可以看到提示+导出文件了。

效果如下:

image.png

源码如下:

image.png

// ==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 `![${alt}](${src})`;
                }
            });
            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);