我们公司有个前台项目,但是使用了vue做成了一个SPA项目,我们都知道vue SPA项目的每一个路由跳转的页面都没有真实发起的请求。
我经常会在面试的时候问,为什么vue项目在搜索引擎优化这方面会有问题,这个问题其实是因为vue项目只有一个真实的可供浏览器访问的地址,因此百度蜘蛛每次的访问只会访问到“index.html”。同时,百度搜索引擎抓取这个思路涉及到一些nodejs服务器和Nginx服务器的知识。
一、整体思路
百度蜘蛛只会抓取网站的静态文件,因此不会抓取到前后端分离项目最终渲染的页面。因此我们需要让蜘蛛从服务器上抓取浏览器渲染完成的页面。经过搜索,我们知道有一种可以在服务器端运行的东西,叫“无头浏览器”,就是一个无界面的浏览器,可以用于页面的服务器端渲染,网页截图等工作。目前流行的服务器端渲染工具有“phantomjs”、基于node的 “puppeteer”等,我们选用的是“phantom.js”。
在此基础上如果我们在所有情况下都使用“phantomjs”渲染页面的话,显然不现实,不但会导致路由跳转在页面上无法操作,甚至还会导致服务器压力过大,基于此我们需要使用nginx通过判断是否是蜘蛛访问来进行转发,以此提高访问效率。
与此同时为了保障phantomjs服务的稳定,我们需要使用node进程管理工具“PM2”对服务进行管理
二、具体实现
1 我们需要使用一台服务器安装基本的node服务,phantomjs服务,以及pm2工具,为了保障项目的正常运行,我们尽量不要选择项目同源服务器!
2 安装完毕后 创建一个js 就命名为“spider.js”吧 内容如下
/*global phantom*/
"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: 1920,
height: 1014
};
// 获取镜像
var capture = function(errCode){
// 外部通过stdout获取页面内容
console.log(page.content);
// 清除计时器
clearTimeout(maxWaitTimer);
// 任务完成,正常退出
setTimeout(phantom.exit(errCode), 1500);
};
// 资源请求并计数
page.onResourceRequested = function(req){
resourceCount++;
clearTimeout(resourceWaitTimer);
};
// 资源加载完毕
page.onLoadFinished = function(status) {
if (status !== 'success'){
return;
}
else{
resourceWaitTimer = setTimeout(capture, resourceWait);
}
// Do other things here...
};
// 资源加载超时
page.onResourceTimeout = function(req){
resouceCount--;
};
// 资源加载失败
page.onResourceError = function(err){
resourceCount--;
};
page.onError = function(msg, trace) {
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function + '")' : ''));
});
}
// uncomment to log into the console
// console.error(msgStack.join('\n'));
};
// 打开页面
page.open(url, function (status) {
// console.log('spider--url:'+url);
if (status !== 'success') {
setTimeout(phantom.exit(), 1500);
} else {
// 当改页面的初始html返回成功后,开启定时器
// 当到达最大时间(默认5秒)的时候,截取那一时刻渲染出来的html
maxWaitTimer = setTimeout(function(){
capture(2);
}, maxWait);
}
});
3. 以此为基础 在命令行里执行 phantomjs
phantomjs spider.js https://www.baidu.com
我们会发现不会返回内容,因为 访问https失败 需要增加配置 修改为
phantomjs --ignore-ssl-errors=true spider.js https://www.baidu.com
我们就能看到返回的内容啦
但是我们的目的是通过蜘蛛访问到地址后获得返回内容,因此我们还需要以下操作
4 创建服务向外暴露phantomjs返回内容
接下来我们就需要用到nodejs啦,我们需要用到express创建一个服务,然后把phantomjs,,放在nodejs的子进程后,监听子进程退出事件,获得phantomjs的返回内容,然后返回给浏览器。
具体实现如下
// server.js
// ExpressJS调用方式
var express = require('express');
// var phantomjs = require('phantom');
var app = express();
var path = process.cwd();
// 引入NodeJS的子进程模块
var child_process = require('child_process');
app.get('*', function(req, res){
// 部署到服务器的完整URL
var url = req.protocol + '://'+ req.hostname + req.originalUrl;
// var url = 'https://test.zgzjzj.com/font/newtest/#/index';
console.log('server--url:'+url);
console.log('server--req:'+req.ip);
try {
console.log('server--req:'+JSON.stringify(req));
} catch (error) {
}
// 测试的url
// var url = 'https://www.baidu.com'
// 预渲染后的页面字符串容器
var content = '';
// 开启一个phantomjs子进程
var phantom = child_process.spawn('phantomjs', ['--ignore-ssl-errors=true','--load-images=false','--local-storage-path=/usr/local/storage',path + '/spider.js', url]);
console.log(phantom);
// 设置stdout字符编码
phantom.stdout.setEncoding('utf8');
// 监听phantomjs的stdout,并拼接起来
phantom.stdout.on('data', function(data){
content += data.toString();
});
// 监听子进程退出事件
phantom.on('exit', function(code){
console.log("content是:")
console.log(content)
switch (code){
case 1:
console.log('加载失败');
res.send('加载失败');
break;
case 2:
console.log('加载超时: '+ url);
res.send(content);
break;
default:
console.log('加载页面: '+ url);
res.send(content);
break;
}
});
});
app.listen(8081, function () {
console.log('Spider app listening on port 8081!');
console.log(path + '/spider.js')
});
现在我们就可以通过访问服务器IP地址实现 phantomjs的访问了!
5. 到此为止我们已经完成了一大半内容,现在的问题是,我们怎么才能通过访问实际项目地址实现获取渲染后的页面内容
此处就需要使用到nginx了,以下需要有nginx的基础
我们在前端项目服务器nginx配置文件里 增加如下代码:
http {...............
upstream spider_server {
server 3x.9x.1xx.2xx:8081;//安装phantomjs的服务器
} ...........
}
和
location /{
......
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 http://spider_server;
}
......
}
重启nginx后 就可以啦
这里可以用百度模拟抓取测试一下
也可以用第三方模拟抓取测试一下“http://www.cjzzc.com/crawl.html”
可以用一下网址进行测试
顺便给我司网站做一个外链 专技天下网 甘肃专技 北京思想天下 北京思想天下 甘肃专技 甘肃省地质矿产勘查开发局继续教育培训平台
6.接下来对项目进行优化
(1)尽量把hash模式改为history模式。
(2)如果可以分模块部署vue项目那就再好不过了!
(3)对于项目内的新闻模块或产品模块可以用一些<a href="***" title="******************"></a>, 减少路由跳转!
(4) 可能的话 发发外链!多在百度站长提交一些网址!
(5)对项目各个页面进行TDK的设置,可以从后台增加 ,也可从前台直接取接口中的新闻标题等。
说个重要的东西,
!!!!!
phantom.js不支持es6的语法,所以在index.html或者静态引用的资源里一定要避免使用es6的语法!!!!!!!!!
友情链接
甘肃档案专业继续教育培训平台 https://gsda.zgzjzj.com
新疆兵团第八师教育局专业技术人员培训平台 https://btbsjy.zgzjzj.com
甘肃兰州新区专业技术人员继续教育培训平台 https://lzxq.zgzjzj.com
甘肃白龙江林业保护中心专业技术人员继续教育培训平台 https://bljly.zgzjzj.com
甘肃武威市专业技术人员继续教育培训平台 https://wws.zgzjzj.com
甘肃甘南藏族自治州专业技术人员继续教育培训平台 https://gnz.zgzjzj.com
甘肃金昌市专业技术人员继续教育培训平台 https://jcs.zgzjzj.com
甘肃省专业技术人员继续教育培训平台 https://gs.zgzjzj.com
甘肃张掖市专业技术人员继续教育培训平台 https://zhangye.zgzjzj.com
甘肃省专业技术人员继续教育培训平台 https://gs.zgzjzj.com
甘肃省体育局继续教育培训平台 https://gsstyj.zgzjzj.com
盐城工学院 https://yancheng.zgzjzj.com
安徽省档案专业技术人员继续教育平台 https://ahda.zgzjzj.com
甘肃省自然资源行业专业技术人员继续教育平台 https://gszrzy.zgzjzj.com
甘肃省会计专业技术人员继续教育培训平台 https://gskuaiji.zgzjzj.com
兰州兰石集团专业技术人员继续教育培训平台 https://lzlsjt.zgzjzj.com
广西财政厅会计人员网络继续教育培训平台 https://gxczj.zgzjzj.com
安徽省应急管理(安全生产)培训平台 https://ahyjpx.zgzjzj.com
四川矿产机电技师学院培训平台 https://sckcjspx.zgzjzj.com
兰州市人事培训考试局 https://lzksj.zgzjzj.com
广西中小企业联合会培训中心 https://gxsme.zgzjzj.com