【问题标题】:Xpath on HTML: difference between HTML source and DOM modelHTML 上的 Xpath:HTML 源代码和 DOM 模型之间的区别
【发布时间】:2013-07-16 04:07:17
【问题描述】:

我使用jQuery Xpath 插件浏览HTML 文档的层次结构。我不使用选择器是因为我需要处理服务器端框架的信息,它告诉我特定元素的 Xpath。

现在我观察到 DOM 不一定代表 HTML 文档的层次结构,并在这里找到了解决此问题的方法:Why does firebug add <tbody> to <table>?。这意味着,如果我的 HTML 文档确实包含以下代码:

<table>
  <tr>
    <td>Hello</td>
  </tr>
</table>

我的 DOM 将像这样表示后者:

<table>
  <tbody>
    <tr>
      <td>Hello</td>
    </tr>
  </tbody>
</table>

我的 Xpath 查询

jQuery(document).xpath('//table[1]/tr[1]/td[1]')

因此不再产生结果。

有没有办法避免 DOM 表示的合成元素?还是一种调整 Xpath 使其包含合成元素的方法?感谢您的帮助。

【问题讨论】:

  • 提供 XPath 的框架有问题,或者您的文档在服务器上被视为 XML/XHTML 与浏览器上的 HTML 不匹配。
  • 嗯,框架在服务器端,对文档的源代码进行操作和分析。我尝试以在客户端使用这些路径位置的方式扩展框架。

标签: jquery html dom jquery-plugins xpath


【解决方案1】:

好吧,在 jQuery 的帮助下,我制作了适用于我的用例场景的替代 XPath 解析器。解析器试图停留在我的输入指定的 XPath 上,但是如果 DOM 模型在路径的中间添加了一个新标签,其中路径的其余部分被包裹在这个单个元素中,解析器会识别这个添加并包含这个单个元素进入路径。这当然不适用于每个人和每个用例场景,但它适用于我。也许这个解决方案对其他人有帮助,至少在一些扩展之后:

var SloppyXPathParser = (function () {

    function childExists($cursor, element) {
        assertSelection($cursor);
        var $movedCursor = $cursor.children(element.name);
        if ($movedCursor.size() > element.index) {
            return jQuery($movedCursor.get(element.index));
        } else if ($cursor.children().size() == 1) {
            return childExists(jQuery($cursor.children().get(0)), element);
        } else {
            throw 'Cannot browse to \'' + element.name + '\' at index ' + element.index + '\'';
        }
    }

    function assertSelection($cursor) {
        if (!($cursor instanceof jQuery) || $cursor.size() != 1) {
            throw 'Selection is invalid: ' + $cursor.size();
        }
    }

    function parsePath(rawPath) {
        var nodes = rawPath.split('/');
        var regex = new RegExp('([a-zA-Z]+)\\[([0-9]+)\\]');
        var elements = [];
        var index = 0;
        jQuery(nodes).each(function (key, element) {
            if (element.length == 0) {
                return true;
            }
            if (!regex.test(element)) {
                throw 'Path element does not match regex: ' + element;
            }
            var matched = regex.exec(element);
            elements[index++] = { name: matched[1], index: matched[2] };
        });
        return elements;
    }

    function findElement(input) {

        var elements = parsePath(input);
        var $cursor = jQuery(document);
        jQuery(elements).each(function (key, element) {
            $cursor = childExists($cursor, element);
        });

        try {
            assertSelection($cursor);
        } catch (cause) {
            console.log('Exception: ' + cause);
            return false;
        }

        return $cursor.get(0);
    }

    return {
        find: function (input) {
            return findElement(input);
        }
    }
})();

var input = '/html[0]/body[0]/table[0]/tr[1]/td[1]';
SloppyXPathParser.find(input);

HTML 源代码为:

<html>
  <body>
    <table>
      <tr>
        <td>wrong</td>
        <td>wrong</td>
      </tr>
      <tr>
        <td>wrong</td>
        <td>right</td>
      </tr>
  </table>
  </body>
</html>

您可以通过例如检查浏览器向 DOM 添加 tbody 元素的 Firebug。解析器将识别这一点并跳过该条目。

【讨论】:

    【解决方案2】:

    如果您没有任何嵌套表,jQuery(document).xpath('//table[1]//tr[1]/td[1]') 应该适用于这两种情况。

    在更一般的情况下,您可以根据How do you select child-or-self (children + self)的答案进行调整

    在 XPath 1.0 中,这将转换为 jQuery(document).xpath('(//table|//table/tbody)/tr[1]/td[1]') 或更一般地转换为 jQuery(document).xpath('(//table|//table/node())/tr[1]/td[1]')

    【讨论】:

    【解决方案3】:

    只需将单个 / 转换为 tr 的双精度:

    //table[1]/tr[1]/td[1] -> //table[1]//tr[1]/td[1]

    这将匹配初始表格标记下方任意深度的表格行,因此您可以添加任意数量的&lt;tbody&gt; 标记。

    【讨论】:

    • 那么,如果文档包含嵌套表怎么办?这会增加歧义。
    • 假设您不再只匹配第一个 &lt;tr&gt;&lt;td&gt; 那么是的。您可以考虑添加一个类属性来帮助确定任何&lt;td&gt; 标记处于哪个级别,但我想这会有点脏。
    猜你喜欢
    • 1970-01-01
    • 2015-05-30
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 2015-10-01
    • 2012-06-28
    相关资源
    最近更新 更多