收录型站点的死链批处理:分层判定与 URL 规范化
维护一个收录型站点(导航站、工具目录、资源索引)到一定规模后,最耗人力的工作不是审核新提交,而是持续处理失效条目。收录量到几千条、每天还有新提交进来时,人工巡检彻底失效,本文整理一套可自动运行的分层判定方案。
一、先区分三类"坏链"
把所有失败都当成 404 处理,是最常见的错误。实际至少有三种,处理动作完全不同。
| 类型 | 表现 | 真实原因 | 处理动作 |
|---|---|---|---|
| 瞬时失败 | 偶发超时、连接重置 | 目标站限流或临时故障 | 退避重试,不下架 |
| 路径失效 | 404 / 410 | 站点改版、路径调整 | 重新抓取首页找新路径 |
| 主体消亡 | 域名解析失败、站点关闭 | 网站关停或域名过期 | 判定下架 |
只有第三类该走下架流程。如果把第一类也下架,热门站点会随机消失,用户体验直接崩掉。
二、判定"主体消亡"的三个独立信号
把下架决策收敛到三个信号,满足其中任意两个才判定:
- DNS 解析连续失败 3 次,且相邻两次间隔大于 24 小时,用于排除 DNS 抖动与服务器短暂重启
- HTTPS 与 HTTP 同时无法建立连接,用于排除单纯的证书问题——很多老站证书过期但 HTTP 仍然可达
- 搜索引擎缓存中已无该域名索引,说明站点确实已不再对外提供服务
第三条最容易被忽略。前两条只能说明"此刻访问不了",而很多站点会换服务器、停机整顿几天。只有第三条能区分"暂时不可用"与"已经离场"。
三、批量抓取时的连接控制
回收死链本身也要发起大量请求,如果不做控制,很容易把目标站打挂,也会让自己的出口 IP 被封。
import asyncio
import random
from datetime import datetime
from urllib.parse import urlsplit
CONCURRENCY = 8 # 并发上限,别贪
MIN_INTERVAL = 1.5 # 同一域名的最小请求间隔(秒)
TIMEOUT = 10 # 单次请求超时
RETRY_BASE = 2.0 # 指数退避基数
_sem = {}
_last = {}
async def throttle(host: str):
"""同一域名串行 + 最小间隔,不同域名之间并发"""
async with _sem.setdefault(host, asyncio.Semaphore(1)):
prev = _last.get(host)
if prev is not None:
wait = MIN_INTERVAL - (datetime.now() - prev).total_seconds()
if wait > 0:
await asyncio.sleep(wait)
_last[host] = datetime.now()
async def check(url: str) -> dict:
host = urlsplit(url).hostname
for attempt in range(3):
await throttle(host)
try:
async with httpx.AsyncClient(timeout=TIMEOUT, follow_redirects=True) as c:
r = await c.head(url)
if r.status_code < 400:
return {"url": url, "ok": True, "status": r.status_code}
return {"url": url, "ok": False, "status": r.status_code}
except Exception as e:
if attempt == 2:
return {"url": url, "ok": False, "error": type(e).__name__}
# 指数退避 + 抖动,避免重试风暴
await asyncio.sleep(RETRY_BASE ** attempt + random.uniform(0, 1))
几个容易踩的点:
- HEAD 请求优先,但有些站点不实现 HEAD,需要回退到 GET
- 不要跟随重定向做最终判定,中间跳转到首页的往往正是"路径失效"而非"主体消亡"
- 失败重试要有上限,无上限重试会让一轮扫描拖到几小时
四、复核环节不能省
自动流程标记为"疑似下架"的条目,一律进人工队列。原则是:任何自动化动作只降权,不删除。
人工复核通常只需要做一件事——打开首页看一眼。实测下来,绝大部分标记都能被一眼否掉:只是改了首页路径、只是把服务挪到了子域名、只是换了 CDN。
五、URL 规范化:投入产出比最高的一步
比起死链治理,重复 URL 的危害更大且更容易被忽略。同一站点被多个分类收录、同一页面因参数变体产生多个 URL,这些都会把权重稀释掉。
规范化的实现很直接:
from urllib.parse import urlsplit, urlunsplit
def normalize(url: str) -> str:
p = urlsplit(url.strip())
scheme = "https" # 统一协议
host = (p.hostname or "").lower()
if host.startswith("www."):
host = host[4:] # 去掉 www
path = p.path.rstrip("/") # 去尾部斜杠
return urlunsplit((scheme, host, path, "", "")) # 丢弃 query
配套三条规则:
- 规范化函数作为唯一入口,写入前必经
- 变体 URL 全部 301 到规范 URL,不做 302
- 站点详情页只允许存在一种形式的地址
这三步做完,重复内容稀释权重的问题才算从根上解决。
六、批量提交时的三个卡点
关于向搜索平台提交 URL,有三点需要提前知道:
提交配额不是无限的。 搜索平台对单个站点的提交速率有上限,且这个上限和站点规模正相关。站点越大,单次提交量越要压着走。
提交了不等于收录了。 提交只是把 URL 放进候选池,能不能被索引取决于内容质量、页面可抓取性和站外引用。收录型站点本身原创内容有限时,这一环尤其明显。
变体提交会浪费配额。 同一个页面提交多次,除了稀释权重,还在消耗本就不宽裕的提交额度。
小结
死链批处理拆成三步:分层判定(区分瞬时/路径/主体)、多信号确认(DNS + 连通性 + 搜索索引)、人工兜底(只降权不删除)。
如果也在维护收录型站点,建议的优先级是:先建好规范化和去重,再谈批量提交。顺序反了,后面所有工作都会被重复内容稀释。
本文涉及的收录治理思路与公开分类结构,可参考 VP导航 的实现方式。
本文同步发布于个人技术博客与中文网址导航站 VP导航。
VP导航是一个人工审核收录、拒绝死链的中文网址导航站,收录 3700+ 优质站点,覆盖 AI 工具、开发编程、站长工具、学习教育等分类。
如果你也在找开发工具、学习资源或效率工具,可以看看:https://www.vpis.cn/
浙公网安备 33010602011771号