打算用vue等mvvm的框架做单页应用,seo是一个头疼的问题。虽然官方提供了服务端渲染的方案,总觉着那样就失去了客户端渲染的意义。
所以通过另一途径来解决spa单页应用的seo问题:通过一个旁路渲染服务,让爬虫过去,抓取渲染好的页面。
而这种渲染,完全是通过反代线上单页应用完成的。
这种方式的优点是简单、无需部署两套系统。
缺点就是速度有些堪忧,后续还要想办法优化一下。
关于是否被爬虫认定作弊的问题,还是有待探讨的(但毕竟返回的页面信息是一样的,也许不算作弊吧)
下面说一下架设过程。
本来打算用phantomjs来完成js页面的渲染。但是发现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代码,用来开启一个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代码,用来监听需要反代的网址。
插一句,这篇文章首发在简-书上面,怕被采集到其他平台上找不到来源,搞不懂的朋友可以来评论交流(搜索标题找)
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(); //创建一个页面.
try {
await page.goto(url); //到指定页面的网址.
await page.waitFor(500);
}
catch (err) {
await page.close();
await browser.disconnect();
console.log('出现错误:'+err); // 这里捕捉到错误 `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);
});
然后分别启动两个服务:node chrome.js 和node app.js
这样,给你的nginx加一个UA验证,当时蜘蛛的时候,就反代到旁路渲染服务上去。
代码如下(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;
}
至此,当爬虫访问你的SPA页的时候,返回的就是被渲染好,满满数据的页面了。
我是在nodejs的反代前面,又架设了一层nginx反代,好处是功能多,比如缓存,ip控制等。这里就不写出来了。
其实我感觉,速度上,好像chrome headless没有phantomjs的快,也许是第一次用phantomjs的时候,没有渲染完全的错觉。这个速度,也和要渲染的页面有关。看js多少。另外,在puppeteer配置方面,可以加快速度的方法,朋友们可以评论告知,不胜感激。
参考学习:
http://www.r9it.com/20171106/puppeteer.html (中文api教程)
https://segmentfault.com/a/1190000011382062