PHP DOMDocument解析HTML时首个table闭合标签失效致嵌套问题
PHP DOMDocument处理多表格HTML的坑及解决办法
问题场景
做HTML转CSV工具时碰到个怪事:用DOMDocument解析多个独立表格的HTML,输出结果重复了后面两个表格的内容。
测试代码
$html = <<<HTML <table> <tr><td>A</td><td>Rose</td></tr> </table> <h1>Leave me behind</h1> <table> <tr><td>By</td><td>Any</td></tr> </table> <table> <tr><td>Other</td><td>Name</td></tr> </table> HTML; $dom = new \DOMDocument(); $dom->loadHTML($html, LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED); foreach ($dom->getElementsByTagName('table') as $table) { foreach ($table->getElementsByTagName('tr') as $row) { echo trim($row->nodeValue) . PHP_EOL; } }
预期 vs 实际输出
预期输出:
ARose ByAny OtherName
实际输出:
ARose ByAny OtherName ByAny OtherName
哪怕把第一个<table>的闭合标签删掉,结果还是一样——明显DOMDocument把后面两个表格错误嵌套进第一个表格里了。
问题根源
getElementsByTagName是递归查询:这个方法会把当前节点下所有层级的目标标签都找出来,不是只找直接子元素。当后面两个表格被嵌套进第一个后,遍历第一个表格时,会把三个表格的<tr>全捞出来。- HTML解析的容错特性:LIBXML的HTML解析器为了兼容不规范HTML,会自动调整DOM结构,这里就出现了非预期的节点嵌套,把独立的表格塞到了第一个里面。
解决办法
用XPath精准查询当前表格的直接子元素<tr>,不要递归查所有后代。代码改成这样:
$xpath = new \DOMXPath($dom); foreach ($dom->getElementsByTagName('table') as $table) { foreach ($xpath->query('./tr', $table) as $row) { echo trim($row->nodeValue) . PHP_EOL; } }
XPath的./tr只找当前表格节点的直接子节点里的<tr>,不会管嵌套在后代表格里的行,完美避开解析器的结构错误,输出就正常了。
内容的提问来源于stack exchange,提问作者tvanc
相关产品推荐
相关产品推荐

