現在很多站点用前端框架渲染,頁面里能看到的正文、列表、價格,全部由 JavaScript 在浏览器中生成。對用戶来说没有問题,但對搜尋引擎来说,第一眼拿到的和最终看到的可能不是同一份東西。這中間的差距,常常就是“抓取看起来正常、收錄却不動”的原因之一。
抓取到的 HTML 和渲染後的頁面,是两份東西
搜尋引擎抓取一個 URL 时,第一步通常是直接請求這個地址,拿到服務器返回的原始 HTML。這份 HTML 里如果只有一個空壳容器和几個 script 标簽,爬虫第一時間並不能看到正文。之後,搜尋引擎可能會把這個頁面放進渲染队列,用接近浏览器的环境执行 JS,再拿到渲染後的结果。
問题在于渲染是有成本的。它比直接讀 HTML 慢得多,也更占资源,而且不是每個 URL 都會被渲染,也不是每次抓取都會渲染。所以有的頁面最终被正确理解,有的頁面則在“只拿到空壳”的狀態下就被處理掉了。
哪些情况最容易出問题
- 正文、商品信息、文章列表完全靠 JS 請求接口後再插入。
- 關键内鏈寫在 JS 里,原始 HTML 中没有任何 a 标簽。
- 标题、canonical、meta robots 由前端動態寫入。
- 内容要等用戶交互,比如点击、滚動、切換标簽頁之後才出現。
- 接口返回依赖登入態或特定 Cookie,爬虫拿不到資料。
這些情况不一定都會導致不收錄,但會明顯增加不确定性。尤其是内鏈:如果連結只存在于渲染结果里,URL 發現這一步就會變慢,新頁面可能很久都進不了抓取队列。
服務端渲染與预渲染是常见折中
要减少這類不确定性,常见的做法是让服務器直接返回带内容的 HTML。可以是服務端渲染,也可以是构建时预渲染,或者對爬虫做静態化。目标只有一個:让第一次請求就返回正文和主要連結,而不是等到 JS 执行完才出現。
需要注意的是,服務端返回的内容和渲染後的内容要尽量一致,至少标题、正文主体、canonical 與 robots 元信息不要互相矛盾。如果两份内容差异過大,反而會让搜尋引擎难以判断哪個才是頁面的真實版本。
几個容易忽略的细节
分頁與懒加载。無限滚動、点“加载更多”才出現的内容,通常不會被当作頁面的一部分来處理。如果這些内容有獨立地址,最好给每個地址一個可以直接訪問的 URL。
接口稳定性。渲染时調用的接口如果超时或返回错誤,渲染结果就是残缺的。這類失敗往往不會体現在常規的可用性监控里,需要單獨观察。
別用 JS 寫關键指令。noindex、canonical、hreflang 這類元信息,尽量在服務端輸出的 HTML 里就寫清楚,避免依赖前端注入。
渲染成本要花在值得的頁面上。如果一個頁面内容本身就很少,或者本来就不需要被收錄,没必要為它增加渲染负担。
怎么自查
- 用浏览器的“查看網頁源代碼”(不是审查元素)看原始 HTML,確認正文和主要連結是否在里面。
- 用抓取工具的模拟抓取與渲染功能,對比渲染前後 HTML 的差异。
- 關閉浏览器 JS 後打開頁面,看還剩多少内容,這是一種粗糙但直观的參照。
- 在服務器日誌里观察目标 URL 是否被反复抓取却没有進入索引。
- 用網址检查類工具看抓取版本與渲染版本是否一致。
抓取成功不等于内容被理解,内容被理解也不等于一定會被收錄。渲染只是這條鏈路上的一個环节,把它梳理清楚,比猜测算法更實际。
最後回到收錄本身:JS 渲染影响的主要是“搜尋引擎能看到什么”,而不是“搜尋引擎愿不愿意收錄”。把内容尽早、完整、稳定地交到爬虫手里,剩下的判断交给頁面质量本身。如果你的頁面在抓取日誌里表現正常但長期不進索引,值得先检查一下原始 HTML 里到底有没有東西。