閱讀528 返回首頁    go 阿裏雲 go 技術社區[雲棲]


javascript模仿QQ、微博動態識別網址轉換為超鏈接

JavaScript模仿QQ微博中自動識別網址,將其轉換為超鏈接,以“網頁鏈接”的形式展現,可以點擊直接打開。

需求:

項目中在做分享動態時要做一個支持分享網址鏈接直接點擊打開的功能。

方案原型:

將輸入的完整網址,在發表動態後,顯示時,將原網址鏈接轉換為超鏈接。

要求:

必須為完整網址,如:

網址不完整不能直接打開,如:

  • baidu.com;
  • yq.aliyun.com/articles/163180?spm=5176.blogshare163180.0.0.0sZT1B

擴展:

展示動態時,自動抓取網址鏈接網頁的標題,抓取到後超鏈接 顯示對應的標題,而不是 “網頁鏈接”,若抓取不到,默認顯示 “網頁鏈接”。

邊界問題:

識別網址時正確區分網址和非網址鏈接的文字,根據要求我們必須要求網址以 https:// 或者https:// 開頭,但網址結尾部分與文字的區分識別有些難度,主要是以下幾點:
1. 中文域名的網址, 如: .中國 .公司 .網絡
2. 網址中有中文, 如: https://52debug.net/pub/#g=1&p=推薦-所有成果
3. 網址參數是否正確結束,如:給大家推薦一篇文章,https://52debug.net/pub/#g=1&p=推薦-所有成果 這篇文章真的很精彩!
4. 超長網址的處理,如: https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=monline_4_dg&wd=.%E4%B8%AD%E5%9B%BD&oq=%25E5%258F%2591%25E5%258A%25A8%25E6%2580%2581%25E5%25A6%2582%25E4%25BD%2595%25E5%258C%25BA%25E5%2588%2586%25E7%25BD%2591%25E5%259D%2580%25E5%2592%258C%25E5%2585%25B6%25E4%25BB%2596%25E6%2596%2587%25E5%25AD%2597&rsv_pq=b732ea4400005764&rsv_t=b9c1Jn2RVdEK2W5qgmMmQIrGZA1GCyzAYAHZjZemjrte2U9CwZWdJK1ongeVhpfZHINH&rqlang=cn&rsv_enter=1&inputT=9291&rsv_sug3=110&rsv_sug1=77&rsv_sug7=101&bs=%E5%8F%91%E5%8A%A8%E6%80%81%E5%A6%82%E4%BD%95%E5%8C%BA%E5%88%86%E7%BD%91%E5%9D%80%E5%92%8C%E5%85%B6%E4%BB%96%E6%96%87%E5%AD%97

網址識別分析:

關於網址的識別,按最長串匹配的原則,可以構造一個有窮狀態自動機,用於識別網址。
1. 匹配 https://|https:// ,匹配成功執行 2,否則 9
2. 匹配 **.(合法域名後綴),匹配成功執行 3, 否則 9
3. 匹配 請求路徑 (/*){0,n},匹配成功執行 4,否則 7
4. 匹配 參數起始標識 ?(&)?,匹配成功執行 5,否則 7
5. 匹配 請求參數 (.*)=(.*),匹配成功執行 6, 否則 7
6. 匹配 參數分隔符 &,匹配成功執行 5, 否則 7
7. 匹配 網址與非網址部分的文字間隔識別符,可以是 中英文逗號,分號,空格,回車等,匹配成功則 8,否則 9
8. 成功匹配出網址,將其展示為超鏈接。
9. 識別網址失敗,不能識別的網址。

上麵這個識別規則是比較嚴謹些的規則,可以防止https://sfadsfsd 這樣類似的假鏈接。但考慮的可能也並不是很全麵,實現起來稍微有些複雜。參考了下QQ對於網址的識別過濾規則,試了下,QQ並沒有像上述過程一樣去嚴格識別網址,也沒有去支持識別中文域名或者網址參數為中文的情況,而僅僅是識別出https:// 之後,遇到空格回車等空白字符或者中文字符後就取從https:// 到空白或中文的內容作為網址,轉換為鏈接。而項目中確實不用考慮的太複雜,因此本著簡單實現的想法,就采用了以下簡化的識別規則:
1. 匹配 https://|https:// ,匹配成功執行 2,否則 4
2. 匹配任意ASCII碼表中非空白字符, 直到匹配到 空格,回車,換行符,製表符等空白字符或者非ASCII碼字符(如中文等),成功則3,否則 4
3. 成功匹配出網址,將其展示為超鏈接。
4. 識別網址失敗,不能識別的網址。

具體實現見代碼部分。

關於代碼:

新建js文件粘貼該代碼。在頁麵中引入該js,即可通過js調用var newstr = transUrl(str);來獲取轉換後的內容字符串。


;(function(window, document) {
    var matchUrl = function(str) {
        var Match = function(str, index, lastIndex) {
            this.str = str; //匹配到的url
            this.index = index; //匹配到的字符串在原字符串中的起始位置
            this.lastIndex = lastIndex; //匹配到的字符串在原字符串中的終止位置
        };
        /**
         * 匹配https://|https://
         */
        var regexp1 = new RegExp('(http:\/\/|https:\/\/)', 'g');
        var matchArray = new Array();
        var matchStr;
        while ((matchStr = regexp1.exec(str)) != null) {
            matchArray.push(new Match(matchStr[0], matchStr.index, regexp1.lastIndex));
            /*console.log(match)
            console.log(regexp1.lastIndex - match.index)*/
        }
        /**
         * 匹配網址分隔符,空白字符或非單字節字符及特殊標點符號,;`·"|'做分隔
         */
        var regexp2 = /\s|[^\u0000-\u00FF]|[,;`·"|']/g;
        for (var i = 0; i < matchArray.length; i++) {
            //考慮內容結束但沒有分隔符的情況
            var endIndex = matchArray[i + 1] ? matchArray[i + 1].index : str.length;
            var substr = str.substring(matchArray[i].lastIndex, endIndex);
            if ((matchStr = regexp2.exec(substr)) != null) { //匹配到分隔符
                if (regexp2.lastIndex > 1) { //提取網址
                    matchArray[i].lastIndex += regexp2.lastIndex - 1;
                    matchArray[i].str = str.substring(matchArray[i].index, matchArray[i].lastIndex);
                } else { //考慮"https:// "這樣的情況
                    matchArray.splice(i, 1);
                }
            } else if (endIndex == str.length) { //考慮內容末尾無空白符的情況
                if (matchArray[i].lastIndex < endIndex) { //提取網址
                    matchArray[i].lastIndex = endIndex;
                    matchArray[i].str = str.substring(matchArray[i].index, endIndex);
                } else { //考慮內容末尾無空白符,但也沒有實際網址的情況,如:"https://"
                    matchArray.splice(i, 1);
                }
            } //考慮內容並未到結尾,無空白符的情況,如:https://baidu.comhttps://sina.com,這種情況識別為一個網址
            else if (matchArray[i + 1] && (endIndex == matchArray[i + 1].index)) {
                matchArray.splice(i + 1, 1);
                i = i - 1;
            } else { //其他情況
                matchArray.splice(i, 1);
            }
            //console.log(matchStr);
            regexp2.lastIndex = 0;
        }
        return matchArray;
    }
    var transUrl = function(str) {
        var matchArray = matchUrl(str);
        var newstr = "";
        for(var i=0; i <= matchArray.length; i++){
            var beginIndex = i==0 ? 0 : matchArray[i-1].lastIndex;
            var endIndex = i == matchArray.length ? str.length : matchArray[i].index;
            var stri = str.substring(beginIndex, endIndex);
            var urli = "";
            if(i < matchArray.length){
                urli = " <a href=\"" + matchArray[i].str + "\" style=\"color: #005eac !important;\" target=\"_blank\">網頁鏈接</a> ";
            }
            newstr += stri + urli;
        }
        //console.log(newstr)
        return newstr;
    }
    window.transUrl = transUrl;
}(window, document));

最後更新:2017-08-13 22:25:46

  上一篇:go  報告|DDOS攻擊利潤可達95%
  下一篇:go  看年營收150億元的邁克菲如何做雲安全?