这个xpath查询(PHP)如何更灵活?

时间:2010-02-28 03:51:54

标签: php html xpath simplexml

我正在使用PHP的SimpleXML解析XHTML文档。我需要在文档中查询包含特定值的节点的一系列ul,然后找到该节点的父节点的直接先前兄弟代码将有助于解释!

给出以下虚拟xhtml:

<html>
<head></head>
<body>
...

<ul class="attr-list"> 
    <li>Active Life (active)</li> 
    <ul> 
        <li>Amateur Sports Teams (amateursportsteams)</li> 
        <li>Amusement Parks (amusementparks)</li> 
        <li>Fitness & Instruction (fitness)</li> 
        <ul> 
            <li>Dance Studios (dancestudio)</li> 
            <li>Gyms (gyms)</li> 
            <li>Martial Arts (martialarts)</li> 
            <li>Pilates (pilates)</li> 
            <li>Swimming Lessons/Schools (swimminglessons)</li>  
        </ul> 
        <li>Go Karts (gokarts)</li> 
        <li>Mini Golf (mini_golf)</li> 
        <li>Parks (parks)</li> 
        <ul> 
            <li>Dog Parks (dog_parks)</li> 
            <li>Skate Parks (skate_parks)</li> 
        </ul> 
        <li>Playgrounds (playgrounds)</li> 
        <li>Rafting/Kayaking (rafting)</li> 
        <li>Tennis (tennis)</li> 
        <li>Zoos (zoos)</li> 
    </ul> 
    <li>Arts & Entertainment (arts)</li> 
    <ul> 
        <li>Arcades (arcades)</li> 
        <li>Art Galleries (galleries)</li> 
        <li>Wineries (wineries)</li> 
    </ul> 
    <li>Automotive (auto)</li> 
    <ul> 
        <li>Auto Detailing (auto_detailing)</li> 
        <li>Auto Glass Services (autoglass)</li> 
        <li>Auto Parts & Supplies (autopartssupplies)</li> 
    </ul>
    <li>Nightlife (nightlife)</li>
    <ul>
        <li>Bars (bars)</li>
        <ul>
            <li>Dive Bars (divebars)</li>
        </ul>
    </ul>
</ul>

...
</body>
</html>

我需要能够查询ul.attr-list中的子元素,并发现它的“根”类别。我不能改变不同形式的xhtml。

所以,如果我把“画廊”作为一个类别,我需要知道它属于“艺术”“根”类别。或者,如果我有“dog_parks”,我需要知道它处于“活动”类别。以下代码完成了工作,但只假设最多有两个嵌套级别:

function get_root_category($shortCategoryName){

    $url = "http://www.yelp.com/developers/documentation/category_list";
    $result = file_get_contents($url);

    $dom = new domDocument();
    @$dom->loadHTML($result);
    $dom->preserveWhiteSpace = false;

    $sxml = simplexml_import_dom($dom);

    $lvl1 = $sxml->xpath("//li[contains(., '".$shortCategoryName."')]/parent::ul/preceding-sibling::li");
    $lvl2 = $sxml->xpath("//li[contains(., '".$shortCategoryName."')]/parent::ul/preceding-sibling::li/parent::ul/preceding-sibling::li");

    if($lvl2){
        return array_pop($lvl2);
    } else {
        return array_pop($lvl1);
    }
}

必须有一种更好的方法来编写XPath,因此只需要进行一次查询,并且对多个嵌套级别具有相对防弹性。

编辑::感谢那些指出此HTML无效的人。但是,页面的结构已设置,我无法编辑它;我只能将它作为一种资源使用,并且必须使它成为现实。

2 个答案:

答案 0 :(得分:1)

怎么样:

/html/body/ul/ul[count(descendant::li[contains(.,'dog_parks')]) > 0]/preceding-sibling::li

这应该适用于深层嵌套列表。它总是成为最上层的类别。

顺便说一下:我认为嵌套ul不是这样有效的。

答案 1 :(得分:1)

  

我需要查询一系列的ul   包含a的节点的文档   具体值,然后找到该节点   父母的直接上一个兄弟...

那就是(这里$v是您要查找的值):

$p = "/html/body//ul[li[contains(text(), '$v')]]/preceding-sibling::li[1]";
  • 确保检查$v不包含单引号,因为这会破坏XPath表达式。
  • 如果您只想查找整个单词,请使用:
    [contains(concat(' ', text(), ' '), concat(' ', '$v', ' '))]
  • 如果您想要看个案,请使用(我用缩写完整的字母):
    [contains(translate(text(), 'ABC…XYZ', 'abc…xyz'), '{strtolower($v)}')]
  • 请注意谓词可以嵌套。
  • 请注意,使用text()可确保仅考虑直接子文本节点。当您使用.时,<li>的整个“子树”将转换为字符串,您可能会获得比实际需要的更多结果。
  • 请注意,我将//运算符(descendant轴的快捷方式)限制在树的某个部分 - 如果您可以进一步限制它,请务必这样做。
    让XPath以//开头会比它需要的速度慢得多,因为检查整个文档的所有节点,即使那些在任何情况下都不会产生匹配的节点。
  • 正如其他人已经注意到的那样,HTML无效。