我使用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不起作用。
答案 0 :(得分:3)
让我们一步一步地完成这个步骤:
第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">
然后我检查了这个页面,看看是不是它扔掉了它或者它是否真的不存在。它不存在。显然,Firebug会插入它,这可以解释为什么你用XPather获得结果(但不是为什么你用YQL得到它):
第3步:校对和结论
我从XPath中删除了<tbody>
并重新编写了脚本。没问题。返回“Gaby”。
虽然我首先怀疑Firebug中有一个错误,但Alejandro评论说这也会发生在IE的DeveloperTools中。然后我怀疑这是由JavaScript添加但无法验证。经过一些研究,亚历杭德罗向我指出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'));
也将返回“Gaby”。