应分两步提取图片链接:先用宽松正则(如 /src\s*=\s*["']([^"']+)["']/i)提取所有候选URL,再对每个URL先urldecode(),再用pathinfo($url, PATHINFO_EXTENSION)获取后缀并比对小写白名单(jpg/jpeg/png/gif/webp/svg/bmp),同时单独处理data:image/开头的base64图片。

怎么用 preg_match_all 提取图片链接但排除非图片后缀
直接匹配所有 src 或 href 里的 URL 再过滤后缀,比靠正则一步断定“是不是图”更可靠。因为真实 HTML 里图片可能藏在 data-src、srcset,甚至带查询参数(如 avatar.jpg?size=large),正则硬写 \.(jpg|png|gif)$ 会漏掉或误杀。
推荐分两步:先用宽松正则抓出候选 URL,再用 PHP 的 pathinfo() 拿扩展名做白名单校验。
- 宽松正则示例:
/src\s*=\s*[\"']([^\"']+\.(?:jpe?g|png|gif|webp|svg|bmp))[\"']/i—— 注意这里只覆盖常见后缀,且允许大小写;但别写死$锚点,否则匹配不了带参数的 URL - 更稳妥做法是先提取所有疑似 URL:
/src\s*=\s*[\"']([^\"']+)["']/i,然后对每个$matches[1]做pathinfo($url, PATHINFO_EXTENSION) - 注意:URL 可能含编码字符(如
%20),提取后建议先urldecode()再解析后缀,否则pathinfo()可能返回空
为什么不能只靠正则判断后缀是否有效
正则在 URL 上做后缀断言很容易翻车。比如 https://example.com/image.png?v=1.2,如果正则写成 \.png$,$ 会卡在 v=1.2 后面,整个不匹配;改成 \.png(?:\?|\#|$) 又容易漏掉 .jpeg 或带路径参数的 /api/avatar/123.jpeg?token=abc。
更麻烦的是伪后缀:有些 CDN 返回 https://cdn.com/img?id=456,实际是 PNG,但正则根本看不出;还有内联 base64 图片(data:image/png;base64,...)——这种得单独处理,不能和普通 URL 用同一套规则。
立即学习“PHP免费学习笔记(深入)”;
- 遇到
data:开头的,用strpos($url, 'data:image/') === 0单独识别,后缀从 MIME 类型里取(如data:image/svg+xml→svg) -
srcset里多个 URL 用逗号或空格分隔,需先explode()再逐个 trim 和校验,别直接丢给正则 - 某些 CMS 输出的 URL 带 hash(如
logo-abc123.png),正则写\.png就够,别画蛇添足加\b或$
过滤时该用哪些后缀白名单
别照着 RFC 列全所有图像类型,实际网页中 95% 是 jpg、jpeg、png、gif、webp。SVG 和 BMP 出现频率低,但必须支持;ico 算不算图看需求——如果是提取「展示用图片」,通常不包括 favicon。
白名单建议写成小写数组,统一转小写再比对,避免大小写问题:
$valid_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'];
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
if (in_array($ext, $valid_exts)) { /* 保留 */ }- 别用
mime_content_type()或getimagesize()做实时校验——HTTP URL 需要远程请求,极慢且不可靠 - 如果源 HTML 来自可信后台,可放宽限制(比如接受
.avif);若来自用户提交内容,建议严格按白名单,防止.php.jpg这类绕过 - 注意
svg是文本格式,但浏览器当图片渲染,应纳入白名单;而pdf虽然能显示,一般不算「图片链接」,除非业务明确需要
常见错误:正则贪婪匹配导致截断或错位
写 /src="(.*)"/ 这种太宽泛的正则,遇到 <img src="a.jpg" alt="foo" src="b.png"> 会从第一个 " 匹配到最后一个 ",把整段 HTML 当成一个 URL。
必须用非贪婪 + 明确边界:
- 正确写法:
/src\s*=\s*[\"']([^\"']*)[\"']/i——[^\"']*?表示“直到下一个引号为止”,不是“任意字符” - 如果 HTML 属性用换行分隔(如
src="换行a.jpg"),默认.不匹配换行,得加s修饰符:/src\s*=\s*[\"']([^\"']*)[\"']/is - 提取多个属性时(如同时抓
src和data-src),别堆在一个正则里,分开调用preg_match_all()更清晰,也方便后续分别过滤
真正难的不是写正则,而是判断哪个 URL 算“有效图片”——这取决于你的业务场景:是生成缩略图?还是做外链检查?后者还得验证 HTTP 状态码,那就超出正则能力了。



















