本文目录导读:

- 核心检查清单(脚本需要验证的内容)
- 使用 Bash 脚本(适合简单检查)
- 使用 Node.js 脚本(更健壮,适合复杂逻辑)
- 使用 Python 脚本(适合 Python 项目)
- 如何部署钩子脚本到团队?
- 高级建议
Git 钩子脚本主要用于在代码提交前或推送前自动化执行检查,要检查 SEO 优化,通常需要结合 HTML 解析工具和正则表达式来扫描修改或新增的 HTML/模板文件。
以下是几种常见场景的实现思路和核心检查逻辑:
核心检查清单(脚本需要验证的内容)
在编写脚本前,明确需要检查哪些 SEO 要素:
- Title 标签:是否存在?长度是否合理(50-60 字符)?
- Meta Description:是否存在?长度是否合理(150-160 字符)?
- H1 标签:是否唯一(一个页面只有一个 H1)?
- Alt 属性:图片
<img>标签是否都有alt属性? - Canonical 标签:是否指定了
rel="canonical"? - Robots 元标签:是否配置了
index或noindex? - 死链/相对路径:
<a>标签的href是否为javascript:void(0)或空? - Open Graph 标签:
og:title、og:description、og:image是否存在?
使用 Bash 脚本(适合简单检查)
在 .git/hooks/pre-commit 中(注意:git 钩子脚本默认不提交到仓库,需手动部署):
#!/bin/bash
# 检查暂存区中新增或修改的 .html 文件
CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.html$')
if [ -z "$CHANGED_FILES" ]; then
exit 0
fi
ISSUES_FOUND=0
for FILE in $CHANGED_FILES; do
if [ ! -f "$FILE" ]; then
continue
fi
echo "检查 SEO: $FILE"
# 1. 检查 Title 标签
if ! grep -q '<title>' "$FILE"; then
echo " ⚠️ 错误: 缺少 <title> 标签"
ISSUES_FOUND=$((ISSUES_FOUND + 1))
fi
# 2. 检查 Meta Description
if ! grep -q 'name="description"' "$FILE"; then
echo " ⚠️ 错误: 缺少 meta description"
ISSUES_FOUND=$((ISSUES_FOUND + 1))
fi
# 3. 检查 H1 标签数量(应唯一)
H1_COUNT=$(grep -c '<h1[^>]*>' "$FILE")
if [ "$H1_COUNT" -eq 0 ]; then
echo " ⚠️ 错误: 缺少 <h1> 标签"
ISSUES_FOUND=$((ISSUES_FOUND + 1))
elif [ "$H1_COUNT" -gt 1 ]; then
echo " ⚠️ 错误: 存在多个 <h1> 标签 (共 $H1_COUNT 个)"
ISSUES_FOUND=$((ISSUES_FOUND + 1))
fi
# 4. 检查图片 Alt 属性
IMGS_WITHOUT_ALT=$(grep -oP '<img[^>]*>' "$FILE" | grep -vP 'alt="[^"]*"' | wc -l)
if [ "$IMGS_WITHOUT_ALT" -gt 0 ]; then
echo " ⚠️ 错误: 有 $IMGS_WITHOUT_ALT 个 <img> 缺少 alt 属性"
ISSUES_FOUND=$((ISSUES_FOUND + 1))
fi
# ... 其他检查类似:canonical、og:tags
done
if [ "$ISSUES_FOUND" -gt 0 ]; then
echo ""
echo "❌ SEO 检查发现 $ISSUES_FOUND 个问题,请修正后重新提交。"
exit 1
fi
exit 0
使用 Node.js 脚本(更健壮,适合复杂逻辑)
如果项目使用 Node.js,可以编写 pre-commit.js 脚本,利用 cheerio 解析 DOM(比正则更准确)。
脚本示例 (pre-seo-check.js):
const { execSync } = require('child_process');
const fs = require('fs');
const cheerio = require('cheerio'); // npm install cheerio
// 获取暂存区中修改的 HTML 文件
const output = execSync('git diff --cached --name-only --diff-filter=ACM', { encoding: 'utf8' });
const files = output.split('\n').filter(f => f.endsWith('.html'));
let errors = [];
files.forEach(file => {
if (!fs.existsSync(file)) return;
const html = fs.readFileSync(file, 'utf8');
const $ = cheerio.load(html);
// Title 检查
const title = $('title').text().trim();
if (!title) {
errors.push(`${file}: 缺少 Title 标签`);
} else if (title.length > 60) {
errors.push(`${file}: Title 长度 ${title.length} 字符,建议 ≤60`);
}
// Meta description 检查
const desc = $('meta[name="description"]').attr('content');
if (!desc) {
errors.push(`${file}: 缺少 Meta Description`);
} else if (desc.length > 160) {
errors.push(`${file}: Description 长度 ${desc.length} 字符,建议 ≤160`);
}
// H1 检查
const h1Count = $('h1').length;
if (h1Count === 0) {
errors.push(`${file}: 缺少 H1 标签`);
} else if (h1Count > 1) {
errors.push(`${file}: 存在 ${h1Count} 个 H1 标签,应唯一`);
}
// 图片 Alt 检查
$('img').each((i, el) => {
if (!$(el).attr('alt')) {
errors.push(`${file}: 图片缺少 alt 属性 (src: ${$(el).attr('src')})`);
}
});
// Canonical 检查
const canonical = $('link[rel="canonical"]').attr('href');
if (!canonical) {
errors.push(`${file}: 缺少 canonical 标签`);
}
// Open Graph 基本检查
if (!$('meta[property="og:title"]').length) {
errors.push(`${file}: 缺少 og:title 标签`);
}
});
if (errors.length > 0) {
console.error('❌ SEO 检查失败:\n' + errors.join('\n'));
process.exit(1);
} else {
console.log('✅ SEO 检查通过');
}
使用 Python 脚本(适合 Python 项目)
利用 beautifulsoup4 库:
import subprocess
import sys
from bs4 import BeautifulSoup
# 获取暂存区文件
result = subprocess.run(
['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
capture_output=True, text=True
)
files = [f for f in result.stdout.split('\n') if f.endswith('.html')]
has_error = False
for file in files:
with open(file, 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f.read(), 'html.parser')
# 检查逻辑与 Node.js 类似
if not soup.find('title') or not soup.find('title').get_text(strip=True):
print(f"❌ {file}: 缺少 Title")
has_error = True
# ... 其他检查
if has_error:
sys.exit(1)
如何部署钩子脚本到团队?
由于 .git/hooks/ 目录下的脚本不会被 Git 跟踪,需要手动同步到团队成员的仓库。
推荐两种方式:
- 存储在项目目录:将脚本放在项目根目录的
scripts/git-hooks/文件夹中,然后在package.json中使用husky或pre-commit库自动安装。 - 结合 CI/CD:使用 pre-push 钩子在推送到远程仓库前触发检查,或者在 CI 流水线(GitHub Actions, GitLab CI)中直接运行,这比本地钩子更可靠,因为不会被开发者绕过。
高级建议
- 增量检查:只检查本次修改的行,而不是整个文件(避免旧代码反复报错),使用
git diff --cached --unified=0获取具体变更行。 - 性能考虑:如果项目中 HTML 文件非常多,钩子会变得很慢,可以限制只检查
.html或.ejs/.pug等模板文件。 - 配置化:允许在
seo-config.json中自定义阈值(title 长度上限、是否强制需要 og 标签等)。
Git 钩子能有效在代码入库前拦截明显的 SEO 问题,最佳实践是本地钩子 + CI 双重校验,确保即使开发者跳过了本地钩子,CI 也能强制检查。