3

我正在尝试对外部网站的 HTML 进行排序,不幸的是,该网站的组织非常糟糕。数据可能如下所示:

<a class="title">Title One</a>
<a class="item">Item One</a>
<a class="item">Item Two</a>

<a class="title">Title Two</a>
<a class="item">Item One</a>
<a class="item">Item Two</a>    

我正在使用这样的 xpath 查询来获取标题:

$titles = $x->evaluate('//a[@class="title"]');

现在,我想列出标题及其下方的项目。不幸的是,这些元素都没有方便地包装在父 div 中,所以我不能只过滤父 div 中的所有内容。所以,我对这些项目使用这样的查询:

$titles = $x->evaluate('//a[@class="item"]');

理想情况下,我想做的只是检查当前标题元素下方的结果。所以,如果我循环并点击“标题一”,我只想检查出现在标题一和标题二之间的“项目”结果。有没有办法做到这一点?

在这里修改 HTML 不是一个选项。我知道这个问题有点荒谬,我的解释可能很糟糕,但如果有解决方案,它真的会帮助我!

谢谢大家。

4

2 回答 2

1

您可以先找到标题元素,然后使用->nextSibling()向前移动:

$html =<<<EOM
<a class="title">Title One</a>
<a class="item">Item One</a>
<a class="item">Item Two</a>

<a class="title">Title Two</a>
<a class="item">Item One</a>
<a class="item">Item Two</a>
EOM;

$d = new DOMDocument;
$d->loadHTML($html);
$x = new DOMXPath($d);
foreach ($x->query('//a[@class="title"]') as $node) {
    echo "Title: {$node->nodeValue}\n";
    // iterate the siblings
    while ($node = $node->nextSibling) {
       if ($node->nodeType != XML_ELEMENT_NODE) {
            continue; // skip text nodes
        }
        if ($node->getAttribute('class') != 'item') {
            // no more .item
            break;
        }
        echo "Item: {$node->nodeValue}\n";
    }
}

输出:

Title: Title One
Item: Item One
Item: Item Two
Title: Title Two
Item: Item One
Item: Item Two
于 2012-12-18T03:12:05.083 回答
0

您想选择元素的所有后续同级<a>元素,其中 theclass="title"再次是<a>元素,但class="item"and 具有前面的第一个同级<a>元素,class="title"并且是您开始查找的第一个元素。

例如在 xpath 中,例如您正在寻找第一个title元素:

//a[class="title"][1]

对于该元素,item元素如下:

//a[@class="title"][1]
    /following-sibling::a[
      @class="item" 
      and preceding-sibling::a[@class="title"][1] 
          = //a[@class="title"][1]
    ]

如果你想在代码中使用它,你可以通过创建一个相对于 title 元素的表达式并使用DOMelement::getNodePath()

$titles = $xp->query('//a[@class="title"]');
foreach ($titles as $title)
{
    echo $title->nodeValue, ":\n";
    $query = './following-sibling::a[@class="item" and 
              preceding-sibling::a[@class="title"][1] = ' .
              $title->getNodePath() . ']';
    foreach ($xp->query($query, $title) as $item)
    {
        echo ' * ', $item->nodeValue, "\n";
    }    
}

输出:

Title One:
 * Item One
 * Item Two
Title Two:
 * Item Three
 * Item Four
于 2012-12-18T03:24:22.170 回答