首页 > 解决方案 > 通过正则表达式解析 HTML 最深匹配优先

问题描述

我正在尝试将 HTML 有序/无序列表递归解析为 OOP 结构并偶然发现了一个问题。假设我有这段代码:

$text = '
<ol>
    <li>
        <ul>
            <li>aaa</li>
            <li>bbb</li>
        </ul>
    </li>
    <li>fff</li>
    <li>
        <ol>
            <li>ccc</li>
            <li>ddd</li>
        </ol>
    </li>
</ol>
';
preg_match_all("/<ol>(.+?)<\/ol>/mis", $text, $matches);

问题是贪婪或懒惰的匹配似乎尽可能地浅:我想要的是相反,从最深到最浅,所以上面的表达式应该匹配:

<ol>
    <li>ccc</li>
    <li>ddd</li>
</ol>

任何想法?

标签: phpregex

解决方案


RegEx 应该只用于从 HTML 中提取特定数据(将其视为文本)。更多信息

将 HTML 解析为 OOP 结构是 DOMDocument::loadHTML() 所做的。OOP 结构是标准化的 DOM。使用 DOM 方法和 Xpath 表达式,您可以遍历、读取和操作数据。

$document = new DOMDocument();
$document->loadHTML($text);
$xpath = new DOMXpath($document);

foreach ($xpath->evaluate('//li[not(.//li)]') as $liLeaf) {
    echo "LABEL: ", $liLeaf->textContent, "\n";
    echo "INDEX: ", $xpath->evaluate('count(preceding-sibling::li)', $liLeaf), "\n";
    echo "LEVEL: ", $xpath->evaluate('count(ancestor::*[self::ol or self::ul])', $liLeaf), "\n";
    echo "IN: ", $xpath->evaluate('local-name(parent::*)', $liLeaf), "\n";
    echo "\n";
}

输出:

LABEL: aaa
INDEX: 0
LEVEL: 2
IN: ul

LABEL: bbb
INDEX: 1
LEVEL: 2
IN: ul

LABEL: fff
INDEX: 1
LEVEL: 1
IN: ol

LABEL: ccc
INDEX: 0
LEVEL: 2
IN: ol

LABEL: ddd
INDEX: 1
LEVEL: 2
IN: ol

推荐阅读