2026年用Python做关键词聚类:把1000个关键词自动分成内容选题(附完整代码)
作者: QAssistant
发布时间: 2026-09-18
标签: SEO、关键词研究、语义聚类、内容规划、Python、实战工具、Embedding
阅读时长: 约 16 分钟
难度: 中级
先说一个内容人每天都在受的罪
你导出了 1000 个关键词,然后呢?
- 手动一个个看,分不清哪些是同一个主题
- 凭感觉分组,分出来的东西没法直接变成选题
- 关键词之间的语义关系(同义、上下位、长尾)全靠脑子记
结果就是:关键词表躺在那,内容规划还是拍脑袋。
今天写一个 关键词聚类工具 ------用 Embedding(词向量)把关键词按语义相似度自动分组,一个聚类 = 一个内容选题,直接输出可执行的内容规划表。
一、工具思路
关键词表(CSV / TXT)
↓
向量化:sentence-transformers 编码成高维向量
↓
聚类:层次聚类(Agglomerative)按余弦距离分组
↓
自动命名:每个簇选"最靠近中心"的词作为主题标签
↓
输出:内容规划表(cluster_id / 主题 / 关键词 / 搜索量)
为什么用 Embedding 而不是关键词匹配?
因为语义聚类能识别"SEO 工具 "和"优化软件推荐 "是一类,"外链建设 "和"发外链的方法 "是一类------即使它们字面完全不同。这是传统分组做不到的。
依赖:
bash
pip install sentence-transformers scikit-learn numpy
二、完整代码:keyword_cluster.py
python
"""
keyword_cluster.py
关键词语义聚类工具:把关键词表自动分组为内容选题
依赖:pip install sentence-transformers scikit-learn numpy
"""
import csv
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.cluster import AgglomerativeClustering
# 中文关键词推荐用中文/多语言模型;英文可用 all-MiniLM-L6-v2
MODEL_NAME = "BAAI/bge-small-zh-v1.5"
def load_keywords(path: str) -> list:
"""
读关键词表。支持两种格式:
- 每行一个词(TXT)
- CSV:第一列=关键词,第二列(可选)=搜索量
返回 list[(keyword, volume)]
"""
items = []
if path.endswith(".csv"):
with open(path, "r", encoding="utf-8-sig") as f:
for row in csv.reader(f):
if not row or not row[0].strip():
continue
if row[0].lower() in ("keyword", "关键词"):
continue # 跳过表头
vol = 0
if len(row) > 1:
try:
vol = int(float(row[1]))
except ValueError:
vol = 0
items.append((row[0].strip(), vol))
else:
with open(path, "r", encoding="utf-8") as f:
items = [(ln.strip(), 0) for ln in f if ln.strip()]
return items
def cluster_keywords(items: list, distance_threshold: float = 0.45):
"""
对关键词做层次聚类。
distance_threshold 越小 → 簇越多越细;越大 → 簇越少越粗。
0.4~0.55 是中文关键词的常用区间。
"""
keywords = [k for k, _ in items]
volumes = {k: v for k, v in items}
print(f"🔢 编码 {len(keywords)} 个关键词...")
model = SentenceTransformer(MODEL_NAME)
# 归一化后,余弦距离=欧氏距离,聚类更稳定
embeddings = model.encode(
keywords, normalize_embeddings=True, show_progress_bar=True
)
print("🧩 层次聚类中...")
clustering = AgglomerativeClustering(
n_clusters=None,
distance_threshold=distance_threshold,
metric="cosine",
linkage="average",
)
labels = clustering.fit_predict(embeddings)
# 按簇聚合
clusters = {}
for kw, label in zip(keywords, labels):
clusters.setdefault(int(label), []).append(kw)
# 为每个簇命名:选最靠近簇中心的词
named = []
for cid, kws in clusters.items():
idxs = [keywords.index(k) for k in kws]
vecs = embeddings[idxs]
centroid = vecs.mean(axis=0)
# 距离中心最近的词 = 主题代表
sims = vecs @ centroid
label_kw = kws[int(np.argmax(sims))]
total_vol = sum(volumes.get(k, 0) for k in kws)
# 簇内按搜索量排序
kws_sorted = sorted(kws, key=lambda k: -volumes.get(k, 0))
named.append({
"cluster_id": cid,
"topic": label_kw,
"size": len(kws),
"total_volume": total_vol,
"keywords": kws_sorted,
})
# 按总搜索量降序(优先做高价值主题)
named.sort(key=lambda x: -x["total_volume"])
return named
def export_plan(clusters: list, path: str = "content_plan.csv"):
"""导出内容规划表:每行一个关键词,标注所属主题"""
with open(path, "w", newline="", encoding="utf-8-sig") as f:
w = csv.writer(f)
w.writerow(["主题ID", "主题(代表词)", "关键词", "簇规模", "簇总搜索量"])
for c in clusters:
for i, kw in enumerate(c["keywords"]):
w.writerow([
c["cluster_id"],
c["topic"] if i == 0 else "", # 主题只写一次,方便阅读
kw,
c["size"] if i == 0 else "",
c["total_volume"] if i == 0 else "",
])
print(f"✅ 内容规划表已导出:{path}")
def print_summary(clusters: list, top_n: int = 20):
print("\n" + "=" * 62)
print(f"📊 共聚出 {len(clusters)} 个主题簇(按总搜索量排序)")
print("=" * 62)
for c in clusters[:top_n]:
head = ", ".join(c["keywords"][:6])
more = f" ...等{c['size']}个" if c["size"] > 6 else ""
print(f"\n[{c['cluster_id']}] 主题「{c['topic']}」"
f"({c['size']}词 / 总搜索量{c['total_volume']})")
print(f" {head}{more}")
if __name__ == "__main__":
import sys
src = sys.argv[1] if len(sys.argv) > 1 else "keywords.csv"
items = load_keywords(src)
clusters = cluster_keywords(items, distance_threshold=0.45)
print_summary(clusters)
export_plan(clusters)
三、运行 & 输出示例
输入 keywords.csv:
csv
keyword,volume
seo工具,4800
免费seo工具,2200
seo优化软件,1600
关键词挖掘工具,1300
外链建设,2400
怎么发外链,980
高质量外链资源,760
独立站seo,3100
外贸独立站优化,1500
网站收录慢,890
输出(节选):
==============================================================
📊 共聚出 4 个主题簇(按总搜索量排序)
==============================================================
[0] 主题「seo工具」(4词 / 总搜索量9900)
seo工具, 免费seo工具, seo优化软件, 关键词挖掘工具
[1] 主题「独立站seo」(2词 / 总搜索量4600)
独立站seo, 外贸独立站优化
[2] 主题「外链建设」(3词 / 总搜索量4140)
外链建设, 怎么发外链, 高质量外链资源
[3] 主题「网站收录慢」(1词 / 总搜索量890)
网站收录慢
一瞬间,1000 个乱词变成了 N 个清晰的选题方向。
四、怎么用这个结果做内容规划
一个规律:一个簇 = 一篇内容 / 一个栏目。
| 簇 | 主题 | 内容动作 |
|---|---|---|
| 0 | seo工具 | 写一篇《2026最好用的N款SEO工具横评》,内部覆盖"免费""优化软件""关键词挖掘"长尾 |
| 1 | 独立站seo | 写《外贸独立站SEO从0到1》(你的核心业务词) |
| 2 | 外链建设 | 写《外链建设实战:怎么发高质量外链》 |
| 3 | 网站收录慢 | 写问答型《网站收录慢怎么办?5个排查方向》 |
关键收益:
- 避免关键词互相打架(cannibalization) :同一簇的词,用一篇内容承接,而不是分散成 5 篇互相内耗
- 选题优先级一目了然 :按
total_volume排序,先做高价值主题 - 内容结构自然生成:主题词 = 文章标题,簇内长尾 = 文章各小节
五、关键参考链接
| 主题 | 链接 |
|---|---|
| sentence-transformers 文档 | https://www.sbert.net/ |
| BGE 中文向量模型 | https://huggingface.co/BAAI/bge-small-zh-v1.5 |
| scikit-learn 聚类算法 | https://scikit-learn.org/stable/modules/clustering.html |
| Google 内容质量指南 | https://developers.google.com/search/docs/fundamentals/creating-helpful-content |
| 关键词堆砌(要避免) | https://developers.google.com/search/docs/essentials/spam-policies |
六、进阶玩法
6.1 用搜索量加权,优先做高价值簇
代码里 total_volume 已经把整簇搜索量加总。你还可以这样排:"总搜索量 ÷ 簇规模 "得到"平均词价值",优先做高价值且竞争集中的主题。
6.2 调整聚类粒度
python
# 想要更细的选题 → 调小阈值
cluster_keywords(items, distance_threshold=0.35)
# 想要更粗的主题栏目 → 调大阈值
cluster_keywords(items, distance_threshold=0.55)
先跑 0.45,看结果再微调,通常两次就能找到你满意的粒度。
6.3 接入 GSC 真实数据做"意图聚类"
把 Search Console API 拉来的已有展示词 (见前文《用 Search Console API 自建排名监控》)和候选新词一起聚类,就能看出:哪些簇你已经在排名、哪些是内容空白------直接定位增长点。
6.4 结合 GEO:一簇 = 一个"事实密度单元"
GEO 时代,AI 需要结构化的、单一主题清晰 的内容。一个聚类正好是一个主题单元,写成一篇事实密集的文章,既利于传统排名,也利于被 AI 引用------一举两得。
七、总结
关键词不是用来"堆"的,是用来"组织"的。
这个工具的价值:
- Embedding 语义聚类,识别字面不同但意思相同的词
- 全自动分组 + 自动命名,1000 词几秒钟出结果
- 输出可直接执行的内容规划表
- 顺带解决关键词打架、选题混乱、长尾浪费三大痛点
把"看关键词表发呆"的时间,换成"按主题批量产出高质量内容"。
你那 1000 个关键词,聚出来几个主题?评论区晒一下主题数量,我帮你看有没有漏掉的高价值簇。
📮 想要本文完整代码包 + 更多 SEO/GEO 实战工具合集?评论区留言或私信我即可,看到都会回。