链接写在脚本里,发现路径就换了
传统意义上的 URL 发现很直接:搜索蜘蛛下载 HTML,解析其中的 a 标签和 href 属性,把新地址放进待抓取队列。这条链路不依赖浏览器,只要 HTML 里有链接就能走通。
当站点的导航、列表、相关阅读由 JavaScript 在页面加载后动态插入 DOM 时,初始 HTML 里往往没有对应的 href。蜘蛛需要进入渲染阶段,用类似无头浏览器的方式执行脚本,才能看到这些地址。发现过程通常不会彻底消失,但会被推后、缩量,而且更不稳定。
渲染阶段实际发生了什么
主流搜索引擎对页面的处理大致分两步:先抓取原始 HTML 做初步解析,再把需要渲染的地址排入渲染队列,执行完脚本后取一次最终的 DOM。渲染队列的资源比抓取队列更紧,排队时间往往更长。
- 渲染要下载脚本、样式和字体,还可能发起接口请求,成本远高于抓一份静态 HTML。
- 渲染超时、脚本报错或接口失败时,这一轮里新出现的链接就丢了,什么时候再补上没有保证。
- 如果 robots.txt 挡住了 JS、CSS 文件,渲染出来的 DOM 可能是不完整的,链接自然也拼不出来。
哪些写法最容易让链接落空
- 用 onclick 加 location.href 做的伪链接,元素本身没有 href 属性。
- 纯 div、span 加事件监听实现的卡片和按钮。
- 点“加载更多”之后才追加进来的列表项。
- 依赖登录态、本地存储或某个接口返回才渲染的导航。
- 无限滚动:不滚动就不产生新地址,滚动位置也无法传递。
- 接口返回 JSON 后在前端拼出来的网址,蜘蛛不一定执行那次请求。
把关键入口放回 HTML 里
做法并不复杂:主导航、面包屑、列表翻页、文章底部的相关阅读,尽量由服务端渲染输出真实的 a href。如果站点是纯前端框架,至少给这几类页面做 SSR 或者预渲染,让原始 HTML 里就带着完整链接。
判断标准可以很简单:关掉 JavaScript,还能不能顺着链接点到目标页。能点到,发现路径就是稳的。
无限滚动与懒加载的折中做法
列表页不要只靠滚动触发。保留带参数的翻页地址,比如 page=2、page=3,每一页服务端返回真实链接,滚动加载只作为同一页内的视觉补充,不要让滚动成为唯一的入口。图片懒加载、次要模块懒加载都可以留,但链接本身不该被延后生成。
用站点地图和静态入口兜底
渲染成本高的站点,用 XML 站点地图把重要 URL 直接列出来,是成本最低的补充入口;再配一个服务端输出的 HTML 站点地图页,把栏目、归档、分页链接写死在 HTML 中。这样即使渲染阶段出问题,地址仍有一条不依赖脚本的发现路径。
自查清单
- 关闭 JavaScript 抓一遍站内链接,记录能发现多少 URL。
- 再开着 JavaScript 抓一遍,对比两次的链接差集,差集里的地址就是依赖渲染才出现的。
- 检查 robots.txt 是否误挡了 JS、CSS 等渲染资源。
- 看服务器日志里渲染类请求是否正常拿到页面,有没有大量超时。
- 把差集中真正重要的地址补进内链或站点地图,次要的可以先放着。
用脚本渲染本身没有问题,问题在于让 URL 发现完全依赖它。把最关键的几条路径用最朴素的方式写出来,其余的交给脚本也无妨。