简介:面向帝国CMS网站管理员与SEO运维人员的免费插件合集,聚焦内容生产与搜索引擎优化环节,适合希望减少手工配置、提升站点收录与排名效率的初中级站长使用。方案覆盖全自动采集、伪原创发布、自动内链、关键词插入和自动配图等能力,可把采集、改写、站内链接与关键词布局串成较完整的SEO工作流。压缩包约312.51MB,文件总数显示为0、类型明细暂无有效数据,上游清单未提供可核验的文件构成;从描述看,内容围绕插件工具及配套运行组件展开。已有275人学习下载,说明该主题在中后台建站群体中有一定关注度。读者可据此了解免费SEO插件的功能边界与组合思路,并以此搭建采集、内链、关键词和配图的基础优化流程,减少重复劳动,把精力更多放在内容审核与站点运营上。
1. 搜“帝国CMS插件免费下载”的人,多半卡在同一个地方
搜“帝国CMS插件免费下载”的人,通常不是因为帝国CMS缺少 SEO 功能,真正的原因是同一个功能被拆散在五个地方:栏目设置里改描述,数据表里加字段,模板里拼 meta,伪静态里写规则,自定义页面里生成 sitemap。改一处没事,五处一起改就很容易互相冲突——标题被模板覆盖,描述被截断,canonical 指向分页,sitemap 里混进 404。这份资源的价值,就是把这类零散的 SEO 功能收进一套插件包里:SEO 字段的批量维护、标题描述的模板化生成、关键词抽取、canonical 与分页处理、JSON-LD 结构化输出、sitemap 分片生成,换站时只改参数,不用重写一遍配置。它适合已经在用帝国CMS跑内容站、又被重复配置拖住的人;不适合只想装个插件就自动把排名做上去的期待。下面按“解包核对、装改验、回滚”的顺序拆,中间的血泪经验我会标出来。
2. 解包先看结构:插件目录、数据表和后台入口怎么对上
拿到一个帝国CMS插件包,先别急着往/e/extend/里丢。帝国CMS 的插件不是“上传即生效”的那种,它要经过后台注册、写数据库配置、生成菜单三个动作,任何一步对不上,表现都是白屏或者“插件不存在”。这一章讲清楚它在系统里是怎么被加载的,以及装之前必须核对哪几样东西。
2.1 插件在帝国CMS里的加载链路
帝国CMS 的扩展插件,代码放在/e/extend/下的独立目录里,每个目录一个入口文件,通常是index.php。后台“系统 → 插件管理 → 增加插件”填写插件名称、插件目录名、插件文件(如index.php),提交后会往插件配置表里写一条记录,并在后台左侧生成菜单项。之后访问/e/extend/插件目录/index.php就能进入插件界面。
插件内部要操作数据库,不能自己另起一套连接,标准做法是引入系统公共文件,复用全局的数据库对象和表前缀变量。这样换站、换表前缀都不用改代码:
<?php // e/extend/seo_toolkit/index.php // 相对层级按实际插件目录深度调整,常见为向上两级 require dirname(__FILE__) . '/../../class/connect.php'; global $empire, $dbtbpre; // 从插件自己的配置表读开关和参数 $cfgRow = $empire->fetch1("SELECT * FROM {$dbtbpre}extend_seo_config LIMIT 1"); $enableSchema = (int)($cfgRow['enable_schema'] ?? 1); // 统一入口参数,避免直接拼 $_GET $do = $_GET['do'] ?? 'setting'; $do = preg_replace('/[^a-z_]/', '', $do); echo '<div class="seo-toolkit">当前动作:' . htmlspecialchars($do, ENT_QUOTES, 'UTF-8') . '</div>';这段代码的关键不在业务,而在两个变量:$empire是帝国CMS 的数据库对象,$dbtbpre是表前缀(默认phome_)。直接写phome_ecms_news的插件,遇到改过前缀的站点会直接报表不存在,这是最常见的“装了没反应”原因之一。参数处理那两行也不是摆设,插件的入口文件通常挂在后台菜单下,带do参数做路由,过滤掉非字母下划线能挡掉一部分拼接注入。
提示:引入路径用
dirname(__FILE__)拼绝对路径,不要用../相对路径。帝国CMS 有些版本会在不同入口切换工作目录,相对路径偶发失效,排查起来很费时间。
2.2 解包后按这张表逐项核对
不同插件包的目录组织会有差异,但一个能正常安装的包,下面这几类文件基本都得有。收到后先对着表格过一遍,缺哪项直接问来源,不要抱着“试试看”的心态装。
| 文件/目录 | 作用 | 核对点 |
|---|---|---|
index.php | 插件后台入口 | 是否可独立访问,是否引入connect.php |
config.php或配置表 | 开关与参数 | 参数项是否与说明一致,是否有默认值 |
install.sql | 建表/加字段 | 表名是否带前缀占位,是否可重复执行 |
uninstall.sql | 回滚 | 是否只删自己加的东西 |
lib/ | 业务类 | 是否有exit阻断、是否有调试输出残留 |
tpl/ | 后台表单模板 | 表单字段名是否与入库代码一致 |
install.sql是重点检查对象。合格的写法是不写死前缀,交给安装程序替换;不合格的写法是通篇phome_,一旦你的站前缀是别的,安装就会失败。另外看有没有DROP TABLE——卸载脚本里出现DROP TABLE phome_ecms_news这种,不要安装。
2.3 装之前先做一次可回滚的备份
插件改的是数据表结构和模板文件,这两样都不在“重新上传源码”能覆盖的范围内。我一般在动插件之前固定执行两条命令,用时间戳命名,出问题直接整包回滚。
#!/usr/bin/env bash set -euo pipefail DB_NAME="site_db" DB_USER="site_user" DB_PASS="your_password" SITE_ROOT="/www/wwwroot/site" BACKUP_DIR="/www/backup/seo" TS=$(date +%F_%H%M) mkdir -p "$BACKUP_DIR" # 1) 整库逻辑备份,InnoDB 用事务保证一致性,不锁表 mysqldump -u"$DB_USER" -p"$DB_PASS" \ --single-transaction \ --default-character-set=utf8mb4 \ --routines --triggers \ "$DB_NAME" > "$BACKUP_DIR/db_$TS.sql" # 2) 只打包会被插件改动的目录,避免整站几十 G 拖慢 tar -czf "$BACKUP_DIR/files_$TS.tar.gz" \ -C "$SITE_ROOT" \ e/extend e/data e/template echo "backup done: $BACKUP_DIR/db_$TS.sql"--single-transaction让备份在一个一致性快照里完成,运行期间写入不被阻塞,这是 InnoDB 表的前提,MyISAM 表仍然是锁的,这点要知道。--default-character-set=utf8mb4不加的话,中文标题在导出文件里可能变成问号,恢复回去就是一堆乱码,这类事故我见过不止一次。第二步只打包e/extend(插件)、e/data(缓存和配置)、e/template(模板),这三个是插件真正会碰的地方,打包速度快,回滚也快。
回滚顺序和安装顺序相反:先停插件(后台插件管理里禁用或直接移走目录),再导回 SQL,最后清e/data下的缓存文件。顺序搞反的话,残留的插件配置会引用已经不存在的数据表,后台会持续报错。
3. 把 SEO 字段落到数据表:标题、描述、关键词和 URL 规则
插件装好只是第一步,真正的活是让 SEO 数据有地方存、有地方取。帝国CMS 原生已经有标题、简介、关键词这些字段,但它们的取值逻辑是固定的,插件要做的是在不破坏原逻辑的前提下加一层“覆盖”:后台手工填了就用手工值,没填就按规则自动生成。这一章讲字段怎么加、模板怎么取、URL 怎么保证唯一。
3.1 字段扩展:在主表上加三条 SEO 元数据
帝国CMS 的内容模型按表拆分,新闻模型的主表是phome_ecms_news,正文等大字段在副表phome_ecms_news_data_1里。SEO 元数据量小、查询频繁,加在主表更合适。加字段前先确认表引擎和字符集,避免索引长度踩坑。
-- 先确认表结构和字符集 SHOW CREATE TABLE `phome_ecms_news`; -- 加三个字段:SEO标题、SEO描述、规范链接 ALTER TABLE `phome_ecms_news` ADD COLUMN `seo_title` varchar(120) NOT NULL DEFAULT '' COMMENT 'SEO标题,为空时回落到title', ADD COLUMN `seo_desc` varchar(200) NOT NULL DEFAULT '' COMMENT 'SEO描述,为空时回落到简介', ADD COLUMN `seo_canonical` varchar(191) NOT NULL DEFAULT '' COMMENT '规范链接,跨站转载时使用', ADD INDEX `idx_seo_canonical` (`seo_canonical`);字段长度是有讲究的:seo_title给 120,是因为中文标题在搜索结果里大约显示 30 个汉字,120 字符足够覆盖并留出英文站点的余量;seo_desc给 200,对应大约 78 个汉字,超出部分搜索引擎会截断,不如自己控制。seo_canonical用 191 而不是 255,是因为部分 MySQL 5.6/5.7 组合在utf8mb4下单列索引上限是 767 字节,255×4 会超,191×4=764 刚好卡在线上,这是一个很实用的保守值。如果你的库已经是 MySQL 8 且行格式为 DYNAMIC,改成 255 也没问题。
加完字段还有一步容易漏:帝国CMS 后台的“管理数据表”里要刷新一次表结构缓存,否则后台录入表单不会出现新字段。很多人加完字段发现后台看不见,以为是没生效,其实是缓存没刷。
3.2 模板层取值:手工值优先,自动值兜底
字段有了,取值逻辑要写成“逐级回落”,而不是简单的三元判断。原因是实际运营里,有的编辑填了 SEO 标题没填描述,有的描述是从正文里截的,还有的正文本身带 HTML 标签,直接输出会把标签带进 meta。
<?php // 放在内容模板顶部,模板需开启“支持PHP” // $navinfor 为帝国内容页的当前文章数组 function ecms_seo_text(array $row, $len) { $candidates = [ trim($row['seo_desc'] ?? ''), trim(strip_tags($row['smalltext'] ?? '')), trim(strip_tags($row['newstext'] ?? '')), ]; foreach ($candidates as $text) { // 压缩连续空白,去掉不可见字符 $text = preg_replace('/[\s\x{00A0}]+/u', ' ', $text); $text = trim($text); if ($text !== '') { return mb_substr($text, 0, $len, 'UTF-8'); } } return ''; } $seoTitle = trim($navinfor['seo_title'] ?? ''); if ($seoTitle === '') { $seoTitle = trim($navinfor['title'] ?? ''); } $seoTitle = mb_substr($seoTitle, 0, 60, 'UTF-8'); $seoDesc = ecms_seo_text($navinfor, 110); $pageUrl = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $canonical = trim($navinfor['seo_canonical'] ?? ''); if ($canonical === '') { $canonical = $pageUrl; } ?> <title><?= htmlspecialchars($seoTitle, ENT_QUOTES, 'UTF-8') ?></title> <meta name="description" content="<?= htmlspecialchars($seoDesc, ENT_QUOTES, 'UTF-8') ?>"> <link rel="canonical" href="<?= htmlspecialchars($canonical, ENT_QUOTES, 'UTF-8') ?>">三个要点。第一,所有截断用mb_substr并显式传UTF-8,用substr会把汉字从中间切成乱码,这是帝国CMS 站点上极高频的事故,尤其是站点从 GBK 迁到 UTF-8 之后,老模板里的substr会继续咬人。第二,strip_tags在截断之前执行,顺序反了会残留半截标签。第三,输出统一走htmlspecialchars,标题里带英文双引号时,不加转义会直接把 meta 标签截断,页面看起来正常,抓取工具读到的描述却是残缺的。
$navinfor是帝国内容页模板里的文章数组,列表页对应的是循环里的$r和$bqno。如果你在列表模板里也输出描述,字段名要跟着换,不能直接复制这段。
3.3 伪静态与 URL 唯一性:同一个内容只保留一个地址
帝国CMS 支持生成静态页,也支持动态访问,两条路同时开着就产生了一个经典问题:同一篇文章有/show-1-2.html和/e/action/ShowInfo.php?classid=1&id=2两个地址,权重被拆成两份。插件一般会处理这个,但前提是你把规则配对。
# 内容页:/show-栏目ID-信息ID.html rewrite ^/show-([0-9]+)-([0-9]+)\.html$ /e/action/ShowInfo.php?classid=$1&id=$2 last; # 列表页:/list-栏目ID-页码.html rewrite ^/list-([0-9]+)-([0-9]+)\.html$ /e/action/ListInfo.php?classid=$1&page=$2 last; # 已生成静态的站点,规则只做兜底,命中真实文件优先 location / { try_files $uri $uri/ /index.php?$query_string; }实际路径要按你站点现有的入口核对,不同版本在e/action/下的文件名可能有差异,不要照抄后直接上生产。last表示重写后重新走一遍 location 匹配,break则在本轮停止;这两个混用是伪静态调试里最常见的困惑,附件 404 十有八九是这里写错。try_files那行保证静态文件存在时直接返回文件,不存在才落到重写,避免把图片也送进 PHP。
URL 唯一性还有两个配套动作:栏目页和 TAG 聚合页的分页,第 2 页以后加noindex,follow,避免分页内容被当成重复页;站内搜索结果页直接noindex并加robots.txt的Disallow。这两条不加,插件把标题描述做得再干净,收录质量也上不去。
4. 结构化数据与 sitemap:插件输出接上真实页面才算完成
很多插件包在后台能生成数据,但前台页面里什么也没多出来,原因是输出环节没接上。这一章讲两件事:JSON-LD 的字段怎么和帝国CMS 的字段对上,sitemap 怎么按量分片并且不混进死链。两者都属于“生成了不等于生效”的典型。
4.1 JSON-LD 输出:字段映射与时间格式
结构化数据用 JSON-LD 比微数据好维护,一段<script>放在</head>前即可。字段映射的原则是:只填你页面上真实存在的值,不要为了凑 schema 编造作者、评分、发布时间。
<?php // 内容页模板内,复用上一节的 $seoTitle / $seoDesc / $canonical $published = (int)($navinfor['newstime'] ?? 0); $modified = (int)($navinfor['lastdotime'] ?? $published); $siteName = '站点名称占位'; $schema = [ '@context' => 'https://schema.org', '@type' => 'NewsArticle', 'headline' => mb_substr($seoTitle, 0, 110, 'UTF-8'), 'description' => $seoDesc, 'datePublished' => $published ? date('c', $published) : null, 'dateModified' => $modified ? date('c', $modified) : null, 'mainEntityOfPage' => [ '@type' => 'WebPage', '@id' => $canonical, ], 'author' => [ '@type' => 'Organization', 'name' => $siteName, ], ]; // 过滤 null,避免输出 "datePublished": null $schema = array_filter($schema, static function ($v) { return $v !== null && $v !== ''; }); echo '<script type="application/ld+json">' . json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '</script>'; ?>date('c', ...)输出 ISO 8601 带时区偏移,这是结构化数据校验工具认的格式,直接输出Y-m-d也能过,但精度丢了。JSON_UNESCAPED_UNICODE让中文不被转成\uXXXX,体积小一半且可读;JSON_UNESCAPED_SLASHES让 URL 保持原样。array_filter那一步是为了去掉空值,headline推荐不超过 110 字符,超了校验工具会给警告但不报错,属于“能过但不好看”的灰区。
注意:
seo_canonical如果用于跨站转载指向原文,结构化数据里的mainEntityOfPage也要跟着用同一个地址,两者不一致会被判定为信号冲突。
4.2 sitemap 分片生成:按上限切片,绕开动态页
sitemap 协议单文件上限是 50000 条 URL、未压缩 50MB。内容量超过几千条的站点,直接一次性SELECT *拼 XML,很容易在生成时把 PHP 内存吃满或者在共享主机上超时。可行的做法是绕开 CMS 的动态入口,用一个独立脚本直连数据库分页读,按栏目切分输出。
<?php // sitemap/build.php,定时任务执行 $pdo = new PDO('mysql:host=127.0.0.1;dbname=site_db;charset=utf8mb4', 'db_user', 'db_pass', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); $perFile = 40000; // 留 1 万条余量,避免追加后超限 $outDir = '/www/wwwroot/site/sitemap'; $baseUrl = 'https://example.com'; // 只取已审核、未删除的内容;status 字段按站点实际字段名调整 $total = (int)$pdo->query("SELECT COUNT(*) FROM phome_ecms_news WHERE checked=1")->fetchColumn(); $pages = (int)ceil($total / $perFile); for ($p = 0; $p < $pages; $p++) { $offset = $p * $perFile; $sql = "SELECT id, classid, newstime FROM phome_ecms_news WHERE checked=1 ORDER BY id ASC LIMIT {$perFile} OFFSET {$offset}"; $rows = $pdo->query($sql)->fetchAll(); $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n"; $xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; foreach ($rows as $row) { $loc = sprintf('%s/show-%d-%d.html', $baseUrl, $row['classid'], $row['id']); $xml .= " <url>\n"; $xml .= ' <loc>' . htmlspecialchars($loc, ENT_XML1) . "</loc>\n"; $xml .= ' <lastmod>' . date('c', (int)$row['newstime']) . "</lastmod>\n"; $xml .= " </url>\n"; } $xml .= '</urlset>'; $file = sprintf('%s/sitemap_%d.xml', $outDir, $p + 1); file_put_contents($file, $xml); echo "written: {$file}\n"; }几个参数要说清楚。$perFile取 40000 而不是 50000,是给自己留缓冲,后续如果要往里补栏目页、TAG 页不至于超限。checked=1是过滤未审核内容,帝国CMS 的审核状态字段在不同模型里可能有差异,上线前用SHOW COLUMNS确认一遍。htmlspecialchars(..., ENT_XML1)对 URL 里的&做 XML 转义,不做的话 XML 解析器会直接报格式错误,sitemap 提交后被判无效。ORDER BY id ASC加OFFSET在百万级表上会越翻越慢,量大的站点改成按id > 上一页最大 id的游标方式,速度能差一个数量级。
生成完再写一个索引文件sitemap_index.xml,把各分片列进去,提交时只提交索引地址。
4.3 robots 与提交节奏
robots 不需要复杂,管住三件事就够:屏蔽后台和动态入口、屏蔽站内搜索结果、放行 sitemap。
User-agent: * Disallow: /e/ Disallow: /search/ Disallow: /*?* Allow: /sitemap/ Sitemap: https://example.com/sitemap/sitemap_index.xmlDisallow: /*?*这条要慎重,有些站点的静态化不彻底,带参数的地址才是有效页,一刀切会把正常内容也挡掉。上线前用抓取工具抽十条 URL 验证一遍。sitemap 提交不要一次全推,先提交索引,观察三到五天抓取量,再决定要不要拆分更细。新站一天抓几百条是正常节奏,急着把几万条塞进去,反而容易触发抓取配额限制。
5. 避坑与排查:五个上线后才暴露的问题
装完当天一切正常,第二天开始出问题,这是 SEO 类插件最常见的节奏。下面五条都是现象、原因、解决三段式,遇到时按顺序对,别一上来就重装插件。
5.1 后台改完描述,前台页面纹丝不动
现象:后台录入 SEO 描述保存成功,刷新前台内容页,meta 里的内容还是旧的,清浏览器缓存也没用。
原因:帝国CMS 有三层缓存叠在一起——数据缓存(e/data下的缓存文件)、模板编译缓存、以及内容页如果开了静态生成,还有一个 HTML 文件。插件改了数据库,但静态 HTML 没重新生成,前台读的仍是旧文件。
解决:按顺序清三样。先清e/data下的缓存目录(保留配置文件),再在后台“更新”里刷新内容页 HTML,最后清 CDN 或反向代理的缓存。只做第一步不做第二步,静态站永远看不到变化。我习惯在插件配置里加一个“保存后自动刷新当前页”的开关,默认关闭,需要时手动开,避免编辑一次刷一次全站。
5.2 描述被截成半个字,或者出现问号方块
现象:meta description 结尾是“……”或者一个孤零零的半个汉字,严重时整段变成????。
原因:两种可能。一是模板里用了substr而不是mb_substr,按字节截断把三字节的汉字切开了;二是站点从 GBK 迁移到 UTF-8 时,部分老数据的编码没转干净,混在里面的非法字节被输出到页面。
解决:先把模板里所有截断函数换成带编码参数的mb_*系列。再跑一次数据体检,把含非法 UTF-8 的记录挑出来:
-- 找出无法按 UTF-8 正常解码的记录,需在命令行用 CONVERT 逐条验证 SELECT id, title FROM phome_ecms_news WHERE title <> CONVERT(CONVERT(title USING latin1) USING utf8mb4) LIMIT 100;这条 SQL 是启发式的,只用来抽样定位,不要拿它当批量修复脚本。确认有问题后逐条人工改,批量转码很容易把正常内容也弄坏。
5.3 伪静态规则一加,图片和附件全部 404
现象:页面能打开,但站内所有图片、CSS、下载附件都返回 404,浏览器控制台一片红。
原因:重写规则把静态资源也吃进去了。常见写法是rewrite ^/(.*)$ /index.php?...这种全捕获,或者location /里缺try_files,导致/uploads/2024/a.jpg也被转给 PHP 处理。
解决:重写规则只匹配你要伪静态的那几类地址,用明确的^/show-、^/list-开头;location /里补try_files $uri $uri/ =404;,让真实存在的文件直接返回。改完用curl -I逐条验证:静态资源应该是 200 且Content-Type为图片类型,内容页应该是 200 且不带查询串。
5.4 sitemap 里混进 404 和未发布内容
现象:提交 sitemap 后,抓取统计里出现大量 404,索引率上不去。
原因:生成脚本的过滤条件写漏了。帝国CMS 的内容有审核状态、有回收站标记、有定时发布(未来时间),这几种都会出现在表里但前台访问不到。另外,如果站点是静态生成,内容改了但 HTML 没刷新,URL 存在但内容是旧的,抓取端也会判为低质。
解决:生成 SQL 里把checked=1、newstime <= 当前时间、回收站字段为空三个条件都加上,先在测试环境跑一遍,随机抽 50 条 URL 用curl -o /dev/null -s -w "%{http_code}"验证返回码,全部 200 再提交。
5.5 插件跑批量任务时后台卡死
现象:点了“批量生成 SEO 描述”,页面转圈几分钟后 502,或者整个后台其他页面也打不开。
原因:插件在循环里逐条UPDATE,每条一次往返,几千条就是几千次写;如果表上没有合适索引,每次更新还扫全表,把连接池占满,其他请求排队。
解决:改成按主键分批,每批 200 到 500 条,用一条CASE WHEN或事务包裹批量提交,批与批之间usleep让出资源。执行入口放到命令行或独立入口,不走后台页面同步等待。另外确认seo_canonical那类查询字段上有索引,全文扫描是最常见的隐形杀手。
6. 验收:抓一批页面回来比对,别靠眼睛看
插件配完,最危险的动作就是打开三个页面看看“好像对了”然后收工。SEO 改动涉及的是模板输出的每一个分支,列表页、内容页、分页、TAG 页的取值逻辑都不一样,靠手点是验不完的。我习惯的做法是抓一批真实 URL 回来,把 title、description、canonical 三个字段抽成表格,横向比对。
#!/usr/bin/env bash # urls.txt 每行一个待验收地址,建议覆盖:首页、栏目页、内容页、分页、TAG 页 OUT="audit_$(date +%F).tsv" printf "url\ttitle\tdesc\tcanonical\n" > "$OUT" while IFS= read -r url; do [ -z "$url" ] && continue html=$(curl -sL --max-time 15 \ -A "Mozilla/5.0 (compatible; SiteAudit/1.0)" \ -w "\n%{http_code}" "$url") code=$(printf '%s' "$html" | tail -n1) body=$(printf '%s' "$html" | sed '$d') title=$(printf '%s' "$body" | grep -oP '(?<=<title>).*?(?=</title>)' | head -1) desc=$(printf '%s' "$body" | grep -oP '(?<=name="description" content=").*?(?=")' | head -1) canon=$(printf '%s' "$body" | grep -oP '(?<=rel="canonical" href=").*?(?=")' | head -1) printf "%s\t[%s] %s\t%s\t%s\n" "$url" "$code" "$title" "$desc" "$canon" >> "$OUT" done < urls.txt echo "结果写入 $OUT,重点关注状态码非 200、以及 title/desc 为空的记录"-w "\n%{http_code}"把状态码追加到最后一行,再用tail和sed拆开,这样一次请求同时拿到内容和对错。-L跟随跳转是必须的,很多站首页会 301 到带www的地址,不跟随就只能看到跳转页。-A里带个可识别的 UA,方便服务端日志里过滤,也避免被当成可疑流量。
跑完拿到 TSV,按下面这几条过一遍,比人眼看页面靠谱得多:
| 检查项 | 合格标准 | 不合格时的常见原因 |
|---|---|---|
| 状态码 | 全部 200 | 伪静态规则漏写、静态未生成 |
| title 唯一性 | 任意两条不相同 | 列表页未拼接页码或栏目名 |
| description 长度 | 60 到 110 个汉字之间 | 回落逻辑取到了正文全文 |
| canonical 指向 | 指向自身绝对地址 | 分页未做自指、转载未指向原文 |
| 分页页 head | title 带页码 | 分页模板没有独立模板 |
title 重复这条要专门查,把 TSV 第二列取出来排序去重,条数对不上就说明有模板没做区分。列表分页最容易出这个问题,第 2 页和第 3 页的 title 一模一样,抓取端会判定为重复页只留一个。
最后一步是把验收脚本挂到发布流程里。我现在的习惯是,凡是动过模板或 SEO 字段的改动,上线后半小时内必须跑一遍这个抓取比对,结果留档。有一次改伪静态,本地测了十条全对,上线后列表分页的第二页开始 404,因为规则里少写了页码分支,人工点页面根本点不到那么深。从那以后我每次动 URL 规则,都把分页、TAG、站内搜索这三类地址强制写进urls.txt一起跑,宁可多花五分钟,也不要在两周后从抓取统计里发现问题。希望帮到你。
本文还有配套的精品资源,点击获取