公司使用angularJs(以下都是指ng1)框架做了互聯網應用,之前沒接觸過seo,突然一天運營那邊傳來任務:要給網站做搜索引擎優化,需要研發支持。搜了下發現單頁面應用做seo比較費勁,國內相關實踐資料分享出來的也比較少,略懵,前後花了一番功夫總算完成了。在這裡記錄下來,做一個總結,也希望能夠幫助在做類似工作的朋友少走一點彎路。還是建議需要seo的網站技術選型盡量不要使用angular react一類的單頁面框架。如果你和我一樣網站做完了發現需要seo,那麼往下看吧。如果各位已有更優的方案歡迎拍磚交流。

單頁面應用seo的困難在哪裡?

  做seo必須了解爬蟲工作的基本原理。 搜索引擎能夠搜到一個網頁是因為對其做了索引,而在這之前需要爬蟲抓取到網站頁面存儲為一個快照,快照的內容即頁面的靜態內容。一般來說,右鍵查看網頁源代碼看到的內容即爬蟲所能抓取到的內容。爬蟲拿到一個url後抓取其頁面信息,查找頁面中的a標籤,拿到下一個url跳轉地址,繼續下一個頁面抓取。seo的工作目的是增加搜索引擎對網站的索引量以及提升網頁排名,傳統的seo工作例如站內tdk的優化、網站url優化、外鏈增加都是為了達到這些目的。做到這些有一個共同的前提,就是網頁內容能夠被搜索引擎抓取到,而單頁面應用seo的困難就卡在這裡。

  如果你的應用是angularjs這類單頁面應用開發的,右鍵查看源代碼你會發現網站沒有動態數據。很遺憾、搜索引擎來抓取的頁面也會是這樣。這主要是因為兩點原因:

  路由、模板和ajax請求

   angular實現單頁面的方案是利用了路由機制配合模板引擎。通過自定義模板,一個應用只有一個主頁面,通過路由切換不同的狀態,嵌套對應的模板引擎。而模板中的動態數據,都是通過ajax請求從後端拿到的。這從路由跳轉到渲染出完整頁面的過程,除了主頁面基本的靜態數據,其他的全靠js來完成。

  爬蟲不執行js

   第一條明白之後看到這裡也就很明顯了。很遺憾,爬蟲不執行js腳本,這個也不難理解,搜索引擎每天都有海量的頁面要抓取,執行js會大大降低效率;另外搜索引擎執行js腳本也存在著巨大的安全隱患。

   搜索引擎拿到一個url後,獲取,結束,僅僅拿到主頁面中了了的幾行靜態信息。angular框架維護的路由、主頁面,以及前端像後端發起的ajax請求等等js完成的工作,搜索引擎一概不會處理。

url優化

  可抓取方案放到下面,先說說url優化。用過angularjs的都知道,ng的url是靠#來標識一個狀態。含#類似符號的url對於seo是非常不友好的,而且據同事反應(本人沒有驗證),搜索引擎在訪問url的時候並不會帶著#後的內容去訪問。總之,url優化是單頁面應用seo繞不開的一個工作,而我們的目的,是把url優化成如同 www.xxx.com/111/222/333 目錄結構的url,它是爬蟲最喜歡的形態。

  如何去除ng框架url中的#,google和百度都能夠搜到不少資料。如:http://blog.fens.me/angularjs-url/

  簡單來說,去除#只需要在路由中配置$locationProvider.html5Mode(true); 開啟html5模式,url會自動去除#以及.html後綴達到最優。但這時存在問題:f5刷新會404找不到頁面,原因是f5會把url提交到後端獲取資源,而html5模式優化後的url在後端並不存在這樣一個資源,直接訪問這個鏈接會連主頁面都找不到,自然就會404。以上鏈接給出的方案是nodejs後端的方案,我們的方案是用springMVC後端,不過原理都是類似的:後端不認識這個鏈接,我們就把這個錯誤的連接重定向到原本帶#的連接,對於後端來說就是一個正常的訪問,而url中的#在瀏覽器端會再次被html5模式給去除。

  重定向的工作可以放在後端springMVC的過濾器中解決,也可以在容器中解決。我們的框架是後端用nginx做負載均衡,我將重定向放在nginx.conf中,對每個路由狀態的url都做了對應的原始url重定向,問題解決。無論如何刷新、訪問,頁面都是簡單舒適的目錄結構url。

兩種可抓取解決方案

  url優化之後,繼續往下看。說白了我們要做的就是單頁面應用的可抓取方案,即:如何讓搜索引擎能夠獲取到完整內容的頁面信息。我調研了現有的一些解決方案,思路都是類似的。搜索引擎不執行js,我們改變不了,那麼我們只有像照顧嬰兒一樣,自己將js執行,拿到模板以及動態數據渲染出一個完全靜態的頁面,交給爬蟲。我調研過git上的兩個方案,做一個分享,如果大家有更好的方案也歡迎分享。

  方案一、johnhuang-cn/AngularSEO

  https://github.com/johnhuang-cn/AngularSEO,這是一個java後端的解決方案。主要分為兩塊:服務端過濾器,本地爬蟲。服務端過濾器有兩個作用:一是為了拿到url,二是識別搜索引擎請求並重定向到本地快照;本地爬蟲是為了渲染頁面為本地快照。工作流程大致如下:

web.xml中配置上過濾器,第一次訪問網站的時候過濾器抓取到url,交給本地爬蟲。這個爬蟲是一個擁有動態數據抓取能力的爬蟲,主要利用了selenium+phantomjs框架,關於這兩個框架可以自行google,其中phantomjs是一個webkit內核。它能夠抓取動態數據的原因就在於它可以獲取dom元素執行事件以及相關js。在獲取到完整的頁面信息後,它會將形如:http://abc.com/#/about的url對應靜態存儲到本地命名為http://abc.com/_23/about 的快照。也就是說,我們需要等待本地爬蟲將每個url渲染出本地快照,然後搜索引擎爬蟲來訪問時,過濾器將請求重定向到對應的快照頁面。過濾器如何識別爬蟲呢?是通過http包頭中的userAgent,每個搜索引擎擁有自己的userAgent,在過濾器內配置上即可。

  優點:這個方案有幾個優點。1、部署相對簡單,對於java應用來說,配置相對方便簡單。2、搜索引擎訪問效率較快,由於快照已經被保存好了,搜索引擎來抓取後直接會返回靜態頁面。

  弊端:這個方案同樣存在幾個弊端。1、本地爬蟲抓取速度慢,對於我們擁有海量動態數據的如資訊模塊,保存快照是個耗時的工作。2、實時性,框架通過配置本地爬取頻率來更新快照,意味著搜索引擎抓取頁面的實時性受限於更新頻率。3、穩定性,不知道現在是否還存在這些問題,可能由於當時該框架還不很成熟,我在試用中,本地爬蟲的激活不夠穩定,另外phantomjs進程出現過無法退出的現象,導致後台開啟大量phantomjs內存耗盡。4、分散式部署問題,我們利用nginx負載均衡做了後端集群,搜索引擎來了之後按規則分配到不同後端,導致使用此框架需要在每個後端部署,引來一系列不便和問題。

由於以上弊端,此方案最終被我放棄了。

方案二、prerender.io

  該方案是我調研過程中找到的相對成熟的解決方案,較為完美的解決了我的需求。原理圖如下,可參考:http://www.cnblogs.com/whitewolf/p/3464555.html

  prerender.io也分為兩塊,客戶端以及服務端prerender服務,客戶端的工作是識別搜索引擎請求並做重定向(和方案一類似),除了userAgent它還會通過escaped_fragment做搜索引擎識別,這是谷歌的一套可抓取方案,詳情可見:Google"s ajax crawling protocol。如果你的站點主要是做國內瀏覽器的優化基本可以忽略,單使用userAgent足夠了。prerender服務端負責接收客戶端重定向過來的請求,拿到請求後再次去向web後端做請求,同樣,prerender後端集成了phantomjs,來執行js獲取動態數據。拿到完整的頁面數據之後,prerender後端將完整頁面返回給搜索引擎。

  prerender的客戶端現在擁有很多種技術實現,基本可以滿足各類技術方案。服務端擁有兩種選擇:1、使用官方提供的prerender.io雲服務 2、搭建自己的prerender後端服務。官網上的介紹推薦使用雲服務,不過官網需要FQ,而且使用別人的服務穩定性總是令人擔憂的,我選擇了自己部署prerender服務。它其實就是個單獨在跑的nodejs進程,使用forever指令跑起來之後還是很穩定的。

  優點:1、高實時性,通過上面分析可以明白prerender服務是實時拿到搜索引擎請求去做頁面渲染的,這意味著一次部署後如果站點沒有大的改動便沒有了後續工作,搜索引擎得到的每一次返回內容和用戶訪問的一樣都是最新的數據。2、分散式部署,prerender服務是完全和web應用分離的一個進程,不管後端有多少集群都和部署互不影響。3、穩定性,框架已經相對成熟,緩存機制、黑白名單體驗起來都很不錯。prerender服務用forever守護進程跑起來之後基本上沒有遇到不穩定的問題。

  缺點:1、搜索引擎抓取效率,頁面是實時渲染的,搜索引擎抓取起來自然會慢一些,不過這個我們並不很關心。

比較兩個方案我自然選擇了prerender的方案,部署實踐的過程也遇到過一系列問題,如果需要我後續會寫prerender部署實踐文章。

總結

總的來說,單頁面應用可抓取的最大問題就在於搜索引擎不執行js,解決方案無非就是我們自己做動態數據渲染然後餵給爬蟲。確定了這些,即使自己去完成這樣一個框架也不是一件困難的事了。

  


推薦閱讀:
查看原文 >>
相关文章