前端渲染與SEO優化踩坑小記

前言

在網站頁面後端渲染的時代,開發者只須要按照規範製做搜索引擎友好的頁面即可以快速讓搜索引擎收錄本身網站的各個頁面。css

隨着先後端技術的更新,愈來愈多的前端框架進入開發者們的視野,網站的先後分離架構愈來愈獲得開發者們的喜好與承認。 後端只提供數據接口、業務邏輯與持久化服務,而視圖、控制與渲染則交給前端。 所以,愈來愈多的網站從後端渲染變成了前端渲染,而由此帶來的直接問題就是各大搜索引擎爬蟲對於前端渲染的頁面( 動態內容 )還沒法比較完善的爬取,這就致使了網站的內容沒法被搜索引擎收錄,直接影響網站流量與曝光度。前端

博主的網站從去年五月開始也開始採用了先後分離的構架,使用了 AngularJS 框架搭建了 NewRaPo , 以後又使用 Vue.js 框架進行了總體重構 RaPo3。 無一例外,他們都是基於前端渲染的,而後在此後的一年多時間裏,搜索引擎收錄的頁面都是這樣的:vue


( 其餘搜索引擎也同樣,最先的截圖已經找不到了,先拿這個應付一下 )nginx

快照是這樣的:git

而博主實際的網站是這樣的:angularjs

和這樣的:github

感受徹底被搜索引擎拋棄了好嘛!這東西誰能搜到啊!搜到了誰會點啊!web

爲了能讓搜索引擎正常的收錄博主的網站,因而博主踏上了動態 SEO 優化的踩坑之路:shell

一、fragment 標籤

首先,博主 Google 到了在動態頁面中加入 <meta name="fragment" content="!"> 會告訴爬蟲這是個動態內容的頁面,而後爬蟲會在當前連接後面加上 ?_escaped_fragment_=tag 來獲取相應頁面的靜態版,因而果斷盤算在路由裏改一下,而後重寫一套後端渲染的頁面返回給全部帶 ?_escaped_fragment_=tag 的連接。django

正當我高興這個問題這麼容易解決的時候忽然發現網上資料都表示這個方法只有 Google 的爬蟲承認,其餘搜索引擎所有沒用! wtf,白高興一場。

二、PhantomJS

PhantomJS 是一個基於 WebKit 的服務器端 JavaScript API。它全面支持web而不需瀏覽器支持,其快速,原生支持各類Web標準: DOM 處理, CSS 選擇器, JSON, Canvas, 和 SVG。 PhantomJS 能夠用於 頁面自動化 , 網絡監測 , 網頁截屏 ,以及 無界面測試 等

簡單來講就是 PhantomJS 能在服務端解析HTML、js。

具體怎麼使用,簡而言之就是判斷爬蟲來爬取頁面的時候把每一個動態頁面先讓 PhantomJS 跑一遍,而後把獲得的靜態結果返回給爬蟲,具體過程能夠參考:用PhantomJS來給AJAX站點作SEO優化

固然博主看過以後沒用採用本身搭 PhantomJS 服務作動態內容優化,主要由於:

  1. 爬蟲每訪問一個頁面就要讓 PhantomJS 渲染一次,至關於爬蟲訪問一次實際服務器要響應兩次,第一次響應爬蟲,第二次響應 PhantomJS 本身,這種方式不只浪費資源,並且並不優雅;

  2. PhantomJS 對於新的前端技術兼容性會存在問題,可能會出現渲染失敗的狀況;

  3. 渲染頁面無緩存,每訪問一次就從新渲染一次,會形成網站響應速度變慢。

三、Prerender.io

Prerender.io 是一個基於 PhantomJS 開發的專爲動態頁面 SEO 提供靜態頁面渲染的在線服務,基本上解決了本身搭建 PhantomJS 服務所遇到的問題,網站配置 Prerender.io 後 Prerender 將會直接取代網站後端對爬蟲請求進行響應,將提早渲染好的動態頁面直接返回給爬蟲。

具體配置:

  1. 註冊 Prerender.io 帳號,免費用戶能夠渲染 250 個頁面,對於博客網站來講足夠了;

  2. 安裝中間件並設置 token,博主直接採用了 nginx 配置方案,( Prerender.io 也提供了其餘解決方案:https://prerender.io/document... )博主後端服務器是 uWSGI, 根據 Prerender.io 提供的 nginx.conf 中作以下修改:

server {
    listen 80;
    server_name www.rapospectre.com;
 
    location @prerender {
        proxy_set_header X-Prerender-Token YOUR_TOKEN;
        include        uwsgi_params;
        
        set $prerender 0;
        if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
            set $prerender 1;
        }
        if ($args ~ "_escaped_fragment_") {
            set $prerender 1;
        }
        if ($http_user_agent ~ "Prerender") {
            set $prerender 0;
        }
        if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
            set $prerender 0;
        }
        
        #resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
        resolver 8.8.8.8;
 
        if ($prerender = 1) {
            
            #setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
            set $prerender "service.prerender.io";
            rewrite .* /$scheme://$host$request_uri? break;
            proxy_pass http://$prerender;
        }
        if ($prerender = 0) {
            uwsgi_pass     127.0.0.1:xxxx;
        }
    }
}

而後重啓服務器,經過 Google Search Console 或其餘站長工具提交頁面進行爬取檢測,能夠看到,Prerender.io 成功截取了爬蟲請求並進行了渲染:

嗯,終於解決了嘛,正當博主感嘆不容易的時候,Google Search Console 的抓取結果卻讓人發現然並卵:

抓取的內容依然是滿滿的 ${ article.views }} 渲染模版,當時我認爲應該是網站緩存的問題,因此沒有多想,然而一週後再次測試,狀況依舊,回頭再看 Prerender 渲染的網頁:

原來壓根沒起做用!以後又檢查了配置和文檔,嘗試聯繫了 Prerender.io 的技術支持甚至向 Prerender 的 Github 提了相關 issue,都沒有解決問題。 最後,不得已博主仍是放棄了 Prerender。

四、本身搭建後端渲染服務

Prerender 的方案啓發了我,經過判斷來訪請求的 User-Agent 來讓不一樣的後端服務器進行響應,雖然網上關於 SEO 優化的討論中明確提到過判斷 UA 返回不一樣頁面將會獲得搜索引擎的懲罰,但我猜想這只是返回不一樣內容纔會懲罰,若是返回的是相同的內容搜索引擎就不會進行懲罰,區別在於一個是直接經過前端渲染的頁面,而另外一個則是後端渲染的頁面,兩個頁面渲染出的內容基本相同,那麼搜索引擎就不會發現。

本身動手,豐衣足食,有了想法當即驗證,首先把網站代碼中前端渲染的部分改成後端渲染,而後 push 到一個新的分支,博主網站修改十分簡單,大概只修改了 50 行不到的代碼就完成了需求: RaPo3-Shadow

接着將後端渲染代碼部署到服務器,而後假設用 uWSGI 將它跑在 11011 端口,
此時前端渲染的代碼由 uWSGI 假設跑在 11000 端口;

最後修改 nginx 配置文件 nginx.conf:

server {
    listen 80;
    server_name www.rapospectre.com;
 
    location @prerender {
        include        uwsgi_params;
        
        set $prerender 0;
        if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
            set $prerender 1;
        }
        #resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
        resolver 8.8.8.8;
 
        if ($prerender = 1) {
            uwsgi_pass     127.0.0.1:11011;
        }
        if ($prerender = 0) {
            uwsgi_pass     127.0.0.1:11000;
        }
    }
}

就是經過 UA 判斷爬蟲,若是是則轉發給 11011 端口,不是就轉發給 11000 端口,固然兩個端口返回的頁面基本是相同的,因此也就不用擔憂會被搜索引擎懲罰了。

經過以上配置後,動態頁面的 SEO 問題終於獲得瞭解決,反應最快的是 Google,次日就爬取並更新到了搜索引擎:

接着 360 搜索:

而後其餘沒有提交過網址的搜索引擎也分分收錄了網站:

( bing 沒有收錄正常的頁面應該是因爲 nginx.conf 裏沒有加入 bing 爬蟲 UA 的緣故 )

固然,不知道什麼緣由百度過了兩個多月仍是沒有收錄,在站長工具中提交網頁甚至申訴都沒有收錄新的網頁。沒錯,開頭那個用來應付一下的圖片就是百度的結果,剛截的。

我該說幸虧沒更新,不然找不到之前的例子了嘛,哈哈哈哈。

五、最後一點猜測

經過博主本身搭建後端渲染服務已經解決了動態頁面 SEO 優化問題,但博主還有一個可行辦法的猜測,不知是否可行,寫在這裏若是看了以上辦法都很差用的朋友能夠試試。

canonical 標籤是 Google 、雅虎、微軟等搜索引擎一塊兒推出的一個標籤,它的主要做用是用來解決因爲網址形式不一樣內容相同而形成的內容重複問題。 這個標籤的制定時間已經好久了,因此如今主流的搜索引擎都支持這個標籤,它本來的做用是將 url 不一樣但內容相同的網址權重所有集中到其中一個網址上,這樣能夠避免大量重複內容網頁被搜索引擎收錄,舉個例子:

http://www.rapospectre.com/archives/1
http://www.rapospectre.com/archives/1?comments=true
http://www.rapospectre.com/archives/1?postcomment=true

這三個網址的內容其實徹底同樣,爲了不重複收錄和分權,在原始網頁加上 <link rel='canonical' href='http://www.rapospectre.com/archives/1' /> 這樣其餘重複網頁將被看做都是 http://www.rapospectre.com/archives/1

那麼假設對於動態網頁 http://www.rapospectre.com/dynamic/1,咱們編寫他的靜態網頁:http://www.rapospectre.com/static/1, 而後在 http://www.rapospectre.com/dynamic/1 的頁面內加入: <link rel='canonical' href='http://www.rapospectre.com/static/1, 這樣是否也能達到動態 SEO 優化的目的呢?

總結

隨着前端渲染的頁面愈來愈多,動態頁面的 SEO 優化逐漸進入人們的視野,博主將本身動態頁面的 SEO 優化經歷寫出,但願能幫到其餘注意到這個領域或遇到相同問題的人,提供一些思路。 謝謝。

原文地址

做者:rapospectre

相關文章
相關標籤/搜索