在 Javascript 中解析 HTML 的最佳方法

IT技术 javascript regex string
2021-03-12 18:44:12

我在学习 RegExp 并想出一个很好的算法来做到这一点时遇到了很多麻烦。我有这个需要解析的 HTML 字符串。请注意,当我解析它时,它仍然是一个字符串对象,而不是浏览器上的 HTML,因为我需要在它到达那里之前对其进行解析。HTML 如下所示:

<html>
  <head>
    <title>Geoserver GetFeatureInfo output</title>
  </head>
  <style type="text/css">
    table.featureInfo, table.featureInfo td, table.featureInfo th {
        border:1px solid #ddd;
        border-collapse:collapse;
        margin:0;
        padding:0;
        font-size: 90%;
        padding:.2em .1em;
    }
    table.featureInfo th {
        padding:.2em .2em;
        font-weight:bold;
        background:#eee;
    }
    table.featureInfo td{
        background:#fff;
    }
    table.featureInfo tr.odd td{
        background:#eee;
    }
    table.featureInfo caption{
        text-align:left;
        font-size:100%;
        font-weight:bold;
        text-transform:uppercase;
        padding:.2em .2em;
    }
  </style>

  <body>
    <table class="featureInfo2">
    <tr>
        <th class="dataLayer" colspan="5">Tibetan Villages</th>
    </tr>
    <!-- EOF Data Layer -->
    <tr class="dataHeaders">
        <th>ID</th>
        <th>Latitude</th>
        <th>Longitude</th>
        <th>Place Name</th>
        <th>English Translation</th>
    </tr>
    <!-- EOF Data Headers -->
    <!-- Data -->
    <tr>
    <!-- Feature Info Data -->
        <td>3394</td>
        <td>29.1</td>
        <td>93.15</td>
        <td>བསྡམས་གྲོང་ཚོ།</td>
        <td>Dam Drongtso </td>
    </tr>
    <!-- EOF Feature Info Data -->
    <!-- End Data -->
    </table>
    <br/>
  </body>
</html>

我需要像这样得到它:

3394,
29.1,
93.15,
བསྡམས་གྲོང་ཚོ།,
Dam Drongtso

基本上是一个数组......如果它根据其字段标题以及它们来自哪个表以某种方式进行匹配则更好,看起来像这样:

Tibetan Villages

ID
Latitude
Longitude
Place Name
English Translation

发现 JavaScript 不支持美妙的映射是一件令人沮丧的事情,我已经有了我想要的工作。然而,它是非常非常硬编码的,我想我应该使用 RegExp 来更好地处理这个问题。不幸的是,我真的很艰难:(。这是我解析字符串的函数(非常丑陋的 IMO):

    function parseHTML(html){

    //Getting the layer name
    alert(html);
    //Lousy attempt at RegExp
    var somestring = html.replace('/m//\<html\>+\<body\>//m/',' ');
    alert(somestring);
    var startPos = html.indexOf('<th class="dataLayer" colspan="5">');
    var length = ('<th class="dataLayer" colspan="5">').length;
    var endPos = html.indexOf('</th></tr><!-- EOF Data Layer -->');
    var dataLayer = html.substring(startPos + length, endPos);

    //Getting the data headers
    startPos = html.indexOf('<tr class="dataHeaders">');
    length = ('<tr class="dataHeaders">').length;
    endPos = html.indexOf('</tr><!-- EOF Data Headers -->');
    var newString = html.substring(startPos + length, endPos);
    newString = newString.replace(/<th>/g, '');
    newString = newString.substring(0, newString.lastIndexOf('</th>'));
    var featureInfoHeaders = new Array();
    featureInfoHeaders = newString.split('</th>');

    //Getting the data
    startPos = html.indexOf('<!-- Data -->');
    length = ('<!-- Data -->').length;
    endPos = html.indexOf('<!-- End Data -->');
    newString = html.substring(startPos + length, endPos);
    newString = newString.substring(0, newString.lastIndexOf('</tr><!-- EOF Feature Info Data -->'));
    var featureInfoData = new Array();
    featureInfoData = newString.split('</tr><!-- EOF Feature Info Data -->');

    for(var s = 0; s < featureInfoData.length; s++){
        startPos = featureInfoData[s].indexOf('<!-- Feature Info Data -->');
        length = ('<!-- Feature Info Data -->').length;
        endPos = featureInfoData[s].lastIndexOf('</td>');
        featureInfoData[s] = featureInfoData[s].substring(startPos + length, endPos);
        featureInfoData[s] = featureInfoData[s].replace(/<td>/g, '');
        featureInfoData[s] = featureInfoData[s].split('</td>');
    }//end for

    alert(featureInfoData);

    //Put all the feature info in one array
    var featureInfo = new Array();
    var len = featureInfoData.length;
    for(var j = 0; j < len; j++){
        featureInfo[j] = new Object();
        featureInfo[j].id = featureInfoData[j][0];
        featureInfo[j].latitude = featureInfoData[j][1];
        featureInfo[j].longitude = featureInfoData[j][2];
        featureInfo[j].placeName = featureInfoData[j][3];
        featureInfo[j].translation = featureInfoData[j][4];
        }//end for 

    //This can be ignored for now...
        var string = redesignHTML(featureInfoHeaders, featureInfo);
        return string;

    }//end parseHTML

因此,您可以看到该字符串中的内容是否发生变化,我的代码将被严重破坏。我想尽可能避免这种情况并尝试编写更好的代码。我感谢你能给我的所有帮助和建议。

6个回答

执行以下步骤:

  1. 创建一个新的 documentFragment
  2. 将您的 HTML 字符串放入其中
  3. 使用选择器来获得你想要的

为什么所有的解析工作-这将不反正工作,因为HTML是不是可解析通过正则表达式-当你有最好的HTML解析器可用?(浏览器)

您可以使用jQuery轻松遍历 DOM 并自动创建具有该结构的对象。

var $dom = $('<html>').html(the_html_string_variable_goes_here);
var featureInfo = {};

$('table:has(.dataLayer)', $dom).each(function(){
    var $tbl = $(this);
    var section = $tbl.find('.dataLayer').text();
    var obj = [];
    var $structure = $tbl.find('.dataHeaders');
    var structure = $structure.find('th').map(function(){return $(this).text().toLowerCase();});
    var $datarows= $structure.nextAll('tr');
    $datarows.each(function(i){
        obj[i] = {};
        $(this).find('td').each(function(index,element){
            obj[i][structure[index]] = $(element).text();
        });
    });
    featureInfo[section] = obj;
});

工作演示

该代码可以处理内部具有不同结构的多个表......以及每个表中的多个数据行......

featureInfo 将保存最终的结构和数据,并且可以像这样访问

alert( featureInfo['Tibetan Villages'][0]['English Translation'] );

或者

alert( featureInfo['Tibetan Villages'][0].id );
我知道,因为我已经深入研究了 jQuery,因为它似乎是 JavaScript 的革命:)。您的代码运行良好,我真的很感谢您向我展示了可用的内容:)
2021-04-19 18:44:12
将使用 jQuery,您可以var dom = $(htmlstring);通过将其作为$('table:has(.dataLayer)', dom). 更新答案..
2021-04-22 18:44:12
你发给我的代码真的很棒……但我认为让每个人都知道的是我正在展示 HTML 的事实。理想情况下,当我解析这个字符串时,它不是“HTML”,因为浏览器还没有看到它。我之前尝试过使用一些 DOM 方法等,但失败了。然后我意识到,如果这个 HTML 还没有发送到浏览器,我如何使用 DOM 函数……我是对的还是非常困惑?
2021-04-27 18:44:12
哇,这真是太棒了,你真是太好了。我对 JavaScript 还是很陌生,我有很多东西要学习!我将完成此代码,当我将其与我的应用程序一起使用时,我会通知您:)
2021-05-07 18:44:12
@elshae,没有提到我的代码使用 jQuery 框架。
2021-05-11 18:44:12

“正确”的方法是使用DOMParser. 像这样做:

var parsed=new DOMParser.parseFromString(htmlString,'text/html');

或者,如果您担心浏览器兼容性,请使用MDN 文档中polyfill

/*
 * DOMParser HTML extension
 * 2012-09-04
 * 
 * By Eli Grey, http://eligrey.com
 * Public domain.
 * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
 */

/*! @source https://gist.github.com/1129031 */
/*global document, DOMParser*/

(function(DOMParser) {
    "use strict";

    var
      DOMParser_proto = DOMParser.prototype
    , real_parseFromString = DOMParser_proto.parseFromString
    ;

    // Firefox/Opera/IE throw errors on unsupported types
    try {
        // WebKit returns null on unsupported types
        if ((new DOMParser).parseFromString("", "text/html")) {
            // text/html parsing is natively supported
            return;
        }
    } catch (ex) {}

    DOMParser_proto.parseFromString = function(markup, type) {
        if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
            var
              doc = document.implementation.createHTMLDocument("")
            ;
                if (markup.toLowerCase().indexOf('<!doctype') > -1) {
                    doc.documentElement.innerHTML = markup;
                }
                else {
                    doc.body.innerHTML = markup;
                }
            return doc;
        } else {
            return real_parseFromString.apply(this, arguments);
        }
    };
}(DOMParser));
我没有在这个答案的第二部分中编写代码,它来自 man 文档
2021-05-05 18:44:12
为什么你决定使用 HTMlHtmlElement 而不是: var iframe= document.createElement("iframe"); iframe.innerHTML = 标记;?
2021-05-06 18:44:12
@Mikalai 对不起,但我不会努力获得 IE9 兼容性。它被不到 1% 的人使用,而且确实比它的value更麻烦。
2021-05-08 18:44:12
它不适用于 ie9,SCRIPT600:此操作的目标元素无效。
2021-05-14 18:44:12

如果可以,更改服务器端代码(添加 JSON)

如果您是在服务器端生成结果 HTML 的人,您也可以在那里生成一个 JSON 并将其与内容一起传递到 HTML 中。您不必在客户端解析任何内容,所有数据都将立即可供您的客户端脚本使用。

您可以轻松地将 JSONtable作为data属性值放入元素中

<table class="featureInfo2" data-json="{ID:3394, Latitude:29.1, Longitude:93.15, PlaceName:'བསྡམས་གྲོང་ཚོ།', Translation:'Dam Drongtso'}">
    ...
</table>

或者,您可以data向包含数据的 TD添加属性,并仅解析那些使用 jQuery 选择器并从中生成 Javascript 对象的属性。不需要 RegExp 解析。

@elshae:我认为添加一个额外的元素属性值得麻烦,因为您在服务器上以结构化对象的方式拥有数据。从中生成 JSON 比解析 HTML 简单得多。如果您在一段时间内更改 HTML 本身会怎样?您还必须重新开发解析器。在其中包含 JSON 不会改变任何客户端功能。检查我添加的示例。
2021-04-19 18:44:12
我是页面的所有者,或者至少我可以访问完整的后端。问题是我正在使用一个为我生成这个 HTML 字符串的服务器,这不是我的选择。
2021-04-21 18:44:12
向 TD 添加数据属性 你能给我一个非常简单的例子吗?这是什么意思<td attr="latitude">92.34</td>???
2021-05-04 18:44:12
@elshae:换句话说,我想问您是否有权限和能力/知识来更改页面的服务器端代码?如果您这样做,那么我建议您实际发送带有页面本身的 JSON。
2021-05-10 18:44:12
如果我的回答不清楚,我很抱歉。理论上我确实可以将 JSON 发送到我的浏览器,但是由于我使用的服务器为我完成了这部分工作,因此可以说这是从我这里封装的。换句话说,进入服务器并重新发明将这些数据发送到浏览器的方式所需的工作量对我来说似乎不值得......
2021-05-14 18:44:12

使用 John Resig 的 *纯 javascript html 解析器

在这里查看演示

* John ResigjQuery的创造者