【问题标题】:Too long xpath with DOMXpath query/evaluate return nothing带有 DOMXpath 查询/评估的 xpath 太长不返回任何内容
【发布时间】:2011-05-05 01:51:21
【问题描述】:

我正在使用 PHP 来检索给定 URL 和 XPATH 的内容。 我使用 DOMDocument / DOMXPath(带有查询或评估)。

对于小的 xpath,我得到了正确的结果,但对于更长的 xpath,它不起作用。 (而且这个 xpath 似乎很好(我用 Xpather(firefox 插件)获得它们并用 YQL 重新测试它们)。

你对这个奇怪的问题有什么建议吗?

代码示例:

$doc = new DOMDocument();
$myXMLString = file_get_contents('http://stackoverflow.com/questions/4097230/too-long-xpath-with-domxpath-query-evaluate-return-nothing');
@$doc->loadHTML($myXMLString); //@ to suppress warnings 
                               //(good for not ending markup)
$xpath = new DOMXPath($doc);

$fullPath ="/html/body/small/path"; //it works
//$fullPath = "/html/body/full/path/with/lot/of/markup";//does not works
$entries = $xpath->query($fullPath);
//or ->evalutate($fullPath) (same behaviour)
//$entries return DOMNodeList (empty for a long path query, 
//                             correct for a small path query)

我使用属性限制进行测试,但似乎没有改变(使用小的 xpath 可以工作,越长它就越不工作)

示例: 对于当前页面:

$fullPath = "/html
              /body
               /div[4]
                /div[@id='content']
                 /div[@id='question-header']
                  /h1
                   /a";//works (retrieve the question title)
$fullPath = "/html
              /body
               /div[4]
                /div[@id='content']
                 /div[@id='mainbar']
                  /div[@id='question']
                   /table
                    /tbody
                     /tr[2]
                      /td[2]
                       /div[@id='comments-4097230']
                        /table
                         /tbody
                          /tr[@id='comment-4408626']
                           /td[2]
                            /div
                             /a"; //does'nt work 
                                  //(should retrieve 'gaby' from comment)

编辑:

我使用 SimpleXML lib 进行测试,我的行为完全相同(小查询的结果很好,长查询没有结果)。


编辑2:

我还通过删除一些第一个元素来剪切最长的 xpath,它可以工作。 顺便说一句,我真的不明白为什么完全正确的 xpath 不起作用。

【问题讨论】:

    标签: php xpath domxpath


    【解决方案1】:

    让我们一步一步来:

    第 1 步:复制错误。

    在确认 XPath 确实不会返回结果后,我写了一个小脚本来看看 XPath 在中断之前会走多远

    foreach (explode('/', $fullPath) as $segment) {
        $xpath .= trim($segment);
        echo '-------------------------------------------', PHP_EOL,
             'Trying: ', $xpath, PHP_EOL,
             '-------------------------------------------', PHP_EOL;
        echo $xp->evaluate("string($xpath)"), PHP_EOL;
        $xpath .= '/';
    }
    

    它会返回结果的最后一件事是

    /html/body/div[4]/div[@id='content']/div[@id='mainbar']/div[@id='question']/table
    

    第 2 步:检查标记

    所以我检查了DOMDocument::saveHTML() 返回的标记,看看它是什么样子的,没有<tbody> (为了可读性而重新格式化):

    <div id="question">
        <div class="everyonelovesstackoverflow" id="adzerk1"></div>
            <table>
                <tr><td class="votecell">
    

    然后我检查了这个页面,看看是 DOM 把它扔掉了,还是它真的不存在。它不在那里。显然,Firebug 插入了它,这可以解释为什么使用 XPather 得到结果(但不能解释为什么使用 YQL 得到结果):

    第 3 步:校对和结论

    我从 XPath 中删除了 &lt;tbody&gt; 并重新运行了脚本。没问题。返回“盖比”。

    虽然我首先怀疑 Firebug 中存在错误,但 Alejandro 评论说这也会发生在 IE 的 DeveloperTools 中。然后我怀疑这是由 JavaScript 添加的,但无法验证。经过更多研究后,Alejandro 将我指向 Why does firebug add &lt;tbody&gt; to &lt;table&gt;? - 实际上它既不是 Firebug 也不是 JavaScript,而是浏览器本身。

    所以修改一下我的结论:

    不要相信您在浏览器中看到的标记,因为它可能会被浏览器或其他技术修改。 DOM 只会下载直接提供的内容。如果你再次遇到类似的问题,你现在知道如何解决它了。


    一些补充说明

    除非您需要在将标记提供给 DOM 之前对其进行修改,否则您不必使用 file_get_contents() 来加载内容。你可以使用 DOM 的loadHTMLFile():

    $dom->loadHTMLFile('http://www.example.com/foo.htm');
    

    另外,抑制错误的正确方法是告诉libxml 使用它的内部错误处理程序。但您无需处理错误,只需清除它们即可。这只会影响与 libxml 相关的错误,例如解析错误(相对于所有 PHP 错误):

    libxml_use_internal_errors(TRUE);
    libxml_clear_errors();
    

    最后,可以针对上下文节点进行 xPath 查询。因此,虽然长 XPath 在查找时间方面很有效,但您可以简单地使用 getElementById() 来获取最深的已知节点,然后对它使用 XPath。

    换句话说:

    libxml_use_internal_errors(TRUE);
    $dom = new DOMDocument;
    $dom->loadHTMLFile('http://www.example.com/foo.htm');
    libxml_clear_errors();
    echo $xp->evaluate(
        'string(td[2]/div/a)', 
        $dom->getElementById('comment-4408626'));
    

    也会返回“Gaby”。

    【讨论】:

    • 对不起,我的帖子不够精确。我希望返回一个带有节点(包含 gaby)的 DOMNodeList。 (感谢有关 libxml 的建议)
    • 我看到了 yql 类,但如果可能的话,我更喜欢一种更快更小的方式(意味着 DOM 或 Simple xml lib 使用,但不是外部服务)
    • @AurelienB 因为您的其他问题表明您正在使用 Zend Studio,请尝试以下操作:下载标记并将其保存到文件中。在 Zend Studio 中使用 XML 透视图打开文件。如果它无效,您可能必须先更正它。然后使用左下角的 XPath 求值器来测试它是否找到了你的节点。
    • @Gordon:+1 非常好的答案。 IE 还添加了tbody。我还第一次检查了开发者工具栏。
    • @Gordon:是的。 here 是 SO 社区自己的答案。
    猜你喜欢
    • 1970-01-01
    • 2020-04-19
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    • 1970-01-01
    • 2021-04-24
    相关资源
    最近更新 更多