DOMDocument + DOMXPath 是 PHP 解析 XML/HTML 最稳组合;loadXML() 要求严格合法 XML,loadHTML() 则容错解析 HTML。

DOMDocument + DOMXPath 是 PHP 处理 XML/HTML 数据提取最稳、最可控的组合,尤其适合字段明确、结构可预期的批量解析任务。别被“XML 解析”吓住——只要路径写对、错误捕获到位,5 行核心代码就能跑通。
DOMDocument::loadXML() 与 loadHTML() 的关键区别
两者底层都是构建 DOM 树,但语义和容错行为完全不同:
-
loadXML()要求输入是严格合法的 XML;哪怕一个未闭合标签或编码声明缺失,就会报DOMDocument::loadXML(): Start tag expected, ' 错误 -
loadHTML()是宽容模式,会自动补全<html>、<body>等缺失结构,适合处理真实世界中“不规范”的 HTML 页面 - 若你用
loadHTML()解析 XML(比如 RSS feed),很可能因自闭合标签(<item/>)被重写为<item></item>导致后续 XPath 查询失效 - 统一建议:XML 用
loadXML(),HTML 用loadHTML();混用前先用libxml_use_internal_errors(true)捕获并检查警告
DOMXPath::query() 中常见路径失效原因
写对 XPath 表达式只是第一步,真正卡住人的往往是环境细节:
- 命名空间未注册:XML 带
xmlns="http://example.com/ns"时,//item查不到任何节点;必须先调用$xpath->registerNamespace('ns', 'http://example.com/ns'),再写//ns:item - 文本节点 vs 元素节点:
//title/text()返回的是纯文本节点,不能直接调用getAttribute();要取属性必须定位到元素本身,如//title[@lang] - 空格干扰:XML 中换行缩进会被解析为
DOMText节点,$node->nodeValue可能含前后空格;建议统一用trim($node->textContent) - 默认只查文档根节点下的直接子节点:
$xpath->query('item')不等价于$xpath->query('//item');前者只在当前上下文节点下找一级,后者才全局搜索
从完整 XML 提取字段并生成精简 XML 文件
这不是“解析完再拼字符串”,而是复用 DOM 树做节点筛选+克隆,内存友好且结构干净:
立即学习“PHP免费学习笔记(深入)”;
- 用
DOMXPath::query()获取所有目标节点(如//itemcode | //stock | //price_eur),返回DOMNodeList - 新建一个空
DOMDocument,创建根节点(如<items>),再遍历DOMNodeList,对每个节点调用$newDoc->importNode($oldNode, true)并appendChild() - 关键点:
importNode()第二个参数设为true才会深拷贝子树;否则只复制空壳节点 - 最后用
$newDoc->save('feed_reduced.xml')写出文件;若需格式化,加一句$newDoc->formatOutput = true
为什么不用 SimpleXML?它在哪会翻车
SimpleXML 看似简单,但在真实数据流中容易突然断裂:
- 遇到非法字符(如控制字符 \x00-\x08)时,
simplexml_load_string()直接返回false,且不抛异常,很难定位源头 - 无法处理带命名空间又没前缀的默认命名空间(
xmlns="..."),->children('ns', true)会静默失败 - 修改节点后保存,会丢失原始声明(如
<?xml version="1.0" encoding="UTF-8"?>),导致后续系统拒绝导入 - 当你要把提取结果反向构造成新 XML 时,SimpleXML 没有
importNode这类跨文档操作能力,只能手动重建——反而更费劲
真正麻烦的从来不是语法,而是 XML 文件里藏的那些没声明的编码、不可见字符、嵌套过深的命名空间,以及上游系统随手改的一个空格。用 DOMDocument + DOMXPath,至少你能看清每一步节点在哪、内容是什么、错误从哪来。



















