内页跟随策略¶
类型:wiki(知识沉淀) 描述:网站抓取深度 ≥ 2 时,从首页找哪些站内链接进入再抓的规则 最后更新:2026-05-26(v0.10.14) 相关源码:
src/utils/dynamic-scraper.ts的findSubLinks+FollowOpts
是什么¶
抓商家官网时,深度=2 表示「首页 + 站内一层链接」,深度=3 表示「再深一层」。
哪些链接算"站内"、应该跟随,由 findSubLinks 用一组可配规则决定。
深度 1: ─→ [首页 acme.com]
深度 2: ─→ [首页] ──┬─→ [contact 页] ✅ 关键词命中
├─→ [about 页] ✅ 关键词命中
├─→ [blog 列表] ❌ blackKw 命中
└─→ [外部 fb] ❌ 跨域
为什么这么设计¶
v0.10.13 之前(写死)¶
用户场景:公司用多根域名(acme.com + acmecorp.io + acme-blog.io),抓不到。
v0.10.13 起(可配 4 项)¶
allowSubdomain // 同根域子域是否算同站
keywordsWhite[] // 优先关键词
keywordsBlack[] // 排除关键词
skipAssets // 跳过 jpg/css/pdf
v0.10.14 加跨根域白名单¶
关键代码¶
FollowOpts 接口¶
export interface FollowOpts {
allowSubdomain?: boolean; // false=严格 hostname
keywordsWhite?: string[]; // 默认 contact/about/imprint/...
keywordsBlack?: string[]; // 默认空
skipAssets?: boolean; // 默认 true
allowedDomains?: string[]; // v0.10.14 跨根域白名单
}
同域判定逻辑(v0.10.14)¶
const linkHost = u.hostname.toLowerCase();
const linkRoot = getRootDomain(linkHost);
let sameSite = false;
if (allowSubdomain) sameSite = linkRoot === baseRoot;
else sameSite = linkHost === base.hostname.toLowerCase();
// 白名单:完全相等 OR 用户填的是根域、链接是其子域
const inAllowList = allowedDomains.length > 0 &&
allowedDomains.some((d) => linkHost === d || linkHost.endsWith('.' + d) || linkRoot === d);
if (!sameSite && !inAllowList) continue; // 跳过此链接
关键词打分¶
// 白名单未命中 → 不跟(避免抓博客/产品页等)
// 命中靠前的关键词 → 分数更高 → 优先跟
for (let i = 0; i < whiteKws.length; i++) {
if (lower.includes(whiteKws[i])) {
score = whiteKws.length - i;
break;
}
}
if (score === 0) continue;
排序后取前 N 个(N = (depth - 1) * 3,depth=2 时 3 个,depth=3 时 9 个递归)。
三档同域策略¶
| 设置 | 行为 | 适用场景 |
|---|---|---|
| 全关 | linkHost === base.hostname 严格相等 |
默认。最严,避免误跟 |
allowSubdomain: true |
同根域子域算同站(shop.acme.com ↔ acme.com) |
公司用 CDN 子域 / 多语言子域 |
allowedDomains: [...] |
用户填的域名也算同站(不论根域) | 公司用多个完全不同的根域名 |
| 两者都开 | 最宽松 | 大公司多品牌矩阵 |
getRootDomain 局限¶
function getRootDomain(hostname: string): string {
const parts = hostname.split('.');
if (parts.length <= 2) return hostname;
return parts.slice(-2).join('.'); // 简单取最后两段
}
⚠️ 不处理 ccTLD 复合后缀:
- acme.co.uk → 返回 co.uk(错;应是 acme.co.uk)
- acme.com.cn → 返回 com.cn(错)
够用判断:本项目用户主要是 .com / .net / .io / .cn 单后缀,目前不修。如果遇 .co.uk 案例,再引入 PSL(Public Suffix List)库。
易踩坑¶
⚠️ 坑 1:keywordsWhite 留空 = 不跟任何链接¶
设计是「白名单匹配制」 — 没命中关键词 = 不跟。用户清空想"跟全部"会反而什么都跟不到。
要"跟全部" → 改代码(目前不开放)。
⚠️ 坑 2:allowedDomains 与 followSubdomain 不冲突¶
二者叠加生效:满足任一条件即算同站。直觉上没问题,但 UI 文案要写明 — 用户可能以为是「3 选 1」。
⚠️ 坑 3:keywordsBlack 优先于 white¶
代码里先检查 black(命中即跳过),再算 white 分数。即使一个 URL 同时含 "blog" 和 "about",会被跳。
⚠️ 坑 4:资源后缀检测用正则不智能¶
只看后缀,不看 Content-Type。.json 之类自定义后缀不会被过滤。
默认值¶
| 字段 | 默认 | 理由 |
|---|---|---|
followSubdomain |
false | 保守,严格 hostname |
followKeywordsWhite |
contact,about,team,impressum,imprint,company,联系,关于,kontakt,contato,nosotros |
多语种联系页关键词 |
followKeywordsBlack |
blog,news,login,cart,checkout,product,/p/,category,tag,archive |
电商/博客噪音页 |
followSkipAssets |
true | 显然 |
followAllowedDomains |
"" | 老用户无感 |
修改这块时要 / 不要做什么¶
- ✅ 加新
FollowOpts字段 → storage-data 加 + UI 加 + 本 wiki 同步 - ✅ 改默认关键词列表 → 改 storage-data 的
DefaultSettings.followKeywords* - ❌ 不要把"白名单匹配制"改成"白名单加分制"(用户已经习惯了)
- ❌ 不要在
findSubLinks里调 PSL 库(增加复杂度,目前用例不需要)
版本里程碑¶
| 版本 | 事件 |
|---|---|
| v0.9.x | 写死同域 + 5 关键词 |
| v0.10.13 | 全部可配(4 个字段) |
| v0.10.14 | 加 followAllowedDomains 跨根域白名单 |