无法从网站上抓取内容

时间:2011-05-29 15:25:49

标签: php xhtml web-scraping xml-namespaces domxpath

我正在尝试从网站中删除一些内容,但下面的代码无效(未显示任何输出)。 这是代码

$url="some url";
$otherHeaders="";   //here i am using some other headers like content-type,userAgent,etc
some curl to get the webpage
...
..
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$content=curl_exec($ch);curl_close($ch);

$page=new DOMDocument();
$xpath=new DOMXPath($page); 
$content=getXHTML($content);  //this is a tidy function to convert bad html to xhtml 
$page->loadHTML($content);    // its okay till here when i echo $page->saveHTML the page is displayed

$path1="//body/table[4]/tbody/tr[3]/td[4]";
$path2="//body/table[4]/tbody/tr[1]/td[4]";

$item1=$xpath->query($path1);
$item2=$xpath->query($path2);

echo $item1->length;      //this shows zero 
echo $item2->length;      //this shows zero

foreach($item1 as $t)
echo $t->nodeValue;    //doesnt show anything
foreach($item2 as $p)
echo $p->nodeValue;    //doesnt show anything

我确定上述xpath代码存在问题。 xpaths是正确的。我已使用xpaths检查了上述FirePath (a firefox addon)。我知道我错过了一些非常愚蠢的东西,但我无法理解。请帮忙。 我已经检查了从Wikipedia抓取链接的类似代码(绝对xpaths是不同的)并且它运行良好。 所以我不明白为什么上面的代码不适用于其他URLs。我正在使用HTML清除Tidy内容,所以我没有问题xpath没有对HTML进行竞争吗? 我检查nodelist之后的$item1=$xpath->query($path1)的长度是0,这意味着$xpath->query出现了问题,因为xpaths是正确的,因为我检查过与FirePath 我已按照指出修改了我的代码并使用loadXML代替loadHTML。 但是这给了我Entity 'nbsp' not defined in Entity的错误,所以我使用libxml选项LIBXML_NOENT来替换实体,但仍然存在错误。

5 个答案:

答案 0 :(得分:5)

是的,你遗漏了一些非常基本的东西:它是XHTML,所以你必须注册(并使用!)正确的namespace才能获得结果。

$xpath->registerNamespace('x', 'http://www.w3.org/1999/xhtml');

$path1="//x:body/x:table[4]/x:tbody/x:tr[3]/x:td[4]";
$path2="//x:body/x:table[4]/x:tbody/x:tr[1]/x:td[4]";

$item1=$xpath->query($path1);
$item2=$xpath->query($path2);

答案 1 :(得分:4)

似乎问题与XPath和命名空间有某种关系。 Php手册揭示了一个有趣的user comment

  

如果您已注册名称空间,   将您的XHTML等加载到您的   XPath的DOMDocument对象和   仍然无法让它工作,   检查以确保您没有使用过   DOMDocument的loadHTML()或   loadHTMLFile()函数。对于XHTML   始终使用XML版本,   否则你的XPath将永远不会   工作

您的代码使用loadHTML()

$content=getXHTML($content);  //this is a tidy function to convert bad html to xhtml 
$page->loadHTML($content);    // its okay till here when i echo $page->saveHTML the page is displayed

HTML不支持名称空间,因此loadHTML()可能不会在文档对象的元素上设置名称空间,即使原始文档(或Tidy输出的XHTML)也有这些名称空间。

因为您使用Tidy将文档转换为XHTML,我想您可以安全地使用loadXML()而不会遇到解析错误。请注意,它将要求输入是格式良好的XML。此外,它可能不知道像 这样的HTML预定义实体,如果是这种情况,它就无法用正确的字符值替换实体。如果出现此类问题,请尝试为loadXML()设置不同的选项。

答案 2 :(得分:2)

我听说FireFox会添加tbody元素(如果不存在)

除@ Tomalak的建议外,尝试删除/tbody位置步骤的XPath表达式。

另外,使用另一个工具the XPath Visualizer 来构建正确的XPath表达式,并立即查看他们正在选择的内容。

答案 3 :(得分:1)

这个问题让我想起很多时候问题的解决方案在于简单而不是并发症。我正在尝试namespaceserror corrections等,但解决方案只是要求仔细检查代码。 我的代码的问题是loadHTML()xpath initialization的顺序。最初订单是

$xpath=new DOMXPath($page);
$page->loadHTML($content);

通过这样做我实际上是在空文档上初始化xapth。现在通过首先使用dom加载html来反转订单,然后初始化xpath我能够获得所需的结果。同样建议通过从tbody移除xpath元素,因为firefox会自动插入它。所以正确的xpath应该是

$path1="//body/table[4]/tr[3]/td[4]";
$path2="//body/table[4]/tr[1]/td[4]";
感谢大家的建议和承诺。

答案 4 :(得分:0)

(尝试以下两者与其他答案结合使用,并与其他答案分开,因为它们是其他可能的警告。)

如果您的XPath无效,请尝试仅应用其中的一部分,以确保您确实遵循了正确的路径。所以做一些像:

$path1="//body";
$item1 = $xpath->query($path1);

foreach ($item1 as $t) {
    // to see the full XML of the returned node, as the nodeValue may be empty
    echo $t->ownerDocument->saveXML($t); 
}

然后继续将XPath增加到您想要的位置。

另外,如果您发现节点的nodeValue和textContent为空,则应确保使用正确的编码加载到DOMDocument中(例如,如果cURL响应返回UTF-8,则需要通过'UTF-8'作为构建DOMDOcument时的第二个参数。)