栏目页权重总是上不去:站点结构与内链分发的排查调整记录
适用读者:负责企业官网的技术人员、接手他人网站做 SEO 优化的工程师、想搞清楚「为什么首页有排名栏目页没有」的后端开发。
接手这家制造企业官网的时候,改版上线已经三个月。首页排名稳的,品牌词第一,两个核心产品词也在第二页晃。但四个产品栏目页(精密加工、模具设计、表面处理、组件装配)在百度和 Google 里像被冻住了一样:百度收录了,排名死死卡在 5 页开外;Google 好一点,也就 3 到 4 页。运营那边换过 TDK、加过内容,都没用。我花了一周时间爬了一遍全站,发现根本不是内容的问题——是这个站的链接结构在物理上阻止权重流到栏目页。这篇把排查过程、脚本和调整前后 8 周的数据变化完整记下来。
先说结论:三个结构性病灶
不想看过程的可以直接看这里,后面全是取证细节:

- 首页权重只通过导航单链灌给栏目页,全站几十个页面的内链几乎全部指向首页和联系方式页,栏目页从正文内容拿到的内链接近于零;
- 正文里所有指向栏目页的链接锚文本都是「查看详情」,这种链接对搜索引擎来说几乎没有主题信号;
- 深层内容页离首页超过 5 跳,部分产品参数页甚至要 7 跳才能到达,栏目页作为它们的父级节点自然分不到什么权重。
顺便还发现面包屑整个缺失,这个后面单独说。
排查第一步:把全站内链分布量化出来
SEO 工具箱里的现成产品(Screaming Frog 之类)当然能用,但那台机器装的 Python 3.11 加 Scrapy 6 已经够用了,自己写一个更可控——尤其是我要统计的是每个页面的入链数、出链数和锚文本分布,这是诊断权重分发的核心三件套。
环境说明:Windows 11,Python 3.11.6,Scrapy 2.11(pip install scrapy 即可),目标站约 800 个页面,无反爬压力。
核心脚本不长,贴出来:
import scrapy
from collections import defaultdict
from urllib.parse import urljoin, urlparse
class LinkAuditSpider(scrapy.Spider):
name = "link_audit"
# 指定爬取域名,防止跟踪到合作站或CDN外域
allowed_domains = ["example-company.com"]
# 从首页出发遍历,首页是权重源头也是入口
start_urls = ["https://www.example-company.com/"]
# 入链表:page -> 指向它的 (来源页, 锚文本) 列表
inlinks = defaultdict(list)
# 出链表:page -> 它指向的页面集合
# 用 set 去重,同页重复链接只记一次
outlinks = defaultdict(set)
def parse(self, response):
# 统一去掉末尾斜杠,避免 /a/ 和 /a 被算成两个页面
url = response.url.rstrip("/")
# 同时取 href 和链接文本,两者按顺序一一对应
links = response.css("a::attr(href)").getall()
anchors = response.css("a").xpath("string(.)").getall()
for href, anchor in zip(links, anchors):
# 相对路径补全成完整URL,比如 /about/ -> https://域名/about/
full = urljoin(response.url, href)
# 过滤掉外链、锚点、mailto 和 js 伪协议
if not full.startswith("http") or self.allowed_domains[0] not in urlparse(full).netloc:
continue
# 去掉 # 锚点部分和末尾斜杠,得到规范化目标页
target = full.split("#")[0].rstrip("/")
# 记录出链和对应的锚文本,排除自链
if target and target != url:
self.outlinks[url].add(target)
# 锚文本压缩空白,防止换行符污染统计
anchor_text = " ".join(anchor.split())
self.inlinks[target].append((url, anchor_text))
# 继续追踪同域页面,构成完整站点遍历
# dont_filter=False 让 Scrapy 自带的去重器生效,避免死循环
for next_url in self.outlinks[url]:
yield scrapy.Request(next_url, callback=self.parse, dont_filter=False)
def closed(self, reason):
# 收尾时输出每页的入链数与锚文本TOP分布,写成CSV方便透视
import csv
# 并集遍历:确保只出不入的页面(如果有)也被记录
with open("link_report.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["页面", "入链数", "出链数", "主要锚文本"])
for page in set(self.inlinks) | set(self.outlinks):
# 入链数按来源记录条数算,反映真实链接流量
in_cnt = len(self.inlinks.get(page, []))
out_cnt = len(self.outlinks.get(page, []))
# 统计锚文本出现频率,取最高频的展示
from collections import Counter
anchors = Counter(a for _, a in self.inlinks.get(page, []))
# 无入链页面用占位符标记,这类页面是重点关注对象
top_anchor = anchors.most_common(1)[0][0] if anchors else "-"
writer.writerow([page, in_cnt, out_cnt, top_anchor])
跑完 800 多个页面,导出 CSV,直接按入链数排序,第一屏就把问题看穿了:
| 页面类型 | 平均入链数 | 主要入链来源 | 最高频锚文本 |
|---|---|---|---|
| 首页 | 780+ | 全站页脚/导航 | 公司名 |
| 联系我们页 | 812 | 全站导航 | 联系我们 |
| 产品栏目页 | 1~3 | 仅首页主导航 | 产品中心 |
| 产品详情页 | 1~2 | 仅所属栏目列表页 | 查看详情 |
| 技术文章页 | 0~1 | 无 | - |
| 关于我们页 | 815 | 全站导航 | 关于我们 |
这张表翻译成人话:全站 800 多个页面的链接权重,除了导航上那几个固定单链,几乎没有任何流通。内容页之间互不链接,栏目页只有首页给的那一条导航链接,深入的内容页链接价值传不到、也传不上去。这就是典型的「链接孤岛」结构。
原理剖析:搜索引擎怎么分配链接权重
这里值得停下来讲清楚底层机制,因为后面所有调整都建立在这个认知上。
现代搜索引擎的链接分析基础可以追溯到 PageRank 算法:每个页面的权重值,通过它页面上的出链平均分给被链接的页面,被分到的权重又取决于来源页自身的权重。这个模型下有个关键推论——权重是沿着链接流动的,链接就是这个系统的血管。
对照到我们这个站,问题就非常具体了:
- 首页是全站权重最高的节点(外部链接基本都指向首页),但它的出链绝大多数给了导航上的固定页面。栏目页每个只拿到一条链接,权重配额自然少;
- 内容页拿到的权重本来就少(离首页远、入链少),它们之间还不互链,权重在站点内部第二次衰减;
- 「查看详情」这类锚文本的问题不是权重,是主题信号。锚文本是搜索引擎判断目标页主题的重要特征之一,800 个「查看详情」对判断「精密加工栏目页讲什么」贡献为零。
还有一个对栏目页特别重要的机制:搜索引擎对页面重要性的判断有一个站点内部层级先验——离首页点击距离越近的页面,被赋予的默认重要性越高。Google 官方文档里明确提过点击距离与 URL 结构的影响。这就是为什么同样一条导航链接,深层 7 跳的页面和 2 跳的页面拿到的待遇完全不同。
改造前的站点结构画出来长这样:
graph TD
HP[首页 - 权重最高] --> NAV1[关于我们]
HP --> NAV2[联系我们]
HP --> C1[精密加工栏目页]
HP --> C2[模具设计栏目页]
HP --> C3[表面处理栏目页]
C1 --> P11[详情页-查看详情]
C1 --> P12[详情页-查看详情]
C2 --> P21[详情页-查看详情]
P11 --> D1[参数页 第5跳]
D1 --> D2[参数子页 第6跳]
ART[技术文章页-无入链] --> HP
注意看:首页到参数子页要 5~6 跳,技术文章页反向只有一条回链,栏目页之间零互通。这个图不用做任何分析,肉眼就能看出权重流不动。
四处调整:把血管重新接通
诊断清楚了,调整方案围绕「让权重沿有意义的路径流向栏目页」展开。实际动手的是四件事。
一、扁平化层级:把栏目页拉到 2 跳以内
原站有一层没用的中间目录页(产品中心大类页 → 四个栏目页),既没内容也没排名,纯粹多跳一层。处理方式是砍掉中间层,首页导航直接挂四个栏目页,同时把 URL 从 /products/category/precision/ 规范成 /precision/。全站做好 301 重定向,这一步 Google Search Console 里提交了 sitemap,百度这边在资源平台做了改版规则提交。
二、正文锚文本重做:消灭「查看详情」
这是工作量最大也是收益最明显的一块。具体做法分两层:
- 产品详情页和文章页里,把指向栏目页的链接锚文本改成带关键词的描述性文本,比如「精密加工服务」「模具设计流程」;
- 栏目列表页的每条「查看详情」前面,补上产品名的独立链接(原来只有「查看详情」一个链接指向详情页)。
为验证改动覆盖度,又写了个小脚本统计改造后全站锚文本分布:
import re
from collections import Counter
# 读取审计脚本输出的全站HTML快照目录,逐文件统计锚文本
# 正则同时捕获 href 和标签内文本,re.S 处理跨行链接
ANCHOR_PATTERNS = re.compile(r"<a[^>]*href=\"([^\"]*)\"[^>]*>(.*?)</a>", re.S)
# 泛化锚文本黑名单:这些词对搜索引擎的主题判断没有贡献
GENERIC = {"查看详情", "更多", "点击进入", "read more", "learn more"}
def audit_anchor_text(snapshot_dir):
# 两个计数器分别累计泛化和带关键词的锚文本
generic_cnt = Counter()
keyword_cnt = Counter()
# 索引文件里每行是一个快照HTML的路径
with open("pages_index.txt", encoding="utf-8") as idx:
for html_path in idx:
html = open(html_path.strip(), encoding="utf-8").read()
# 只关心站内链接的锚文本质量
for href, text in ANCHOR_PATTERNS.findall(html):
# 剥掉链接文本里可能嵌套的 <span>/<strong> 等标签
text = re.sub(r"<[^>]+>", "", text).strip()
# 命中黑名单归入泛化,长度4以上的文本算关键词锚文本
if text.lower() in GENERIC:
generic_cnt[text] += 1
elif text and len(text) >= 4:
keyword_cnt[text] += 1
# 输出占比和TOP榜,占比是本次改造的核心验收指标
total = sum(generic_cnt.values()) + sum(keyword_cnt.values())
print(f"泛化锚文本占比: {sum(generic_cnt.values()) / total:.1%}")
print("高频关键词锚文本TOP10:", keyword_cnt.most_common(10))
改造前泛化锚文本(「查看详情」类)占比 71%,改造后压到 18%——剩下的是页脚「更多」之类的必要导航,这个比例可以接受。
三、面包屑补齐 + 结构化数据
栏目页和内容页全部加上面包屑导航:首页 > 精密加工 > 五轴CNC加工件。这不只是用户体验——面包屑每一条都是一条带上下文的内链,同时用 JSON-LD 输出了 BreadcrumbList 结构化数据,让搜索结果里也能展示路径。栏目页因此额外获得了所有子页面的入链,这是单一改动里对栏目页入链数提升最直接的。
四、tag 聚合页收口
内容页之间互链起来,靠的是 tag 聚合页。每个技术文章和产品详情页打 2~3 个 tag(如「五轴加工」「铝合金处理」),tag 页把同主题页面串起来。这样任意一个内容页最多 2 跳就能到另一个相关内容页,权重在内容层内部开始横向流动,而不是全部挤在首页一条路径上。tag 页本身也成为长尾词的着陆页。
改造后的结构:
graph TD
HP[首页] --> C1[精密加工栏目页]
HP --> C2[模具设计栏目页]
HP --> C3[表面处理栏目页]
C1 --> P1[产品详情页-关键词锚文本]
C2 --> P2[产品详情页-关键词锚文本]
P1 --> T1[tag页-五轴加工]
P2 --> T1
T1 --> ART[技术文章页]
ART --> C1
subgraph 面包屑
BC[首页 > 精密加工 > 详情页]
end
四个栏目页互相之间通过 tag 页和相关推荐形成环,深层参数页被并进详情页锚点或直接 301 到详情页,全站任意页面离首页不超过 3 跳。
8 周前后:数据对照
调整分两批上线:第一批(扁平化 + 301)上线于第 1 周,第二批(锚文本 + 面包屑 + tag)上线于第 3 周。之后没再动过,只观察。记录的排名均为人工查询值,取当日位次:
| 页面 | 改造前百度 | 第8周百度 | 改造前Google | 第8周Google | 栏目页入链数变化 |
|---|---|---|---|---|---|
| 精密加工栏目页 | 60+ | 14 | 28 | 9 | 2 → 47 |
| 模具设计栏目页 | 70+ | 21 | 34 | 12 | 2 → 41 |
| 表面处理栏目页 | 50+ | 17 | 31 | 11 | 3 → 38 |
| 组件装配栏目页 | 75+ | 26 | 40+ | 15 | 1 → 30 |
| tag聚合页(合计6个) | 未收录 | 4个收录 | 未收录 | 5个收录 | - |
几个值得记录的细节:
- Google 的响应明显快于百度。第 3 周第二批上线后约 10 天,Google 侧排名就开始动了;百度拖到第 5 周才有可见变化,且至今没回到首页,推测与新 URL 收录周期有关;
- 栏目页入链数从个位数涨到 30~47,来源主要是面包屑(子页面数 × 1)加正文关键词锚文本和 tag 页;
- 收录侧:全站被收录页面数从改造前的 312 涨到第 8 周的 586,增长主要来自原本没有入链的技术文章页——它们通过 tag 页被重新发现。
两个容易踩的误区
误区一:栏目页没排名就是内容不行,猛加内容。 这家站改版前后内容量几乎没变,排名照样上来了。内容页孤岛状态下,往栏目页里塞再多文章,权重也传不进去。先做链接结构体检,再谈内容投入。
误区二:为了扁平化把所有页面 301 到首页或砍掉。 扁平化的正确姿势是缩短「有价值的层级」,不是消灭层级。那个没用的中间目录页该砍,但产品参数页的层级应该保留原 URL 做 301 映射,而不是图省事全指首页——我接手过另一个站就是被人这么干的,三个月丢了两百多个收录位。
再补一句趋势上的观察:这套内链和层级的整理,同样决定了 AI 搜索引擎抓取和引用你内容时的路径清晰度——抓取器沿链接走,结构混乱的站点在传统搜索和 AI 搜索里吃亏的方式是一致的。先把传统搜索的底座修好,后面的 AI 搜索引用是顺路的事。
参考与延伸
- Google Search Central:链接最佳实践 — https://developers.google.com/search/docs/crawling-indexing/links-crawlable
- Google Search Central:Breadcrumb(BreadcrumbList)结构化数据 — https://developers.google.com/search/docs/appearance/structured-data/breadcrumb
- 百度搜索资源平台:网站改版工具说明 — https://ziyuan.baidu.com/wiki/2546
- Wikipedia:PageRank — https://en.wikipedia.org/wiki/PageRank
SEO, 内链结构, 锚文本优化, 栏目页排名, 站点扁平化, Scrapy, 百度收录