Prerender + vuejs SEO最佳实践 百度爬虫 + 谷歌收录 亲测成功

文章 4月1号 有更新 请注意使用最新的方案

前言

单页应用的痛点以及什么原因导致的我不多说了 网上资料很多 能搜索到这篇文章 我想你也经历了诸多痛苦和无奈
注: 由于前端后分离 不同公司采用不同的语言组合 请根据本文配置 自行推倒相应配置
本司采用 SpringMVC + Vuejs1.x(可以忽略 SEO基本于该架构无关)
服务器环境: Oneinstack 本文仅作演示 只安装了其中的Tengine(Nginx)作为WEB容器
本文仅以Ubuntu14.04 LTS系统作为演示 (阿里云已经成功了 自用+商用都成功了 但是没有多余的资源 贡献了我许久未用的学生优惠服务器[doge] 大三狗强行秀即将逝去的优势)
请其他服务器在安装相应依赖或者软件的时候注意替换相应命令

用到的服务

服务器软件安装

(若已经安装过 均可跳过)

1
2
// 刚装完系统 先升级下旧包 觉得每次都要输sudo麻烦的 先执行 sudo su 保证以下操作不会出现因为权限到导致的一些莫名奇妙的问题
sudo apt-get update

安装git

1
sudo apt-get install git

安装nodejs nodejs各大平台官方安装指南 下列是Ubuntu安装 nodejs 6.x版本步骤

1
2
3
4
5
6
sudo apt-get install curl // 请先确认服务器是否安装了curl 如果已经安装跳过即可
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt-get install -y build-essential
// 如果不受 墙 影响可以忽略这步 并将下方cnpm换成npm即可
sudo npm install -g cnpm --registry=https://registry.npm.taobao.org

Why cnpm?

prerender需要用到phantomjs当你在墙内下载的时候 你会感受到来自 墙 满满的恶意 而每当这时候淘宝镜像源仿佛救世主一般降临

Clone prerender rep

1
git clone https://github.com/prerender/prerender

npm install

1
2
cd prerender
cnpm install

after-cnpm-install

执行node server.js

1
node server.js

如果遇到以下错误(后面两行提示的)

1
2
3
4
5
6
ubuntu@VM-0-110-ubuntu:~/prerender$ node server.js
2016-12-31T19:57:42.231Z starting worker thread #0
2016-12-31T19:57:42.452Z starting phantom...
2016-12-31T19:57:42.460Z Server running on port 3000
/home/ubuntu/prerender/node_modules/.2.1.14@phantomjs-prebuilt/lib/phantom/bin/phantomjs: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory
(node:10992) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: /home/ubuntu/prerender/node_modules/.2.1.14@phantomjs-prebuilt/lib/phantom/bin/phantomjs: error while loading shared libraries: libfontconfig.so.1: cannot open shared object file: No such file or directory

修复方案

1
2
3
4
5
// ubuntu
sudo apt-get install libfontconfig
// centos
yum install libXext libXrender fontconfig libfontconfig.so.1

直到看到了类似下面的提示(不同配置会开启不同数量的的进程 所以可能看到多行starting phantom…) 才算开启成功

1
2
3
4
5
ubuntu@VM-0-110-ubuntu:~/prerender$ node server.js
2016-12-31T20:00:55.655Z starting worker thread #0
2016-12-31T20:00:55.865Z starting phantom...
2016-12-31T20:00:55.873Z Server running on port 3000
2016-12-31T20:00:56.491Z started phantom

测试服务是否安装成功

  • 打开你的服务器外网IP:3000/http://www.deboy.cn 查看shell控制台是否有进入日志 如有有说明成功了(不用管网页样式表加没加载 待会儿会讲)

    服务器环境安装 (以下环境安装尽量做到与实际项目部署一致, 但是由于只是项目演示 会缩减掉部分旁枝末节) 安装完成后是nginx处理静态文件 并负责接口转发到PHP或者java的

    这部分建议可以不随本文这样做 因为每个公司的架构都不一样 环境也是不一定一样 我这里只演示nginx中间件的使用 其他的中间件 点这里:prerender-install-middleware

  • 按照Oneinstack官方安装文档安装好你需要的任意组合的环境(作为演示 我不安装其他环境 单独安装nginx 因为全套安装下来时间比较长)
    注意 安装过程中 当问到是否安装pureftpd服务的时候选择y等下会用到这个创建FTP帐号

  • 参考官方的 如何删除虚拟主机?如何管理FTP账号? 分别新建一个域名对应的主机 和 一个默认的FTP帐号
  • 通过FTP帐号使用xftp或者任意FTP工具链接并上传一个简单的页面 确保域名解析正常后 开始改造你的项目代码 具体的环境说明 在oneinstack官网有

改造项目代码(不想手动创建直接到这一步最后一条Clone我的github demo)

我将使用一个简单的 vue-cli生成的模版 集成了Vue + vue-router + vue-resource)的项目来演示动态改变标题和内容

  • 通过vue-cli新建一个项目 我们这里初始化的模版是1.0的 当然 如果vue2.0你不想用ssr方案 也可以使用本文的方案 只需将部分修改推倒过去即可

    1
    vue init webpack#1.0 vue-prerender-demo
  • 修改(config/index.js)buildassetsPublicPath配置 修改为以下格式 (此步是为了让页面在抓取的时候 不因为css资源路径的问题导致页面排版错乱 也为了最高程度还原网页内容)

    原本: /

    改成: http://youdomain.com/ 或者你静态文件服务器的地址

  • 修改vue-router beforeEach 和 afterEach 原因戳我

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 这两个设置 要和这里一样
    var router = new VueRouter({history: true, hashbang: false})
    router.beforeEach(function () {
    window.prerenderReady = false
    })
    router.afterEach(function (transition) {
    // 这一步不是必须的 在那里修改标题由项目决定 但不要延迟更改
    window.document.title = transition.to.title || 'Default page title'
    window.prerenderReady = true
    })
  • 给index.html 加meta标签 告诉爬虫我这是ajax页面 你丫无论如何也得等我js请求完再抓内容(国内然并卵 谷歌会识别)

    1
    <meta name="fragment" content="!">
  • Github: vue-prerender-demo

修改站点对应Nginx(Tengine)配置文件

请注意将这个配置中的某些参数换成你们自己项目/环境对应的

使用nginx中间件 我这里添加了针对Vuejs history模式下转发的代码 可以直接用我的(官方版本通常是在我修改的上一行被注释)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
server {
listen 80;
server_name youdomain.com;
access_log /data/wwwlogs/youdomain.com_nginx.log combined;
index index.html index.htm index.php index.jsp;
root /data/wwwroot/youdomain.com;
location / {
try_files $uri @prerender;
}
location @prerender {
#proxy_set_header X-Prerender-Token YOUR_TOKEN;
set $prerender 0;
if ($http_user_agent ~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator") {
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) {
#setting prerender as a variable forces DNS resolution since nginx caches IPs and doesnt play well with load balancing
set $prerender "127.0.0.1:3000";
#rewrite .* /$scheme://$host$request_uri? break;
rewrite ^(.+)$ /$scheme://$host$request_uri? break;
proxy_pass http://$prerender;
}
if ($prerender = 0) {
rewrite ^(.+)$ /index.html break;
}
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 7d;
access_log off;
}
location ~ /\.ht {
deny all;
}
// 接口转发 这里只是模拟 假设用的是java 那么端口就是转发到8080 只转发 有项目包名的哪一个 把/projectName改掉即可
location /projectName {
proxy_pass http://127.0.0.1:8080;
include proxy.conf;
}
}
  • 修改完成 重启Nginx
    1
    service nginx restart

使用pm2让进程保持运行

1
2
3
4
5
6
7
cnpm install pm2 -g
// 开机自启
pm2 startup
// 启动项目 先cd到刚才clone的prerender项目根目录
pm2 start server.js
// 保存进程 重启会自动启动
pm2 save

本地测试结果

检验成果 Prerender官方说明

原地址: http://vue-prerender.deboy.cn/titleFromRouterDefine

可抓取地址: http://vue-prerender.deboy.cn/titleFromRouterDefine?_escapedfragment=
成功的标志就是 打开该页面 F12 看network选项卡 是不会有ajax请求的 但是网页的标题已经换了 说明是渲染之后的静态页面 纯粹是SEO功能
访问上面那个页面 得到的应该是没有异步请求的纯静态页面(异步请求都在服务端完成了)

google收录(2017-1-3更新)

百度站长平台爬取结果(2017-1-2更新)

Chinaz抓取结果

下图是我wget得到的结果 加 ?_escaped_fragment_= 是为了主动触发prerender渲染服务
你们看Nginx配置就清楚了 同样百度爬虫来了 和这个是一样的效果

另外附上几个可以模拟爬虫的工具

原理

  • 判断是否爬虫以及一些自定义调试条件
  • 服务端prerender预渲染(使用phantomjs在服务器上跑了一遍)并缓存文件 发送给爬虫
  • 页面抓取完成

后记

  • 使用该服务爬取的页面 所有js都会失效 即 只能爬取该页面内容 SEO足矣
  • 添加百度官方推荐的一种推送方式
    在百度站长平台中的工具-网页抓取-链接提交-自动提交-自动推送栏目可以找到
    只要加到index.html或者放到任意全局js文件中即可
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    (function(){
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
    bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
    }
    else {
    bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
    })();
坚持原创技术分享,您的支持将鼓励我继续创作!