.**? 会跨标签吞内容,因其仅按字面匹配 ,无法识别标签结构边界;它在遇到第一个 时停止,但若 html 中存在嵌套或畸形结构,仍可能误匹配;正则无法安全解析 html,应使用 dom 或 xpath。

为什么 .*? 会跨标签吞内容
用 <p>(.*?)</p> 看似能提取段落文本,但一旦 HTML 是 <p>第一段</p>
<div>第二段</div>
<p>第三段</p>,非贪婪的 .*? 仍会从第一个 <p></p> 一直匹配到最后一个
<div> 整块吞进去——因为 <code>. 默认不匹配换行符以外的任意字符,包括 和 <code>>。根本问题在于:它没能力识别“标签边界”,只认字面量
<p></p> 的合法闭合。- 必须限制匹配范围,禁止出现
字符,才能守住单层标签边界 - 若内容含换行,还得加
re.DOTALL(Python)或/s(JS),否则.跳过换行就断开了 - PHP 中对应的是
PREG_DOTALL修饰符,写在正则末尾,如/pattern/s
用 [^ 替代 <code>.*? 防跨标签
真正安全的内文捕获不是“尽可能少地匹配”,而是“明确禁止匹配起始尖括号”。[^ 表示“匹配零个或多个非 <code> 的字符”,天然卡死在下一个标签开头前。
配合反向引用确保成对,完整模式是:/<p>([^/i</p>。它能正确切分 <p>A</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill4156" title="Pptx To Html"><img
src="https://img.php.cn/upload/skill/000/000/081/178995432594388.jpg" alt="Pptx To Html" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill4156" title="Pptx To Html" class="overflowclass">Pptx To Html</a>
<p class="overflowclass">使用 MinerU 将 PowerPoint (.pptx) 演示文稿转换为 HTML,保留幻灯片内容与结构,生成可直接在网页使用的 HTML格式。</p>
</div>
<a rel="nofollow" href="/xiazai/skill4156" title="Pptx To Html" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<p>B</p>,不会把 B 当作 A 的一部分。
- 支持多行:加
/s后,[^ 仍有效,因为换行符不是 <code>,所以可跨行匹配纯文本 - 不支持属性:如果 HTML 是
<p class="intro">text</p>,该模式会失败——需扩展为/<p>]*>([^/is</p> - 不处理嵌套:遇到
<p>hello <strong>world</strong>!</p>,[^ 在 <code><strong></strong>处就停了,只捕获到hello
PHP 中提取带属性的标签内容要绕开引号陷阱
属性值里可能含 >(比如 title="a > b"),此时单纯用 [^>]* 会提前截断。更稳的方式是:先跳过标签开头所有非引号内容,再按引号配对吃掉属性值,最后才到内容区。
但实际项目中,90% 场景只需容忍常见属性。推荐这个平衡方案:/<p>]*>([^/is</p> —— \b 防止匹配到 <p1></p1>,[^>]* 吃掉属性(只要属性值不含 >)。
- 若真要兼容
title="a > b",得用递归式匹配或改用(*SKIP)(*FAIL)跳过整标签,再捕获文本节点(见下节) -
preg_match_all()第二个参数必须是字符串,不是文件路径;别直接传file_get_contents()结果而不检查是否为空 - 返回数组索引:匹配内容在
$matches[1],不是$matches[0](那是整个标签)
真正安全的文本提取:用 (*SKIP)(*FAIL) 跳过所有标签
当你要提取“所有可见文本”(比如做关键词高亮),而不是某类标签的内容,(*SKIP)(*FAIL) 是 PHP PCRE 下最接近安全的正则方案。它不尝试解析标签,而是把标签当“黑盒”跳过,只在剩余纯文本里搜索。
典型写法:/$pattern = '/]*>(*SKIP)(*FAIL)|' . preg_quote($keyword, '/') . '/i';
-
]*>匹配任意完整标签(开始、结束、自闭合),(*SKIP)(*FAIL)让引擎丢弃这部分,绝不回溯 -
|后的$keyword只会在跳过所有标签后的文本区域生效,完全避开属性和注释干扰 - 注意:它仍无法区分
<script></script>或<!-- comment -->里的文本,这些需额外预过滤
正则永远没法 100% 安全处理 HTML,哪怕加了 (*SKIP)(*FAIL)。嵌套、畸形结构、CDATA、SVG 标签都会让边界失效。真正需要鲁棒性的场景,DOMDocument + getElementsByTagName() 或 XPath 才是唯一解——正则只是临时胶带,不是承重墙。










