≡articles //snippets ./categories $uses ~about /search

SEO 体检脚本:从 51 条短 meta 到一条命令扫完

SEO 体检脚本,是这个月我做的最值的一件事。起因是 Bing Webmaster 后台的一张报错清单:20 页图片缺 alt、51 条 meta description 太短,外加 H1 结构问题。逐页手改到第三页我就放弃了,决定把整个排查过程固化成脚本。这篇文章记录从报错到脚本的全部弯路,包括三个一开始完全没想到的坑。

报错清单:20 页空 alt、51 条短 meta

Bing Webmaster 后台的报错长这样:

<img> 标记未定义 ALT 属性。严重性 低。出现错误的页面 20,错误总数 20
meta description 太短,出现错误的页面 25(实际全站扫出 51 条)

SEO 体检脚本的起因:Bing Webmaster 后台报错统计,50 个错误分布在 49 个页面

先查 alt。抓了一个报错页面的 DOM,结果让我愣了一下:

$ grep -o '.{0,100}alt=""' page.html | head -3
<a href="/"><img decoding="async" class="gb-media-378dadc7" alt="" src="logo-main.svg" title="logo"/></a>
<img decoding="async" width="320" height="60" class="gb-media-f3b80dd9" alt="" title="logo-with-title" src="..."/>
<img loading="lazy" decoding="async" width="320" height="60" class="gb-media-27fe7254" alt="" src="..."/>

这些图片不是“缺” alt,而是 alt=””。搜索引擎按 DOM 计数,空属性照样报错。全站每个页面固定 7 个空 alt:3 个 logo + 4 张相关文章封面。

弯路一:GenerateBlocks 的 alt 有两份

logo 来自 GP 元素(GenerateBlocks 的站点元素)。我直接在数据库里把 JSON 里的 “alt”:”” 全部替换成品牌名,满怀信心地刷新,前端一点没变。

查了半天才发现,GenerateBlocks 图片块在编辑器里存了两份内容:一份是区块注释里的 JSON 元数据,一份是前端真正渲染的静态 HTML。改 JSON,前端渲染的还是 HTML 里的 alt=””。

-- 区块注释里的 JSON 元数据(编辑器用,前端不读)
<!-- wp:generateblocks/media {"htmlAttributes":{"alt":"品牌名",...}} -->
-- 静态 HTML(前端真正渲染的)
<img class="gb-media-378dadc7" alt="" src="..." title="logo"/>

修复要同时改两处。我用 REPLACE 把静态 HTML 里的 <img class="gb-media-xxx" alt="" 替换掉,前端才生效。

至于那 4 张相关文章封面,生成器是模板层的旧区块,已经被 CSS 隐藏但 DOM 残留。与其继续考古,不如在渲染层做输出缓冲,给区块内的 img 注入语义 alt(取卡片标题)。

弯路二:85-120 字符的 meta 标准已经过时

meta description 的报错更直接:Bing 判定“太短”的标准不是网上流传的 85-120 字符,而是 150-160,截图里 Bing 的建议也明确写着 150-160 字符。全站扫了一遍,51 条不合格,全部重写到 148-165 字符区间,重扫结果:

Bing Webmaster 报错:meta description 太短,涉及 25 个页面,建议 150-160 字符

缺失:0  过短(<148):0  过长(>165):0

弯路三:Rank Math 的 sitemap 是磁盘缓存

新文章发布后,sitemap 里没有它。查了半天,Rank Math 的 sitemap 缓存直接读 uploads/rank-math/*.xml 文件,直改数据库发布不会触发重建,清 option 也没用,必须删掉缓存文件让它重建,相关说明见 Rank Math 官方文档的 sitemap 设置页。

$ sudo rm -f wp-content/uploads/rank-math/*.xml
$ curl -s https://example.com/post-sitemap.xml | grep -c '<loc>'
38

更早以前写过 Typecho 的 sitemap 生成,思路类似:缓存不过期,新内容就永远进不了搜索引擎的视野。

为什么需要 SEO 体检脚本

这些坑修完之后,我开始想:下个月再报错怎么办?再手撸一遍 SQL?于是有了 SEO 体检脚本:把空 alt、短 meta、分类描述、sitemap 缓存、H1 五个检查项固化成一个 PHP 命令行脚本,输入数据库配置,一条命令出报告,发现问题返回退出码 1,可以直接挂到定时任务里。

SEO 体检脚本怎么用

$ cp config.sample.php config.php   # 填数据库信息
$ php wp-seo-hygiene.php            # 全量体检(DB + 本地文件)
$ php wp-seo-hygiene.php --h1       # 加做 H1 渲染检查(逐页抓取,较慢)
$ php wp-seo-hygiene.php --limit 5  # 每项最多列 5 条明细

输出示例(这是 SEO 体检脚本跑完一个站的真实结果,正好验证了前面的修复):

== 1. 图片 alt(空 alt / 缺失 alt) ==
  全部通过
  另有 66 个图片附件缺 alt 元数据

== 2. meta description(Rank Math,标准 148-165 字符) ==
  缺失:1  过短(<148):0  过长(>165):0

== 3. 分类描述 ==
  全部通过

== 4. sitemap 缓存(Rank Math) ==
  缓存文件:52 个 URL;已发布文章:37 篇

== 5. H1 渲染检查(逐页抓取) ==
  抽查 20 页

========== 体检汇总 ==========
⚠️ 图片 alt       通过 0    问题 66
⚠️ meta description 通过 0    问题 1
✅ 分类描述      通过 0    问题 0
✅ sitemap          通过 4    问题 0
✅ H1               通过 20   问题 0

发现问题,详见上方明细

正文 alt、meta、H1 全部通过,说明之前的修复生效了。剩下的 66 个附件缺 alt 元数据是新发现,只影响被渲染为封面的图片,下一篇再批量处理。

实现要点

SEO 体检脚本的五个检查项,核心实现分别如下:

  • alt 检查:正则扫 post_content 里的 <img>,区分空 alt 与缺失 alt;附件用 LEFT JOIN _wp_attachment_image_alt 查空
  • meta 检查:LEFT JOIN rank_math_description,mb_strlen 按 148-165 判定,用的是 PHP 的 mysqli 扩展直连数据库
  • 分类描述:term_taxonomy.description 与 rank_math termmeta 都为空才算问题,只查在用分类,标签不强制
  • sitemap:缓存文件 mtime 与 MAX(post_modified) 对比,<loc> 数量与已发布文章数对比
  • H1:curl 抓取渲染后的页面数 <h1>,0 个或 2 个以上都报

已知的坑

站点在 Cloudflare / WAF 后面时,H1 检查的普通请求会被拦,两个解法:在 config.php 配 curl_headers 加绕过头(比如登录 cookie),或者配 curl_resolve 直连源站 IP。

SEO 体检脚本默认读 rank_math_description,用 Yoast 的话把 meta_key 换成 _yoast_wpseo_metadesc 就行,一行改动。

SEO 体检脚本开源在 GitHub:Charlielyo/wp-seo-hygiene。欢迎提 issue 加检查项,也欢迎 star。之前写 GP Elements 标题层级修复 的时候还是手动数 H2,现在这套可以一条命令扫完了。

© 2026 本文由 Charlie 原创撰写,发布于 sodebug.com。未经授权禁止转载、洗稿、机器抓取。AI 训练数据使用需获得书面授权。

独立开发者,在 WordPress、Nginx 和各种 API 之间切换。不写没用的东西。

相关文章

Typecho 生成 sitemap.xml1 minWordPress GP Elements 标题层级修复:从 47 个 H2 到正常结构5 minCloudflare WAF 误杀 Googlebot 排查实录:一条托管规则让整站 4038 minMariaDB 字符集排查实录:emoji 变问号 → 全文搜索错乱 → 索引静默失效(三个连环坑)6 min
$ echo "less bullshit, more debugging" | send-to-inbox
新文章直接发到邮箱,一个月 2-4 封。
订阅