前言
博客上线也有一段时间了,也在各大搜索引擎上提交了收录信息,但没啥用,闲着无聊就开始搞起seo了,vue 作为一个单页面应用,都是通过js来渲染页面,这就导致了蜘蛛在爬取网站的时候只能获取到一个空壳。没有信息自然也不会被收录。于是开始研究Vue seo优化问题,本来想使用官方的做法服务器渲染,但这样会导致很多问题,由于我的功能已经开发好了,如果使用服务器渲染,那我的整个项目都要进行重构,费时费力。而且我使用的阿里云学生服务器,性能也不够用啊!于是我采用了预渲染的方式,针对蜘蛛爬虫,在服务器开一个小灶给它,通过nginx服务器来判断是否为爬虫(通过请求头判断的),如果为爬虫就把请求转发的预渲染服务器中,然后再把渲染好的页面返回给爬虫,大概时这么个思想。由于方案太多前前后后试了几个方法。最后使用了puppeteer
(性能好)
puppeteer
渲染
Puppeteer 是 Chrome 开发团队在 2017 年发布的一个 Node.js 包,用来模拟 Chrome 浏览器的运行
由于我Linux
还是不熟练,所以使用的宝塔面板配置的,可以在宝塔面板中下一个pm2管理器( 管理你的node进程,也包括了node.js和npm )安装成功后就可以按下面方法配置。Puppeteer Api
- 首先安装
puppeteer
:npm install puppeteer --save - 安装依赖:
# 依赖库
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 -y
# 字体
yum install ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc -y
接下来就是配置服务器代码了
puppeter.js性能优化,去除不必要的功能,提高性能。
const puppeteer = require('puppeteer')
const MAX_WSE = 2; //启动几个浏览器
let WSE_LIST = []; //存储browserWSEndpoint列表
//负载均衡
(async () => {
for (var i = 0; i < MAX_WSE; i++) {
const browser = await puppeteer.launch({
//无头模式
headless: true,
//参数
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-first-run',
'--no-sandbox',
'--no-zygote',
'--single-process'
]
});
browserWSEndpoint = await browser.wsEndpoint();
WSE_LIST.push(browserWSEndpoint);
}
})();
module.exports = WSE_LIST
spider.js渲染请求的页面
const puppeteer = require('puppeteer')
const WSE_LIST = require('./puppeteer-pool.js')
const spider = async (url) => {
let tmp = Math.floor(Math.random() * WSE_LIST.length);
//随机获取浏览器
let browserWSEndpoint = WSE_LIST[tmp];
//连接
const browser = await puppeteer.connect({
browserWSEndpoint
});
//打开一个标签页
var page = await browser.newPage();
//打开网页
await page.goto(url, {
timeout: 0, //连接超时时间,单位ms
waitUntil: 'networkidle0' //网络空闲说明已加载完毕
})
//获取渲染好的页面源码。不建议使用await page.content();获取页面,因为在我测试中发现,页面还没有完全加载。就获取到了。页面源码不完整。也就是动态路由没有加载。vue路由也配置了history模式
var html = await page.evaluate(() => {
return document.getElementsByTagName('html')[0].outerHTML;
});
await page.close();
return html;
}
module.exports = spider;
server.js,通过express 开启一个服务器。接受转发的请求
var express = require('express');
var app = express();
var spider = require("./spider1.js")
var minify = require('html-minifier').minify;
app.get('*', async (req, res, next) => {
// 部署到服务器的完整URL
var url = req.protocol + '://'+ req.hostname + req.originalUrl;
console.log('请求的完整URL:' + url);
var content = await spider(url).catch((error) => {
console.log(error);
res.send('获取html内容失败');
return;
});
//由于是直接获取的源码,下面通过minify库压缩代码,也不知道是不是多余的。
content=minify(content,{removeComments: true,collapseWhitespace: true,minifyJS:true, minifyCSS:true});
res.send(content);
});
//监听3000端口
app.listen(3000, () => {
console.log('预渲染服务已启动!');
});
Nginx配置
upstream spider_server {
server localhost:3000;
}
server
{
location / {
# 蜘蛛爬虫处理
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
if ($http_user_agent ~* "spider|bot") {
proxy_pass http://spider_server;
}
try_files $uri $uri/ @router;
}
location @router {
rewrite ^(.*)$ /index.html last;
}
……
PhantomJS
刚开始接触的就是这种方法,后来了解puppeteer
出来了,PhantomJS
开发者就不维护了。也和puppeteer
做了性能对比,性能也不行。这也不是最关键的,在我用百度站长工具爬取我的文章是,发现文章并没有渲染,可能是因为使用了新的语法了吧。毕竟开发者也不维护了。所以直接放代码就不过多的解释了。
// spider.js
"use strict";
// 单个资源等待时间,避免资源加载后还需要加载其他资源
var resourceWait = 500;
var resourceWaitTimer;
// 最大等待时间
var maxWait = 5000;
var maxWaitTimer;
// 资源计数
var resourceCount = 0;
// PhantomJS WebPage模块
var page = require('webpage').create();
// NodeJS 系统模块
var system = require('system');
// 从CLI中获取第二个参数为目标URL
var url = system.args[1];
// 设置PhantomJS视窗大小
page.viewportSize = {
width: 1280,
height: 1014
};
// 获取镜像
var capture = function(errCode){
// 外部通过stdout获取页面内容
console.log(page.content);
// 清除计时器
clearTimeout(maxWaitTimer);
// 任务完成,正常退出
phantom.exit(errCode);
};
// 资源请求并计数
page.onResourceRequested = function(req){
resourceCount++;
clearTimeout(resourceWaitTimer);
};
// 资源加载完毕
page.onResourceReceived = function (res) {
// chunk模式的HTTP回包,会多次触发resourceReceived事件,需要判断资源是否已经end
if (res.stage !== 'end'){
return;
}
resourceCount--;
if (resourceCount === 0){
// 当页面中全部资源都加载完毕后,截取当前渲染出来的html
// 由于onResourceReceived在资源加载完毕就立即被调用了,我们需要给一些时间让JS跑解析任务
// 这里默认预留500毫秒
resourceWaitTimer = setTimeout(capture, resourceWait);
}
};
// 资源加载超时
page.onResourceTimeout = function(req){
resouceCount--;
};
// 资源加载失败
page.onResourceError = function(err){
resourceCount--;
};
// 打开页面
page.open(url, function (status) {
if (status !== 'success') {
phantom.exit(1);
} else {
// 当改页面的初始html返回成功后,开启定时器
// 当到达最大时间(默认5秒)的时候,截取那一时刻渲染出来的html
maxWaitTimer = setTimeout(function(){
capture(2);
}, maxWait);
}
});
server.js
// server.js
// ExpressJS调用方式
var express = require('express');
var app = express();
// 引入NodeJS的子进程模块
var child_process = require('child_process');
app.get('*', function(req, res){
// 完整URL
var url = req.protocol + '://'+ req.hostname + req.originalUrl;
// 预渲染后的页面字符串容器
var content = '';
// 开启一个phantomjs子进程
var phantom = child_process.spawn('phantomjs', ['spider.js', url]);
// 设置stdout字符编码
phantom.stdout.setEncoding('utf8');
// 监听phantomjs的stdout,并拼接起来
phantom.stdout.on('data', function(data){
content += data.toString();
});
// 监听子进程退出事件
phantom.on('exit', function(code){
switch (code){
case 1:
console.log('加载失败');
res.send('加载失败');
break;
case 2:
console.log('加载超时: '+ url);
res.send(content);
break;
default:
res.send(content);
break;
}
});
});
app.listen(3000, function () {
console.log('Spider app listening on port 3000!');
});
Prerender
使用Prerender
优化seo,经过我的测试,这种方法要优于PhantomJS
但不如puppeteer
(都在服务器上测试,且访问同样的页面),而且还要使用Chrome
安装Chrome
- 配置yum源
国内无法访问Google,需要配置yum源,在目录 /etc/yum.repos.d/ 下新建google-chrome.repo文件
cd /ect/yum.repos.d/
touch google-chrome.repo
- 通过vi编辑器添加内容
vi 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
- 安装成功后路径
/opt/google/chrome
- 安装成功后无法打开chrome
主要的解决办法是找到/opt/google/chrome
目录,编辑google-chrome
文件,把最后一行的
exec -a "$0" "$HERE/chrome" "$@"
修改为
exec -a "$0" "$HERE/chrome" "$@" --user-data-dir $HOME
安装Prerender
git clone https://github.com/prerender/prerender.git
cd prerender
npm install
#启动server.js, 默认监听3000端口
node server.js
通过curl 命令解析你要访问的网址,如果返回渲染好的html 就说明成功了。
curl http://localhost:3000/你的网站路径
Nginx配置
location / {
# 表示是否需要代理
set $prerender 0;
# 代理地址
set $prerender_url "http://127.0.0.1:3000";
# 判断请求是否来自蜘蛛,如果是则表示需要代理
if ($http_user_agent ~* "baiduspider|Googlebot|360Spider|Bingbot|Sogou Spider|Yahoo! Slurp China|Yahoo! Slurp|twitterbot|facebookexternalhit|rogerbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
set $prerender 1;
}
if ($prerender = 1) {
proxy_pass $prerender_url;
rewrite ^(.*)$ /https://$host$1 break;
}
}
总结
这三种方法我都在我的网站服务器测试过,并通过百度官方的抓取诊断测试过,也使用过Postman
工具本地批量抓取测试。虽然说PhantomJS
响应时间都短,但它不适合我的项目,因为部分页面没有渲染出来(文章),而Prerender
在爬取的时候服务器几乎满载了。而且部分页面也没渲染完全。经过我不断地百度最后才使用了puppeteer
简单不复杂,配置语句也基本能看懂。
效果
目前本博客部分文章和页面已经被谷歌收录。
而百度只收录了一个首页,不知道是为什么,蜘蛛爬取频率也不高,可能是有点懒吧!其实百度搞了好久,而谷歌就提交了几个网址,效率不行啊!可能我更新的也比较慢!