aardio 文档

aardio 范例: WebView2 - Readability 网页阅读模式提取正文、清洗、转为 Markdown

用法参考

import win.ui;
/*DSG{{*/
var winform = win.form(text="WebView2 - Readability 网页阅读模式提取正文、清洗、转为 Markdown";right=966;bottom=622)
winform.add(
btnExtract={cls="button";text="提取正文 / 复制 Markdown";left=758;top=6;right=950;bottom=36;dr=1;dt=1;z=3};
btnGo={cls="button";text="加载网址";left=648;top=6;right=748;bottom=36;dr=1;dt=1;z=2};
custom={cls="custom";left=0;top=45;right=966;bottom=622;db=1;dl=1;dr=1;dt=1;z=4};
editUrl={cls="edit";left=12;top=10;right=641;bottom=34;dl=1;dr=1;dt=1;edge=1;multiline=1;z=1}
)
/*}}*/

import web.view;

// 使用 custom 控件作为宿主窗口(方便我们在顶部保留输入框和按钮)
var wb = web.view(winform.custom);

/*
通过 wb.export 将 aardio 函数导出为 JavaScript 全局函数。
提取后的 JS 对象会自动转换为 aardio 中的表(table)。
*/
wb.export({
    saveCleanData = function(article){
        if( !article || !article.content ){
            winform.msgbox("提取失败,此页面可能没有标准的文章正文格式。");
            return true;
        }

        // 容错处理,防止对应字段为 null
        var title = article.title : "未知标题";
        var byline = article.byline : ""; 

        import web.turndown;

        var turndownService = web.turndown( codeBlockStyle = "fenced" );
        turndownService.remove('script');
        turndownService.remove('style');

        //启用 gfm(GitHub Flavored Markdown)插件。
        turndownService.useGfm()

        // 添加自定义规则处理带类名的代码块
        turndownService.addRule('codeBlock', "{
            filter: function(node) {
                return node.nodeName === 'PRE' && node.classList.contains('code');
            },
            replacement: function(content, node) {
                var language = node.classList.item(1)  || ''; 
                language = language.replace(/^language-/, '');
                return '```' + language + '\n' + content.trim() + '\n```';
            }
        }");


        //用法参考: https://github.com/mixmark-io/turndown
        var md = turndownService.turndown( 
            article.content
        );

        import string.markdown;
        var content = string.markdown().render(md);

        import win.clip;
        win.clip.write(md)


        // 构造干净的阅读模式 HTML(加入一些优美的排版 CSS)
        var html = `
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="utf-8">
            <title>` + title + `</title>
            <style>
                body {
                    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
                    line-height: 1.8;
                    color: #333;
                    max-width: 800px;
                    margin: 0 auto;
                    padding: 20px;
                    background: #f4f5f7;
                }
                article {
                    background: #fff;
                    padding: 40px;
                    border-radius: 8px;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.05);
                }
                h1 { text-align: center; color: #222; font-size: 2em; margin-bottom: 0.5em; }
                .byline { text-align: center; color: #888; font-size: 0.9em; margin-bottom: 30px; }
                img { max-width: 100%; height: auto; display: block; margin: 20px auto; border-radius: 4px; }
                p { margin-bottom: 1.2em; font-size: 1.1em; }
                a { color: #1a73e8; text-decoration: none; }
                a:hover { text-decoration: underline; }
                pre, code { background: #f1f3f5; padding: 4px 8px; border-radius: 4px; font-family: Consolas, monospace; }
                pre { padding: 15px; overflow-x: auto; }
            </style>
        </head>
        <body>
            <article>
                <h1>` + title + `</h1>
                <div class="byline">` + byline + `</div>
                ` + content + `
            </article>
        </body>
        </html>
        `;

        /*
        被 JS 回调的 aardio 函数中应避免进行阻塞调用。
        这里使用 setTimeout 异步更新 WebView 内容。
        */
        winform.setTimeout(
            function(){
                wb.html = html;
            }
        );

        return true; // 务必返回非 null 值
    }
})

// 加载网址事件
winform.btnGo.oncommand = function(id,event){
    var url = winform.editUrl.text;
    if(#url) wb.go(url); 
}

// 提取正文事件
winform.btnExtract.oncommand = function(id,event){
    winform.btnExtract.disabled = true;

    import inet.http;

    // 1. 获取 Readability.js 的代码文本,注意 inet.http.get 是创建新线程下载
    var readabilityJs = inet.http.get("https://lib.baomitu.com/readability/0.6.0/Readability.min.js");

    if(!readabilityJs){
        winform.msgbox("无法下载 Readability.js 库,请检查网络!");
        winform.btnExtract.disabled = false;
        return;
    }

    // 2. 拼接我们自己的提取执行代码
    var script = readabilityJs + `
    ;(() => {
        try {
            // Readability 库在解析时会破坏原有的 DOM 结构,因此必须传入 document 的深度克隆
            let documentClone = document.cloneNode(true);
            let reader = new Readability(documentClone);
            let article = reader.parse();

            // 调用 aardio 导出的 saveCleanData 函数,把提取到的 JSON 抛给 aardio
            window.saveCleanData(article);
        } catch(e) {
            window.saveCleanData(null);
        }
    })();
    `;

    //这比在页面内创建 <script src="..."> 标签更稳定,无视目标网站的 CSP 拦截。
    wb.doScript(script);

    winform.btnExtract.disabled = false;  
}

// 监听导航事件,用户在网页内点击跳转时自动同步网址栏
wb.onDocumentInit = function(url) {
    winform.editUrl.text = url;
}

// 启动时自动加载预设的默认网址
winform.btnGo.oncommand();

winform.show();
win.loopMessage();
Markdown 格式