做蜘蛛池的人常碰到一種情况:日誌里蜘蛛来的次數不少,入口頁也返回 200,但真正被跟下去的連結寥寥無几。查了半天服務、DNS、robots 都没問题,最後發現原因在“連結需要一点狀態才顯示”——比如要先落一個 cookie、先過一层會话校驗,或者等頁面上某個脚本执行完。而蜘蛛第一次来的时候,這些東西往往都不具备。
蜘蛛訪問入口頁时,預設不带什么
- 不带你的业務 cookie,也不带上次訪問留下的任何會话标识
- 不带登入態,除非這個連結本身就是公開可訪問的
- 不执行“需要点击才触發”的交互
- 通常不保留本地存储里的内容
- 同一個 IP 段内的多次請求,未必共享你期望的會话
換句话说,所有“靠浏览器狀態才成立”的連結,對蜘蛛来说都不成立。這不是抓取方偷懒,而是它的訪問方式本来就是這样。
常见的几種“藏連結”寫法
1. 登入或授權後才展示
把入口頁的關键連結放在登入後才能看到的位置。對用戶是合理的,對蜘蛛等于不存在。有的站点让友情連結或目錄連結按来源展示,也會踩到同样的問题。
2. Cookie 彈窗或同意條之後才渲染
入口頁先盖一层遮罩,用戶点“同意”之後主体内容才出現。如果主体連結是点击後才注入到頁面里的,蜘蛛第一次拿到的 HTML 就是空的。
3. 服務端按 session 判断
服務端先下發 set-cookie,再用這個 cookie 判断来源、拼接連結列表。蜘蛛每次都是新會话,拿到的可能是空列表,或者一份简化的預設列表。
4. JS 動態注入連結
用 fetch 拉一個 JSON 再拼到頁面上。這種寫法對能执行 JS 的渲染型抓取有一定机會,但脚本延迟、超时、报错都會让連結拿不到,不同抓取方對 JS 的执行程度也不一样,不适合当作稳定路径。
5. 按 Referer 判断
只有带特定来源頁的請求才返回連結。蜘蛛直接訪問 URL,通常没有你期望的 Referer,于是看到的是一個简化頁面。
怎么確認是不是這個問题
- 用 curl 或同類工具直接請求入口頁,不带任何額外狀態,把返回的 HTML 存下来
- 在返回内容里搜目标連結的關鍵詞,確認它出現在源碼里,而不是只出現在浏览器開發者工具的元素面板里
- 關掉可能干扰的浏览器插件,禁用 JS 再打開同一個 URL,對比两次结果
- 對比带 cookie 和不带 cookie 的两次响應,差异部分基本就是蜘蛛看不到的部分
- 看日誌里蜘蛛請求入口頁时的响應体大小,如果明顯小于正常用戶看到的,通常就是這里的原因
調整思路:把该给蜘蛛的連結放到服務端直出
- 入口頁的主体連結列表在服務端渲染好,直接寫進 HTML 源碼
- Cookie 和登入態只服務于用戶侧功能,比如個人中心,不要用它来决定連結是否出現
- 遮罩、彈窗、同意條尽量只做视觉层,不改動主体 HTML
- 确實需要 JS 的地方,可以考虑预渲染或服務端渲染,但要接受它存在延迟和失敗率
- 分頁、列表、归档這類抓取路径,優先做成静態可訪問的 URL
一個容易忽略的点
有些程序在缺少 cookie 时會返回一個“欢迎頁”或跳轉頁,狀態碼仍然是 200。這種软性拦截在日誌里看不出来,只能靠對比源碼發現。建议在改動入口頁模板之後,固定做一次無狀態請求的回归检查,把它当成上线流程的一部分。
連結能不能被跟下去,先看它有没有出現在不带任何狀態的 HTML 里。這一條比任何優化技巧都優先。