单页面网站,比如 vue、recat 框架的网站,一般都是直接从服务器推送 index.html,再根据自身路由通过 js 在客户端浏览器渲染出完整的 html 页面。
但是搜索引擎的爬虫可没有这么智能(实际上 google 就有这么智能,拿到 js 文件自动帮你渲染好,但身在 CN,将就下百度这个阿斗吧),为了 SEO,要想爬虫爬到你的网站的内容,就得先由服务器把页面渲染好后再发送给爬虫,这就尴尬了,传统的服务器渲染是多页面的,一个请求对应一个页面,但 SPA 不是啊,本来就一个单页面,你叫我写各种路由对应渲染好了再给你??当然也不是不可以,以下就是几种方案:
react 自带的 renderToString
react 自带的 renderToString 和 renderToStaticMarkup 就可以用来将组件(Virtual DOM)输出成 HTML 字符串,看起来不错,但是要自己配参数啊,webpack 不会怎么办,原本路由写在一起怎么办,redux 要改动怎么办,如果这些你都 ok 的话,react 自带的方案也是一种不错的选择,这里就不多说了,网上相关帖子很多。
nextjs
还有一种方案,就更尴尬了,叫 nextjs 框架(nextjs 是 react 的,vue 的叫 nuxtjs),这种框架写出来直接就是多页面的,也就是用 react 的语法和规则,写出多页面网站来,每个页面的入口名字就是路由名字,服务器也是 nextjs 自带的,短短几行就能把单个网页渲染好并推送出去,是不是看起来棒棒的?!
那为啥说尴尬呢?就是因为他虽说跟 react 很像,但还是一个新框架,你不得不花时间学一下 nextjs;他的路由对应页面文件,这种路由看似简单明了,但是一点都不自由;nextjs 是多页面的,好不容易进化到单页面,你让我再回到中世纪?原罪啊!所以如果你已经写好了一个单页面网站,要改成 nextjs 框架的话,我只能说呵呵了,这返工返的……
rendertron
我们的主角要出场了!rendertron 的由来我不多说了,当初诞生就是为了做 SEO 的。先说说原理,听完你就知道是个好东西了。
Rendertron 是 nodejs 框架下的产物,是 google-chrome 旗下的的配套产品。首先,服务器上装有个 google-chrome,rendertron 把他打开,然后在服务器(官方推荐 express)中增加中间件,先判断 UA(user-agent)里面有没有带有类似 Baiduspider(百度爬虫)等字样,如果没有,就像正常的单页面服务器那样,把原始 html 推送出去,由客户端浏览器完成 js、css 渲染的工作;如果带有指定 UA 头字样,就先把网页推送给本地服务器那个 google-chrome,等他渲染好对应页面后,把渲染好的 html 结果推送出去。不就是为了 SEO 么,你爬虫来了我再渲染给你总行了吧,其余的我还是做我的单页面,呵呵哒。
下面上一张图,说明原理。(原理跟 rendora 的差不多,下图的 rendora 你把他换成 rendertron 就好了)
好了,现在讲讲怎么用,官网有用法,我给个链接 rendertron 官网。但是这里我讲一下具体怎么用的一种无脑方案,你照做就可以了,另外官网 demo 还有不少的坑和 bug,我也挑出来和大家分享一下。
安装 Chromeheadless
先在你的服务器上安装 Chromeheadless,这是服务器上的无头 chrome 浏览器,如果你的服务器上已经有这个,那恭喜你了,因为安装过程的坑实在是太多太多了。网上关于安装 Chromeheadless 的教学贴很多,在此我贴几个。
先在你的服务器上安装 Chromeheadless,这是服务器上的无头 chrome 浏览器,如果你的服务器上已经有这个,那恭喜你了,因为安装过程的坑实在是太多太多了。网上关于安装 Chromeheadless 的教学贴很多,在此我贴几个:
- 在 ubuntu 服务器上使用 Chrome Headless
- linux 安装 Headless Chrome – bambooleaf – CSDN 博客
- Chromeheadless 安装与使用 – 探索技术世界 – CSDN 博客
安装过程中你会遇到很多坑,不过不要紧,把 error 复制粘贴一下放百度,还是有很多解决方案的,毕竟 Chromeheadless 不是什么小众的东西
安装 rendertron
直接命令行输入
npm install -g rendertron
回车,就装好了。就这么简单?呵呵,有 greatWall,安装过程中必需的某个东东下载不了,这时候要用代理,会的同学当然 ok,但是不会的同学就没办法了,我自己一台大陆一台海外,大陆的装不了就改海外服务器了。
还有一个方案,也是官方给出的:
git clone https://github.com/GoogleChrome/rendertron.git
cd rendertron
npm install
npm run build
运行:
npm run start
能用第一种安装方案的推荐用第一种。
在你的 express 服务器程序中引入中间件 rendertron-middleware
进入项目目录,命令行输入并回车
npm install --save express rendertron-middleware
在你的 express 服务器程序的代码中加入几行:
const express = require('express');const rendertron = require('rendertron-middleware');
const app = express();
app.use(rendertron.makeMiddleware({proxyUrl: 'http://localhost:3000/render',}));
app.use(express.static('files'));app.listen(8080);
重点是插入的
app.use(rendertron.makeMiddleware({proxyUrl: 'http://localhost:3000/render',}));
其中 rendertron.makeMiddleware 有好几个参数,github 上说有
proxyUrl、userAgentPattern、excludeUrlPattern、injectShadyDom、timeout,也就是有类似以下的设置。app.use(rendertron.makeMiddleware({
proxyUrl: 'http://localhost:3000/render',
userAgentPattern:****,excludeUrlPattern:****,injectShadyDom:true or false, // 这个参数一般不用设,用的时候这行删掉就好了 timeout:11000,// 这个 timeout 超时参数亲测已经无效了,尴尬,用的时候也去掉这行就好}));
下面重点讲讲以上的 userAgentPattern 和 excludeUrlPattern 参数的含义和怎么设置。
userAgentPattern 是用来写清楚哪些 UA 头需要服务器渲染,除此之外的请求都直接单页面推送。比如说:
const botUserAgents = [
'Baiduspider',
'bingbot',
'Embedly',
'facebookexternalhit',
'LinkedInBot',
'outbrain',
'pinterest',
'quora link preview',
'rogerbot',
'showyoubot',
'Slackbot',
'TelegramBot',
'Twitterbot',
'vkShare',
'W3C_Validator',
'WhatsApp',];// 略 n 行代码 app.use(rendertron.makeMiddleware({// 其他参数 userAgentPattern: new RegExp(botUserAgents.join('|'), 'i'),}));
把你要的爬虫 UA 头都写到一个数组里,然后用 new RegExp()正则一下
excludeUrlPattern 是指哪些文件格式需要在 chromeheadless 中被完全加载,用法如下
const staticFileExtensions = [
'ai', 'avi', 'css', 'dat', 'dmg', 'doc', 'doc', 'exe', 'flv',
'gif', 'ico', 'iso', 'jpeg', 'jpg', 'js', 'less', 'm4a', 'm4v',
'mov', 'mp3', 'mp4', 'mpeg', 'mpg', 'pdf', 'png', 'ppt', 'psd',
'rar', 'rss', 'svg', 'swf', 'tif', 'torrent', 'ttf', 'txt', 'wav',
'wmv', 'woff', 'xls', 'xml', 'zip',];// 略 n 行代码 app.use(rendertron.makeMiddleware({// 其他参数 userAgentPattern: new RegExp(`\\.(${staticFileExtensions.join('|')})$`, 'i'),}));
把你需要加载的文件后缀都写到一个数组里,然后用 new RegExp()正则一下
至于 proxyUrl
参数的用法,如果 rendertron+chromeheadless 在本地服务器,那就写'http://localhost:3000/render'
(rendertron 启动后默认开启 3000 端口),如果是在别的服务器,那就写 http://www.xxxx.com/render 或 …://106.xx.xx.xxx:xxxx/render。
proxyUrl
参数设置的就是遇到爬虫 UA 头后、转到 rendertron 用本地服务器上的 chromeheadless 浏览器渲染的地址。
好了,express 服务器改写好后,正常启动他,然后再启动 rendertron,方法也很简单,直接命令行输入 rendertron
就行了。
下面测试一下,命令行输入curl -A“baiduspider”http:// 你需要测试的网址(就是访问你的 server 程序对应的那个网址,即改写前的那个原来的网址)
,然后就能看到通过 chromeheadless 渲染好的 html 代码,大功告成!
rendertron 的改进
有没有发现你每次 curl 以后都需要 10s 左右后才能返回数据,这种响应时间怎么可能用在 SEO 上呢??!!所以要改进一下咯。
在 rendertron 的 github 上有写到,可以在 rendertron 的根目录写一个config.json
,里面可以设置 datastoreCache(是否适用缓存,默认 false),timeout(渲染超时,过了这个时间还没有渲染结束就硬性返回已渲染好的 html,默认 10000ms,即 10s),port(端口,默认 3000),width 和 height(渲染用的浏览器屏幕宽高,默认 1000,这个在 rendertron 的另一个功能‘截图’上可以用到)。
然后你很天真的设置了 config.json 文件,把 timeout 改成 3000ms——这差不多已经是搜索引擎认为你是优质网站所要求的响应时间的上限了。再一次 curl,什么!!还是 10s!!这个怎么搞哦!!
又一次大写的尴尬!下面来公布答案吧~rendertron 的码源里面已经没有引用 config.json 里面的 timeout 参数了,这个参数没法通过外部设置,呵呵哒,坑。
好了,下面说解决方法。找到 rendertron
根目录,里面有个 build
文件夹,里面有个 renderer.js
文件,打开后,搜索 timeout,一共有两处,后面都跟了 10000 这个值,把它改成你需要的 2000 或者之类的(单位 ms),然后再次重新启动 rendertron,在命令行输入
curl -A“baiduspider”http:// 你需要测试的网址(就是访问你的 server 程序对应的那个网址,即改写前的那个原来的网址)
可以发现只需要 2s 就有 html 代码返回了,搞定!
注:以上问题是针对
npm install -g rendertron
方式安装的 rendertron,其他方式安装后是否有以上问题不一定。
如果你的渲染程序会崩,那就pm2 start rendertron
。pm2 真的挺好用,不熟悉的同学百度下就好了,满满的资料。
原文链接:单页面 (如 react,vue) 网站的服务器渲染 SSR 之 SEO 大杀器 Rendertron