必须用 DOMDocument + DOMXPath 提取 HTML 标题:先启用容错模式、屏蔽警告、禁用自动补全;再用 XPath 查询 h1–h6 并取 textContent;需注入 UTF-8 声明防乱码;Laravel 中应封装为服务且不回退正则。

在 Laravel 8 中用正则表达式提取 HTML 标题(如 <h1>、<h2>)极易因换行、属性顺序、注释或嵌套标签崩溃,导致抓取结果错乱甚至空值;必须改用 DOMDocument + DOMXPath 组合实现结构化、可预测的标题提取。
加载 HTML 并规避自动补全与警告
第一步:创建 DOMDocument 实例,并启用容错模式——不加这一步,遇到缺少 <html><body> 的片段时,$dom->documentElement 会为 null,后续所有查询都返回空。
第二步:调用 libxml_use_internal_errors(true),屏蔽 malformed HTML 导致的 PHP Warning;否则脚本可能中断,且错误信息混入响应体。
第三步:使用 @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD) 加载内容。【LIBXML_HTML_NOIMPLIED 必须启用】 否则 DOMDocument 会强行包裹一层 <html><body>,使 XPath 定位偏离原始结构。
立即学习“前端免费学习笔记(深入)”;
用 DOMXPath 精准定位各级标题
方法一:提取全部标题标签(h1–h6)及其文本内容
创建 DOMXPath 实例:$xpath = new DOMXPath($dom);然后执行 $nodes = $xpath->query('//h1|//h2|//h3|//h4|//h5|//h6');遍历 $nodes 时,统一用 $node->textContent 获取纯文本——【绝不能用 $node->nodeValue】,后者会把内部 <script><style> 的内容也拼进来。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
方法二:按层级优先级提取首个非空标题
写 XPath 表达式 //h1[.!=''] | //h2[.!=''] | //h3[.!=''],用 $xpath->query() 执行后取 $nodes->item(0);这样能跳过空标题或占位符,直接拿到第一个有效标题。
处理 UTF-8 编码与特殊字符
如果原始 HTML 含中文或 emoji,但解析后出现乱码,说明 DOMDocument 默认按 ISO-8859-1 解析。必须在 loadHTML 前手动注入 XML 声明:$html = '' . $html;再传入 loadHTML(),否则 textContent 会丢字节。
这一步不可省略。哪怕 meta charset="utf-8" 已存在,DOMDocument 仍可能忽略它。
集成到 Laravel 8 服务中
在 app/Services/HtmlTitleExtractor.php 中定义 extractFromUrl(string $url): ?string 方法:
先用 GuzzleHttp\Client 获取 HTML 内容;接着按前述三步加载 DOM;然后用方法二 XPath 查询首个有效标题;最后 trim() 去首尾空白并返回字符串。
若 $nodes->length === 0,直接 return null;不要 fallback 到正则——那等于退回脆弱方案。


















