轉 https://www.mxgw.info/t/phantomjs-prerender-for-seo.html
騰訊問卷所有動態內容,全部由Ajax接口提供。
衆所周知,大部分的搜索引擎爬蟲都不會執行JS,也就是說,如果頁面內容由Ajax返回的話,搜索引擎是爬取不到部分內容的,也就無從做SEO了。
先來看看效果
去年一整年,搜索引擎收錄都少得可憐。
更致命的是,被收錄的頁面,其搜索引擎裏面顯示的標題是最原始的html標題,權重如此高的地方,卻被收錄了一個沒什麼用的標題。
在去年年底完成實施了預渲染服務後,收錄量蹭蹭蹭的起來了,並且收錄的標題也都全部正常了。
而這所有的一切,除了Nginx接入層的配置是需要改動業務代碼外,其他全部都是旁路機制。也就是說,自己做一套,可以給所有同類型業務共用,同時不會影響現有業務的任何代碼任何流程。
PhantomJS來解圍
Ajax無法做SEO這個問題,困擾了我很久,後來發現PhantomJS這東西能在服務端解析HTML,瞬間這個問題不再是問題。
PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast andnativesupport for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.
準備一個PhantomJS任務腳本
這裏我命名爲spider.js。
|
/*global phantom*/ |
|
"use strict"; |
|
|
|
// 單個資源等待時間,避免資源加載後還需要加載其他資源 |
|
var resourceWait = 500; |
|
var resourceWaitTimer; |
|
|
|
// 最大等待時間 |
|
var maxWait = 5000; |
|
var maxWaitTimer; |
|
|
|
// 資源計數 |
|
var resourceCount = 0; |
|
|
|
// PhantomJS WebPage模塊 |
|
var page = require('webpage').create(); |
|
|
|
// NodeJS 系統模塊 |
|
var system = require('system'); |
|
|
|
// 從CLI中獲取第二個參數爲目標URL |
|
var url = system.args[1]; |
|
|
|
// 設置PhantomJS視窗大小 |
|
page.viewportSize = { |
|
width: 1280, |
|
height: 1014
|
|
}; |
|
|
|
// 獲取鏡像 |
|
var capture = function(errCode){ |
|
|
|
// 外部通過stdout獲取頁面內容 |
|
console.log(page.content); |
|
|
|
// 清除計時器 |
|
clearTimeout(maxWaitTimer); |
|
|
|
// 任務完成,正常退出 |
|
phantom.exit(errCode); |
|
|
|
}; |
|
|
|
// 資源請求並計數 |
|
page.onResourceRequested = function(req){ |
|
resourceCount++; |
|
clearTimeout(resourceWaitTimer); |
|
}; |
|
|
|
// 資源加載完畢 |
|
page.onResourceReceived = function (res) { |
|
|
|
// chunk模式的HTTP回包,會多次觸發resourceReceived事件,需要判斷資源是否已經end |
|
if (res.stage !== 'end'){ |
|
return; |
|
} |
|
|
|
resourceCount--; |
|
|
|
if (resourceCount === 0){ |
|
|
|
// 當頁面中全部資源都加載完畢後,截取當前渲染出來的html |
|
// 由於onResourceReceived在資源加載完畢就立即被調用了,我們需要給一些時間讓JS跑解析任務 |
|
// 這裏默認預留500毫秒 |
|
resourceWaitTimer = setTimeout(capture, resourceWait); |
|
|
|
} |
|
}; |
|
|
|
// 資源加載超時 |
|
page.onResourceTimeout = function(req){ |
|
resouceCount--; |
|
}; |
|
|
|
// 資源加載失敗 |
|
page.onResourceError = function(err){ |
|
resourceCount--; |
|
}; |
|
|
|
// 打開頁面 |
|
page.open(url, function (status) { |
|
|
|
if (status !== 'success') { |
|
|
|
phantom.exit(1); |
|
|
|
} else { |
|
|
|
// 當改頁面的初始html返回成功後,開啓定時器 |
|
// 當到達最大時間(默認5秒)的時候,截取那一時刻渲染出來的html |
|
maxWaitTimer = setTimeout(function(){ |
|
|
|
capture(2); |
|
|
|
}, maxWait); |
|
|
|
} |
|
|
|
}); |
通過PhantomJS命令直接執行即可在終端中看到渲染後的html結構
|
phantomjs spider.js'http://wj.qq.com/'
|
命令服務化
什麼意思呢,因爲上面是一個命令,沒法很好的響應搜索引擎爬蟲的請求,估我們要把他服務化。
PhantomJS自帶一個Web Server Module,但總是不穩定,如前面文章所說時不時會假死。
我們就通過Node給他起一個簡單的Web服務。
|
// ExpressJS調用方式 |
|
var express = require('express'); |
|
var app = express(); |
|
|
|
// 引入NodeJS的子進程模塊 |
|
var child_process = require('child_process'); |
|
|
|
app.get('/', function(req, res){ |
|
|
|
// 完整URL |
|
var url = req.protocol + '://'+ req.hostname + req.originalUrl; |
|
|
|
// 預渲染後的頁面字符串容器 |
|
var content = ''; |
|
|
|
// 開啓一個phantomjs子進程 |
|
var phantom = child_process.spawn('phantomjs', ['spider.js', url]); |
|
|
|
// 設置stdout字符編碼 |
|
phantom.stdout.setEncoding('utf8'); |
|
|
|
// 監聽phantomjs的stdout,並拼接起來 |
|
phantom.stdout.on('data', function(data){ |
|
content += data.toString(); |
|
}); |
|
|
|
// 監聽子進程退出事件 |
|
phantom.on('exit', function(code){ |
|
switch (code){ |
|
case 1: |
|
console.log('加載失敗'); |
|
res.send('加載失敗'); |
|
break; |
|
case 2: |
|
console.log('加載超時: '+ url); |
|
res.send(content); |
|
break; |
|
default: |
|
res.send(content); |
|
break; |
|
} |
|
}); |
|
|
|
}); |
旁路服務
我們現在已經有了一個能跑預渲染的Web服務了,剩下就是要將搜索引擎爬蟲的流量導入到這個預渲染的服務中,同時把結果再返回給搜索引擎爬蟲。
我們使用Nginx這個接入層利器即可輕鬆解決這個問題。
|
# 定義一個Nginx的upstream爲spider_server |
|
upstream spider_server { |
|
server localhost:3000; |
|
} |
|
|
|
# 指定一個範圍,默認 / 表示全部請求 |
|
location / { |
|
proxy_set_header Host $host:$proxy_port; |
|
proxy_set_header X-Real-IP $remote_addr; |
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|
|
|
# 當UA裏面含有Baiduspider的時候,流量Nginx以反向代理的形式,將流量傳遞給spider_server |
|
if ($http_user_agent ~* "Baiduspider") { |
|
proxy_pass http://spider_server; |
|
} |
|
} |
這個栗子裏面僅僅對百度爬蟲做了處理,可以自行把爬蟲都補完整。
Free
說了這麼多,我突然覺得這篇文章非常值錢。
因爲,國外也有專門的服務端預渲染服務了,但他們統統要收費。
你可以根據本文的思路,自行部署一套旁路渲染服務。
附上一份新鮮收集的爬蟲UA列表
- 360 【文檔】
- 360Spider
- HaoSouSpider
- 360Spider-Image
- 360Spider-Video
- Baidu 【文檔】
- Google 【文檔】
- Googlebot
- Googlebot-News
- Googlebot-Video
- Googlebot-Mobile
- Sogou 【文檔】
- Sogou web spider
- Sogou inst spider
- Sogou Spider
- Bingbot【文檔】
- bingbot
- msnbot
- msnbot-media
- adidxbot
- BingPreview