打算用vue等mvvm的框架做單頁應(yīng)用,seo是一個(gè)頭疼的問題韩脏。雖然官方提供了服務(wù)端渲染的方案缩麸,總覺著那樣就失去了客戶端渲染的意義。
所以通過另一途徑來解決spa單頁應(yīng)用的seo問題:通過一個(gè)旁路渲染服務(wù)赡矢,讓爬蟲過去杭朱,抓取渲染好的頁面。
而這種渲染吹散,完全是通過反代線上單頁應(yīng)用完成的弧械。
這種方式的優(yōu)點(diǎn)是簡單、無需部署兩套系統(tǒng)空民。
缺點(diǎn)就是速度有些堪憂刃唐,后續(xù)還要想辦法優(yōu)化一下。
關(guān)于是否被爬蟲認(rèn)定作弊的問題界轩,還是有待探討的(但畢竟返回的頁面信息是一樣的画饥,也許不算作弊吧)
下面說一下架設(shè)過程。
本來打算用phantomjs來完成js頁面的渲染耸棒。但是發(fā)現(xiàn)vue用到的ES6特性荒澡,貌似無法完成,再加之chrome headless出來后与殃,phantomjs的作者宣布停止更新单山,所以索性就換一套方案,用chrome headless+Puppeteer幅疼。
chrome headless是谷歌退出的chrome瀏覽器的無頭版米奸,Puppeteer是谷歌官方出的一套基于nodejs的chrome headless API。
Puppeteer的API手冊(英文版):https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md
chrome.js代碼爽篷,用來開啟一個(gè)chrome瀏覽器悴晰。
// chrome.js
"use strict";
const puppeteer = require('puppeteer');
var fs = require("fs") ;
puppeteer.launch({dumpio:true,args: ['--no-sandbox', '--disable-setuid-sandbox'],timeout: 10000}).then(
? async browser => {
? fs.writeFile("chrome.txt",browser.wsEndpoint(),function (err) {
? ? if (err) throw err ;
? ? console.log("存入chrome.txt成功"); //文件被保存
? }) ;
? browser.disconnect()
});
app.js代碼,用來監(jiān)聽需要反代的網(wǎng)址逐工。
插一句铡溪,這篇文章首發(fā)在簡-書上面,怕被采集到其他平臺上找不到來源泪喊,搞不懂的朋友可以來評論交流(搜索標(biāo)題找)
const express = require('express');
const app = express();
const fs = require("fs");
const puppeteer = require('puppeteer');
const browserUrl = fs.readFileSync("chrome.txt","utf8");
app.get('*', function (req, res) {
? ? var url = req.protocol + '://'+ req.hostname + req.originalUrl;
? ? var ua = req.headers['user-agent'];
? ? (async() => {
? ? ? ? const browser = await puppeteer.connect({browserWSEndpoint:browserUrl});
? ? ? ? const page = await browser.newPage(); //創(chuàng)建一個(gè)頁面.
? ? ? ? try {
? ? ? ? ? ? await page.goto(url); //到指定頁面的網(wǎng)址.
? ? ? ? ? ? await page.waitFor(500);
? ? ? ? }
? ? ? ? catch (err) {
? ? ? ? ? ? await page.close();
? ? ? ? ? ? await browser.disconnect();
? ? ? ? ? ? console.log('出現(xiàn)錯(cuò)誤:'+err); // 這里捕捉到錯(cuò)誤 `error`
? ? ? ? }
? ? ? ? res.send(await page.content());? ? ? ?
? ? ? ? await page.close();
? ? ? ? await browser.disconnect();
? ? })();
});
var server = app.listen(3000,'127.0.0.1', function () {
? ? var host = server.address().address;
? ? var port = server.address().port;
? ? console.log('Example app listening at http://%s:%s', host, port);
});
然后分別啟動(dòng)兩個(gè)服務(wù):node chrome.js 和node app.js?
這樣棕硫,給你的nginx加一個(gè)UA驗(yàn)證,當(dāng)時(shí)蜘蛛的時(shí)候袒啼,就反代到旁路渲染服務(wù)上去哈扮。
代碼如下(nginx.conf):
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;
? ? ? ? proxy_set_header? X-Forwarded-Proto $scheme;
? ? ? ? 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? $scheme://127.0.0.1:3000;
? ? ? ? }
? ? ? ? index? index.html index.htm index.php;
? ? }
至此纬纪,當(dāng)爬蟲訪問你的SPA頁的時(shí)候,返回的就是被渲染好滑肉,滿滿數(shù)據(jù)的頁面了包各。
我是在nodejs的反代前面,又架設(shè)了一層nginx反代靶庙,好處是功能多问畅,比如緩存,ip控制等惶洲。這里就不寫出來了按声。
其實(shí)我感覺膳犹,速度上恬吕,好像chrome headless沒有phantomjs的快,也許是第一次用phantomjs的時(shí)候须床,沒有渲染完全的錯(cuò)覺铐料。這個(gè)速度,也和要渲染的頁面有關(guān)豺旬∧瞥停看js多少。另外族阅,在puppeteer配置方面篓跛,可以加快速度的方法,朋友們可以評論告知坦刀,不勝感激愧沟。
參考學(xué)習(xí):
http://www.r9it.com/20171106/puppeteer.html (中文api教程)
https://segmentfault.com/a/1190000011382062