前言
单页应用的痛点以及什么原因导致的我不多说了 网上资料很多 能搜索到这篇文章 我想你也经历了诸多痛苦和无奈
注: 由于前端后分离 不同公司采用不同的语言组合 请根据本文配置 自行推倒相应配置
本司采用 SpringMVC + Vuejs1.x(可以忽略 SEO基本于该架构无关)
服务器环境: Oneinstack 本文仅作演示 只安装了其中的Tengine(Nginx)作为WEB容器
本文仅以Ubuntu14.04 LTS
系统作为演示 (阿里云已经成功了 自用+商用都成功了 但是没有多余的资源 贡献了我许久未用的学生优惠服务器[doge] 大三狗强行秀即将逝去的优势)
请其他服务器在安装相应依赖或者软件的时候注意替换相应命令
用到的服务
服务器配置
(若已经安装过 均可跳过)
// 刚装完系统 先升级下旧包 觉得每次都要输sudo麻烦的 先执行 sudo su 保证以下操作不会出现因为权限到导致的一些莫名奇妙的问题sudo apt-get update
安装git
sudo apt-get install git
安装nodejs
nodejs各大平台官方安装指南 下列是Ubuntu安装 nodejs 6.x版本步骤
sudo apt-get install curl // 请先确认服务器是否安装了curl 如果已经安装跳过即可curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -sudo apt-get install -y nodejssudo apt-get install -y build-essential// 如果不受 墙 影响可以忽略这步 并将下方cnpm换成npm即可sudo npm install -g cnpm --registry=https://registry.npm.taobao.org
Clone prerender rep
git clone https://github.com/prerender/prerender
Why cnpm?
prerender需要用到phantomjs当你在墙内下载的时候 你会感受到来自 墙 的满满的恶意 而每当这时候淘宝镜像源仿佛救世主一般降临
执行npm install
cd prerendercnpm install
执行node server.js
node server.js
如果遇到以下错误(后面两行提示的)
ubuntu@VM-0-110-ubuntu:~/prerender$ node server.js2016-12-31T19:57:42.231Z starting worker thread #02016-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
// ubuntusudo apt-get install libfontconfig// centosyum install libXext libXrender fontconfig libfontconfig.so.1
直到看到了类似下面的提示(不同配置会开启不同数量的的进程 所以可能看到多行starting phantom…) 才算开启成功
ubuntu@VM-0-110-ubuntu:~/prerender$ node server.js2016-12-31T20:00:55.655Z starting worker thread #02016-12-31T20:00:55.865Z starting phantom...2016-12-31T20:00:55.873Z Server running on port 30002016-12-31T20:00:56.491Z started phantom
测试服务是否安装成功
- 打开
你的服务器外网IP:3000/http://www.deboy.cn
查看shell
控制台是否有进入日志 如有有说明成功了(不用管网页样式表加没加载 待会儿会讲)
服务器环境安装 (以下环境安装尽量做到与实际项目部署一致, 但是由于只是项目演示 会缩减掉部分旁枝末节)
这部分建议可以不随本文这样做 因为每个公司的架构都不一样 环境也是不一定一样 我这里只演示nginx中间件的使用 其他的中间件 点这里:prerender-install-middleware
- 按照Oneinstack官方安装文档安装好你需要的任意组合的环境(作为演示 我不安装其他环境 单独安装nginx 因为全套安装下来时间比较长)
注意 安装过程中 当问到是否安装pureftpd
服务的时候选择y
等下会用到这个创建FTP帐号 - 参考官方的 如何删除虚拟主机? 和 如何管理FTP账号? 分别新建一个域名对应的主机 和 一个默认的FTP帐号
- 通过FTP帐号使用xftp或者任意FTP工具链接并上传一个简单的页面 确保域名解析正常后 开始改造你的项目代码
改造项目代码(不想手动创建直接到这一步最后一条Clone我的github demo)
我将使用一个简单的 vue-cli生成的模版 集成了
Vue
+vue-router
+vue-resource
)的项目来演示动态改变标题和内容
-
通过vue-cli新建一个项目 不详细讲 请自行查看vue-cli文档 我们这里初始化的模版是1.0的 2.0的已经默认是vue2.0了
vue init webpack#1.0 vue-prerender-demo -
修改(config/inex.js)
build
的assetsPublicPath
配置 修改为以下格式 (此步是为了让页面在抓取的时候 不因为css资源路径的问题导致页面排版错乱 也为了最高程度还原网页内容)
原本: /
改成: youdomain.com/ -
修改vue-router beforeEach 和 afterEach 原因
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请求完再抓内容(国内然并卵)
<meta name="fragment" content="!"> -
Github: vue-prerender-demo
修改站点对应Nginx(Tengine)配置文件(使用nginx中间件)
server { listen 80; server_name youdomain.com; access_log /data/wwwlogs/youdomain.com_nginx.log combined; index index.html index.htm index.php; include /usr/local/tengine/conf/rewrite/typecho.conf; root /data/wwwroot/youdomain.com; #error_page 404 = /404.html; #error_page 502 = /502.html; 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; 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
service nginx restart
让node脚本不随终端断开而断开
sudo cnpm install forever -gforever start server.js
检验成果 官方
原地址: vue-prerender.deboy.cn/#!/titleFro…
可抓取地址: vue-prerender.deboy.cn/?_escapedfragment=/titleFromRouterDefine
百度的缓存更新得太慢了 抓的IP是腾讯云的 我已经换阿里云了 可还是不行 只能换一个平台来抓取了 下面的Hello World!渲染出来 说明已经是成功了 访问上面那个页面 得到的应该是没有异步请求结果
下图是我wget得到的结果 为什么要把地址中#!
换成 ?_escaped_fragment_=
你们看nginx配置就清楚了 同样百度爬虫来了 和这个是一样的效果
原理
- 判断是否爬虫以及一些自定义调试条件
- 服务端prerender预渲染(使用phantomjs在服务器上跑了一遍)并缓存文件 发送给爬虫
- 页面抓取完成