背景
单页面应用使用浏览器动态解析 JS,呈现出最终的页面,用户体验比较好,网站性能也提高不少。但网络爬虫并不会动态解析 Js (谷歌比较先进可以解析 JS),所以访问所有未处理的单页面应用 URL 得到的只会是项目入口(index.html)文件中的代码,不能得到具体的内容,对 Seo 的优化极其的不友好。为了优化项目 Seo,目前主要有两个方案:服务端渲染(Server Side Rendering)、预渲染(Prerending)。使用服务端渲染的话 需要对项目进行改造,也违背了开始做 SPA 的初衷,这里就不做深入了,这里提供了一种专门针对搜索引擎爬虫的渲染 SEO 优化
预渲染
使用 Prerender.io 做网站预渲染,可以将网站页面渲染之后再返回给网络爬虫,间接完成网页的解析。 Prerender 相较于其他的解决方案,配置相对要简单一些,不用修改项目源码,代码零侵入,是一个不错的解决方案。可以使用访问 htttps:/prerender.io
就可以按照步骤接入,但是这事商业化的方案,使用量较多时就需要付费了,免费的计划 适合小型项目,看个人需求。另一种是可以自建 prerender
服务 可以为多个项目进行 预渲染
自建 Prerender 服务
选用的是 Prerender.io 开源渲染方案 再加上自己的优化处理。这里就主要介绍搭建基于 Centos 7 和 Nginx 环境的 Prerender 渲染服务,完成项目中网页的预渲染
安装 Prerender 服务
- 在服务器上安装 Node 环境
- 在服务器上安装 Chrome
- 在 Node 部署 Perender 服务
安装 Node 环境
第一步安装 Node 环境 我就不多赘述了 网上教程一大堆
安装 Chrome
Centos 系统下安装
配置 yum 源 因为国内无法访问 Google,所以需要自己配置 yum 源,在目录
/etc/yum.repos.d/
下新建google-chrome.repo
文件cd /ect/yum.repos.d/ touch google-chrome.repo
写入内容 google-chrome.repo
[google-chrome] name=google-chrome baseurl=http://dl.google.com/linux/chrome/rpm/stable/$basearch enabled=1 gpgcheck=1 gpgkey=https://dl-ssl.google.com/linux/linux_signing_key.pub
安装运行
# 国内推荐 yum -y install google-chrome-stable --nogpgcheck
检查安装
安装成功后,Chrome
的安装路径应该是/opt/google/chrome
.
默认情况下,root
用户不能直接运行chrome
,所以可以 使用adduser www
新建另一个用户来运行,这里新建了 新的www
用户。cd /opt/google/chrome su www ./chrome
debian 系统下安装
需要通过apt在终端中运行以下命令来确保您的系统是最新的
sudo apt update sudo apt upgrade
从官方存储库安装Google Chrome,需要添加Google的GPG密钥:
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
将Google Chrome存储库添加到您的Debian系统
sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
使用以下命令安装Google Chrome浏览器64位稳定版
sudo apt update sudo apt install google-chrome-stable
其实不一定非要安装 Chrome ,开源的 chromium 也行
安装
sudo apt update sudo apt install chromium
找出位置 一般在 /usr/bin/chromium
which chromium
配置项目的chrome 地址
把得到的地址填入 chromeLocation
const server = prerender({ chromeLocation: '/usr/bin/chromium' });
部署 Perender 服务
本地新建项目
└─node_modules
└─index.js
└─package.json
先安装 prerender 木块
npm i prerender
编写 index.js
index.js
const prerender = require('prerender'); const server = prerender({ port:26666 }); server.start();
后台运行
nohup node ./server.js &
如此就在 本地 26666
端口 运行了。
安装中间件,这里使用 nginx 作为处理中间件
server {
listen 80;
server_name example.com;
root /path/to/your/root;
index index.html;
location / {
try_files $uri @prerender;
}
location @prerender {
# 如果使用 商业的 prerender.io 需要 将 YOUR_TOKEN替换为你的个人token
proxy_set_header X-Prerender-Token YOUR_TOKEN;
set $prerender 0;
# 这里判断哪些爬虫需要走预渲染
if ($http_user_agent ~* "googlebot|bingbot|yandex|baiduspider") {
set $prerender 1;
}
# 测试用
if ($args ~ "_escaped_fragment_") {
set $prerender 1;
}
if ($http_user_agent ~ "Prerender") {
set $prerender 0;
}
# 静态文件不需要预渲染
if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)") {
set $prerender 0;
}
#resolve using Google's DNS server to force DNS resolution and prevent caching of IPs
resolver 8.8.8.8;
if ($prerender = 1) {
# 后续自建将 http://service.prerender.io 替换为自己的prerender服务,如 http://127.0.0.1:26666
set $prerenderServer "http://service.prerender.io";
rewrite .* /$scheme://$host$request_uri? break;
proxy_pass $prerenderServer;
}
if ($prerender = 0) {
rewrite .* /index.html break;
}
}
}
nginx 配置如上,看注释 自及修改自己的配置,检测nginx配置,并重启nginx。
至此,Prerender 服务已经安装并启动成功。
测试
通过curl命令测试
curl http://www.example.com/?_escaped_fragment_=
一般情况下就可看到 实际运行渲染的 html 结构了
也可以 在网上找一个 在线模拟百度爬虫的网站 对网站进行模拟访问一下,看看 html 结构
其他优化
缓存优化
同一个页面 每次 进来 都要进过chrome 来进行渲染,这很影响性能,这时可以通过缓存在优化,在有效时间内,如果有缓存直接取缓存数据返回
需要编写缓存处理
在 node 项目中安装 cache-manager-fs
模块
npm i cache-manager-fs
添加 插件 文件夹 及文件
└─node_modules
└─plugins
│ └─fs-cache.js
└─index.js
└─package.json
fs-cache.js
var cacheManager = require('cache-manager');
var fsStore = require('cache-manager-fs');
module.exports = {
init: function () {
this.cache = cacheManager.caching({
store: fsStore,
maxsize: process.env.CACHE_MAXSIZE || 500 * 1000 * 1000 * 1000,
ttl: process.env.CACHE_TTL || 24*60*60, /*seconds*/
path: 'cache',
preventfill: true
});
},
requestReceived: function (req, res, next) {
this.cache.get(req.prerender.url, function (err, result) {
if (!err && result) {
req.prerender.cacheHit = true;
console.log('cacheHit 缓存命中', req.prerender.url)
res.send(200, result);
} else {
next();
}
});
},
beforeSend: function (req, res, next) {
if (!req.prerender.cacheHit && req.prerender.statusCode == 200) {
this.cache.set(req.prerender.url, req.prerender.content);
}
next();
}
};
其中 maxsize
ttl
path
可以通过 环境变量设置也可以 使用默认设置
相应的 index.js 也需要小小修改
const prerender = require('prerender');
const fsCache = require('./plugins/fs-cache.js');
const server = prerender({
port:26666
});
server.use(fsCache)
server.start();
重启 服务就可以实现了
接口保护
如果你的服务需要收到保护,防止别人恶意使用 可以像 prerender.io 添加接口 token 保护
修改 index.js
const prerender = require('prerender');
const fsCache = require('./plugins/fs-cache.js');
const server = prerender({
port:26666
});
server.use({
requestReceived: (req, res, next) => {
const canPassToken = [] //这里放 可以通过的token
const token = req.headers['x-prerender-token']; // 这里使用与 prerender.io 一样的头部字段名
if (!canPassToken.includes(token)) { // 如果 token 不在 数据里 则返回403
res.send(403)
} else {
next()
}
},
})
server.use(fsCache)
server.start();
赞,试试