栏目关键词是网站SEO优化中不可忽视的一环,无论是生成页面的meta标签,还是做内容聚合、标签云展示,都离不开从栏目数据中取出关键词列表这个基础操作。看似简单的需求,实际做起来却有不少细节要处理:关键词可能存在数据库里,可能是逗号分隔的字符串,也可能已经是一个数组,甚至还混着全角标点和空格。本文把常见的几种情形整理出来,逐个给出PHP的实现方案。

一、从逗号分隔的字符串中提取关键词
最常见的存储方式是把关键词用一个字符串保存,比如"PHP,教程,编程,后端开发"。这种格式在老项目或者简单的栏目表中非常普遍。处理它最直接的工具就是explode函数,按逗号切开后得到一个数组。
<?php
// 栏目表中存的关键词字段
$keywordsStr = 'PHP,教程,编程,后端开发';
// 按英文逗号切分为数组
$keywords = explode(',', $keywordsStr);
print_r($keywords);
// 输出: Array ( [0] => PHP [1] => 教程 [2] => 编程 [3] => 后端开发 )
?>但实际数据往往没那么干净。用户录入时可能用了中文全角逗号",",也可能在词与词之间夹了空格,甚至结尾多了一个逗号。这时候简单的explode就会产生带空格或者空字符串的元素。稳妥的做法是先用str_replace把全角符号统一,再配合array_filter和array_map做清洗。
<?php
function getKeywordsFromString($str) {
// 统一全角逗号、分号、竖线等常见分隔符为英文逗号
$str = str_replace([',', ';', '、', '|', ' '], ',', $str);
// 切分后过滤空元素并去除首尾空白
$arr = array_filter(explode(',', $str), function($v) {
return trim($v) !== '';
});
return array_values(array_map('trim', $arr));
}
$raw = 'PHP,教程, 编程,,后端开发,';
print_r(getKeywordsFromString($raw));
// 输出: Array ( [0] => PHP [1] => 教程 [2] => 编程 [3] => 后端开发 )
?>如果分隔符本身就不是固定字符,比如可能是一个或多个标点的组合,用preg_split配合正则会更灵活:preg_split('/[,,、|]+/', $str)一次性匹配多种分隔符的连续出现,代码更简洁。
二、从数据库查询结果中提取栏目关键词
栏目数据一般放在数据库里,以MySQL为例,假设有一张栏目表category,其中keywords字段存储关键词字符串,查询后提取的流程就多了一层数据库操作。用PDO来演示完整的写法。
<?php
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'root', '123456');
$stmt = $pdo->prepare('SELECT keywords FROM category WHERE id = :id LIMIT 1');
$stmt->execute([':id' => 5]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$keywords = [];
if ($row && $row['keywords'] !== '') {
$keywords = getKeywordsFromString($row['keywords']);
}
print_r($keywords);
?>如果要一次性取出多个栏目的关键词并合并成一个总列表,就需要遍历结果集,逐行分割再合并。合并时别忘了用array_unique去重,因为不同栏目之间很可能存在重复词条,比如"PHP"这个关键词几乎每个技术栏目都会有。
<?php
$stmt = $pdo->query('SELECT keywords FROM category WHERE status = 1');
$all = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$all = array_merge($all, getKeywordsFromString($row['keywords']));
}
// 去重并重建索引
$all = array_values(array_unique($all));
print_r($all);
?>这里有一个性能上的提醒:如果栏目数量非常大(几千条以上),把所有关键词拉到PHP层处理并不是最优解。MySQL从5.7开始可以考虑把关键词单独拆成关联表,或者在应用层做缓存,避免每次请求都执行全表扫描和字符串分割。对于中小型站点,上面的方案已经足够用了。
三、处理多维数组和嵌套栏目结构中的关键词
很多CMS的栏目是树形结构,取出整棵栏目树后得到的是一个多维数组,每个元素下面还挂着子栏目。想从这样的结构里把所有层级的关键词都抽出来,就得用到递归遍历。
<?php
$categories = [
['name' => '编程', 'keywords' => 'PHP,Java', 'children' => [
['name' => 'PHP进阶', 'keywords' => '框架,Composer', 'children' => []],
['name' => 'Java基础', 'keywords' => 'JVM,集合', 'children' => []],
]],
['name' => '数据库', 'keywords' => 'MySQL,Redis', 'children' => []],
];
function collectKeywords(array $categories, array &$result = []) {
foreach ($categories as $cat) {
if (!empty($cat['keywords'])) {
$result = array_merge($result, getKeywordsFromString($cat['keywords']));
}
if (!empty($cat['children'])) {
collectKeywords($cat['children'], $result);
}
}
return $result;
}
$all = array_values(array_unique(collectKeywords($categories)));
print_r($all);
?>递归写法的关键在于使用引用参数&$result来累积结果,或者干脆让函数返回值参与合并。层级过深时(超过几百层)要注意递归深度限制,不过栏目树的深度一般不会超过五层,这个担忧基本是多余的。
另外,如果只是想把关键词按栏目分组而不是摊平,可以在递归时保留栏目的层级路径,把路径作为键名存入结果数组,这样输出的结构既保留了归属关系,又拿到了完整的关键词列表,方便后续生成导航或者面包屑。
四、封装一个通用的关键词获取函数
综合上面几种场景,可以封装一个通用函数,它同时接受字符串、一维数组、多维数组三种输入,内部自动判断并归一化输出。这种函数放在项目的工具类里,各个模块都能复用。
<?php
function extractKeywords($data) {
$result = [];
if (is_string($data)) {
$result = getKeywordsFromString($data);
} elseif (is_array($data)) {
foreach ($data as $item) {
$result = array_merge($result, extractKeywords($item));
}
}
return array_values(array_unique($result));
}
// 字符串输入
print_r(extractKeywords('PHP,框架, Laravel'));
// 数组嵌套字符串输入
print_r(extractKeywords(['PHP,MySQL', 'Redis,队列']));
?>这个函数的巧妙之处在于递归调用自身:数组里的每个元素如果是字符串就分割,如果还是数组就继续深入,天然兼容任意嵌套层级的数据结构。去重放在最后一步统一执行,保证输出结果的干净。
最后补充几个容易踩的坑。一是编码问题,处理中文关键词前务必确认数据是UTF-8编码,否则trim和正则匹配可能出现乱码;二是不要在循环内反复调用array_unique,批量处理完再去重一次效率更高;三是关键词如果用于输出到HTML的meta标签,记得用htmlspecialchars转义,防止存储的内容中夹带特殊字符破坏页面结构。掌握这些细节,栏目关键词的获取和展示就不再是难事了。