2010-11-04 21 views
0

我正在使用PHP检索给定URL和XPATH的内容。 我使用DOMDocument/DOMXPath(与查询或评估)。DOMXpath查询/评估的xpath过长太长

对于小的xpath,我获得正确的结果,但对于更长的xpath,它不起作用。 (这XPath的似乎也不错(我Xpather(火狐插件)获得他们和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不起作用。

+1

给我们xml和xpath – 2010-11-04 13:44:10

+1

我添加一个例子。 – AlphaB 2010-11-04 14:04:23

回答

3

让我们通过这一步一步:

第1步:复制错误。

验证与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 .= '/'; 
} 

的最后一件事情之前的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投掷它离开或如果它真的不存在。它不在那里。显然,萤火虫将它插入,这可以解释为什么会得到与XPather结果(而不是为什么你和YQL得到它):

Screenshot showing page source and apparently bugged Firebug view

第3步:proofchecking和结论

我删除XPath中的<tbody>并重新执行脚本。没问题。返回“Gaby”。

虽然我第一次怀疑在Firebug的错误,亚历杭德罗评论说这会发生在IE的DeveloperTools了。然后我怀疑这是通过JavaScript添加的,但无法验证。经过一些更多的研究Alejandro指出我Why does firebug add <tbody> to <table>? - 它实际上既不是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')); 

将返回 “盖比” 为好。

+1

对不起,我的帖子不够精确。我希望返回一个节点(其中包含gaby)的DOMNodeList。 (和THX有关libxml的建议) – AlphaB 2010-11-04 14:35:15

+0

只见YQL类,但我更喜欢如果可能快速和更小的方式(指DOM或简单的XML lib中的使用而不是外部服务) – AlphaB 2010-11-04 14:41:45

+0

@AurelienB因为你的另一个问题建议你使用的Zend Studio请尝试以下操作:下载标记并将其保存到文件中。在Zend Studio中使用XML透视图打开文件。如果它无效,您可能必须先纠正它。然后使用左下方的XPath评估器来测试它是否找到您的节点。 – Gordon 2010-11-04 16:29:18