使用PhantomJS解決動(dòng)態(tài)網(wǎng)站的SEO方案

前段時(shí)間在對(duì)博客進(jìn)行了Vue前后端分離改造,導(dǎo)致了一個(gè)問(wèn)題,網(wǎng)站內(nèi)容無(wú)法得到搜索引擎的收錄。。。這困擾了我一段時(shí)間,由于工作比較繁忙,也沒(méi)有對(duì)此問(wèn)題進(jìn)行探究,巧在最近幫老婆寫(xiě)個(gè)爬蟲(chóng)腳本的時(shí)候,接觸到了PhantomJS,頓時(shí)開(kāi)竅,我可以用PhantomJS來(lái)解決呀~

PhantomJS is a headless WebKit scriptable with a JavaScript API. It has **fast **and **native **support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.

于是Google一番,發(fā)現(xiàn)這種方案原來(lái)很多人在用了,天哪~真是凹凸曼了,科普一下,這種解決方案其實(shí)是一種旁路機(jī)制,原理就是通過(guò)Nginx配置,將搜索引擎的爬蟲(chóng)請(qǐng)求轉(zhuǎn)發(fā)到一個(gè)node server,再通過(guò)PhantomJS來(lái)解析完整的HTML。廢話(huà)不說(shuō),直接上代碼吧!

準(zhǔn)備一個(gè)PhantomJS任務(wù)腳本

首先,我們需要一個(gè)叫spider.js的文件,用于phantomjs 解析網(wǎng)站。

// spider.js
"use strict";

// 單個(gè)資源等待時(shí)間,避免資源加載后還需要加載其他資源
var resourceWait = 500;
var resourceWaitTimer;

// 最大等待時(shí)間
var maxWait = 5000;
var maxWaitTimer;

// 資源計(jì)數(shù)
var resourceCount = 0;

// PhantomJS WebPage模塊
var page = require('webpage').create();

// NodeJS 系統(tǒng)模塊
var system = require('system');

// 從CLI中獲取第二個(gè)參數(shù)為目標(biāo)URL
var url = system.args[1];

// 設(shè)置PhantomJS視窗大小
page.viewportSize = {
    width: 1280,
    height: 1014
};

// 獲取鏡像
var capture = function(errCode){

    // 外部通過(guò)stdout獲取頁(yè)面內(nèi)容
    console.log(page.content);

    // 清除計(jì)時(shí)器
    clearTimeout(maxWaitTimer);

    // 任務(wù)完成,正常退出
    phantom.exit(errCode);

};

// 資源請(qǐng)求并計(jì)數(shù)
page.onResourceRequested = function(req){
    resourceCount++;
    clearTimeout(resourceWaitTimer);
};

// 資源加載完畢
page.onResourceReceived = function (res) {

    // chunk模式的HTTP回包,會(huì)多次觸發(fā)resourceReceived事件,需要判斷資源是否已經(jīng)end
    if (res.stage !== 'end'){
        return;
    }

    resourceCount--;

    if (resourceCount === 0){

        // 當(dāng)頁(yè)面中全部資源都加載完畢后,截取當(dāng)前渲染出來(lái)的html
        // 由于onResourceReceived在資源加載完畢就立即被調(diào)用了,我們需要給一些時(shí)間讓JS跑解析任務(wù)
        // 這里默認(rèn)預(yù)留500毫秒
        resourceWaitTimer = setTimeout(capture, resourceWait);

    }
};

// 資源加載超時(shí)
page.onResourceTimeout = function(req){
    resouceCount--;
};

// 資源加載失敗
page.onResourceError = function(err){
    resourceCount--;
};

// 打開(kāi)頁(yè)面
page.open(url, function (status) {

    if (status !== 'success') {

        phantom.exit(1);

    } else {

        // 當(dāng)改頁(yè)面的初始html返回成功后,開(kāi)啟定時(shí)器
        // 當(dāng)?shù)竭_(dá)最大時(shí)間(默認(rèn)5秒)的時(shí)候,截取那一時(shí)刻渲染出來(lái)的html
        maxWaitTimer = setTimeout(function(){

            capture(2);

        }, maxWait);

    }

});

讓我們來(lái)運(yùn)行一下吧~
$ phantomjs spider.js 'https://wj.qq.com/'

可在終端看到渲染后的HTML結(jié)構(gòu)啦!棒棒噠!

命令服務(wù)化

要做到響應(yīng)搜索引擎爬蟲(chóng)的請(qǐng)求,我們需要將此命令服務(wù)化,通過(guò)node起個(gè)簡(jiǎn)單的web服務(wù)。

// server.js
// ExpressJS調(diào)用方式
var express = require('express');
var app = express();

// 引入NodeJS的子進(jìn)程模塊
var child_process = require('child_process');

app.get('*', function(req, res){

    // 完整URL
    var url = req.protocol + '://'+ req.hostname + req.originalUrl;

    // 預(yù)渲染后的頁(yè)面字符串容器
    var content = '';

    // 開(kāi)啟一個(gè)phantomjs子進(jìn)程
    var phantom = child_process.spawn('phantomjs', ['spider.js', url]);

    // 設(shè)置stdout字符編碼
    phantom.stdout.setEncoding('utf8');

    // 監(jiān)聽(tīng)phantomjs的stdout,并拼接起來(lái)
    phantom.stdout.on('data', function(data){
        content += data.toString();
    });

    // 監(jiān)聽(tīng)子進(jìn)程退出事件
    phantom.on('exit', function(code){
        switch (code){
            case 1:
                console.log('加載失敗');
                res.send('加載失敗');
                break;
            case 2:
                console.log('加載超時(shí): '+ url);
                res.send(content);
                break;
            default:
                res.send(content);
                break;
        }
    });

});

app.listen(3000, function () {
  console.log('Spider app listening on port 3000!');
});

運(yùn)行node server.js,此時(shí)我們已經(jīng)有了一個(gè)預(yù)渲染的web服務(wù)啦,接下來(lái)的工作便是將搜索引擎爬蟲(chóng)的請(qǐng)求轉(zhuǎn)發(fā)到這個(gè)web服務(wù),最終將渲染結(jié)果返回給爬蟲(chóng)。
為了防止node進(jìn)程掛掉,可以使用nohup來(lái)啟動(dòng),nohup node server.js &

通過(guò)Nginx配置,我們可以輕松的解決這個(gè)問(wèn)題。

upstream spider_server {
  server localhost:3000;
}

server {
    listen       80;
    server_name  example.com;
    
    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;

      if ($http_user_agent ~* "Baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|bingbot|Sosospider|Sogou Pic Spider|Googlebot|360Spider") {
        proxy_pass  http://spider_server;
      }
    }
}

大功告成!

注:本文代碼主要參考以下文章的分享,鄙人略作修改后的可用版本,感謝分享!

參考鏈接:
https://www.mxgw.info/t/phantomjs-prerender-for-seo.html
http://imweb.io/topic/560b402ac2317a8c3e08621c
https://icewing.cc/linux-install-phantomjs.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 33款可用來(lái)抓數(shù)據(jù)的開(kāi)源爬蟲(chóng)軟件工具 要玩大數(shù)據(jù),沒(méi)有數(shù)據(jù)怎么玩?這里推薦一些33款開(kāi)源爬蟲(chóng)軟件給大家。 爬蟲(chóng),即...
    visiontry閱讀 7,699評(píng)論 1 99
  • 你爬了嗎? 要玩大數(shù)據(jù),沒(méi)有數(shù)據(jù)怎么玩?這里推薦一些33款開(kāi)源爬蟲(chóng)軟件給大家。 爬蟲(chóng),即網(wǎng)絡(luò)爬蟲(chóng),是一種自動(dòng)獲取網(wǎng)...
    Albert新榮閱讀 2,322評(píng)論 0 8
  • 《楚喬傳》這部劇最近挺火的,軍哥匆匆忙忙地快進(jìn)了幾集,就有了不少的靈感,所以,今天還是要寫(xiě)一寫(xiě)的。 簡(jiǎn)單地說(shuō),《楚...
    首席人才官閱讀 370評(píng)論 0 0
  • 據(jù)說(shuō)“妮妲”忘記了登陸密碼,沒(méi)有正面登陸,不知去哪兒了?廣西的同學(xué)家人來(lái)電都說(shuō)天還晴著呢,奇怪的事情天天有,我把它...
    蔣開(kāi)心閱讀 323評(píng)論 0 0
  • 茶余飯后,方總饒有興趣的和我們聊起了“赤腳醫(yī)生”的話(huà)題,他說(shuō)解放初期交通和醫(yī)療尚不發(fā)達(dá)的時(shí)候,就是靠每個(gè)村里的赤腳...
    手曲一指閱讀 799評(píng)論 0 51

友情鏈接更多精彩內(nèi)容