├── .gitignore ├── README.md ├── package.json ├── server.js └── spider.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | db.json 4 | *.log 5 | node_modules/ 6 | public/ 7 | .deploy*/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue seo phantomjs方案 2 | 3 | ## 安装 4 | 5 | 本地phantomjs安装:http://npm.taobao.org/dist/phantomjs/ 6 | 7 | 配置环境变量,以windows为例,如你放在`D:\software\phantomjs-2.1.1-windows`目录下, 8 | 设置环境变量:控制面板->系统和安全->系统->高级系统设置->高级->系统变量->编辑变量Path->将`D:\software\phantomjs-2.1.1-windows\bin`添加到最末端即可(cmd命令要重新打开窗口才生效)。 9 | 10 | 执行 11 | ``` 12 | $ phantomjs -v 13 | ``` 14 | 输出版本号说明成功了。 15 | 16 | 17 | ``` 18 | # 克隆项目到本地 19 | $ git clone https://github.com/lengziyu/vue-seo-phantomjs.git 20 | 21 | # 安装express 22 | $ cnpm i 23 | ``` 24 | 测试是否可以: 25 | ``` 26 | $ phantomjs spider.js 'https://www.baidu.com/' 27 | ``` 28 | 打印出一堆html代码就说明成功了。 29 | 30 | # 线上部署 31 | 请先安装PM2、phantomjs、nodejs,并配置全局环境变量。 32 | ``` 33 | # 运行 34 | PM2 start server.js 35 | ``` 36 | nginx配置: 37 | ``` 38 | upstream spider_server { 39 | server localhost:8081; 40 | } 41 | 42 | server { 43 | listen 80; 44 | server_name example.com; 45 | 46 | location / { 47 | proxy_set_header Host $host:$proxy_port; 48 | proxy_set_header X-Real-IP $remote_addr; 49 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 50 | 51 | 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") { 52 | proxy_pass http://spider_server; 53 | } 54 | } 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "express": "^4.17.1" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | // ExpressJS调用方式 3 | var express = require('express'); 4 | // var phantomjs = require('phantom'); 5 | var app = express(); 6 | var path = process.cwd(); 7 | // 引入NodeJS的子进程模块 8 | var child_process = require('child_process'); 9 | app.get('*', function(req, res){ 10 |     // 部署到服务器的完整URL 11 |     var url = req.protocol + '://'+ req.hostname + req.originalUrl; 12 | 13 | // 测试的url 14 | // var url = 'https://www.baidu.com' 15 | 16 |     // 预渲染后的页面字符串容器 17 |     var content = ''; 18 |     // 开启一个phantomjs子进程 19 |     var phantom = child_process.spawn('phantomjs', [path + '/spider.js', url]); 20 |     // 设置stdout字符编码 21 |     phantom.stdout.setEncoding('utf8'); 22 |     // 监听phantomjs的stdout,并拼接起来 23 |     phantom.stdout.on('data', function(data){ 24 |         content += data.toString(); 25 |     }); 26 |     // 监听子进程退出事件 27 |     phantom.on('exit', function(code){ 28 | console.log("content是:") 29 | console.log(content) 30 |         switch (code){ 31 |             case 1: 32 |                 console.log('加载失败'); 33 |                 res.send('加载失败'); 34 |                 break; 35 |             case 2: 36 |                 console.log('加载超时: '+ url); 37 |                 res.send(content); 38 |                 break; 39 |             default: 40 |                 console.log('加载页面: '+ url); 41 |                 res.send(content); 42 |                 break; 43 |         } 44 |     }); 45 | }); 46 | app.listen(8081, function () { 47 |   console.log('Spider app listening on port 8081!'); 48 |   console.log(path + '/spider.js') 49 | }); 50 | -------------------------------------------------------------------------------- /spider.js: -------------------------------------------------------------------------------- 1 | /*global phantom*/ 2 | "use strict"; 3 | // 单个资源等待时间,避免资源加载后还需要加载其他资源 4 | var resourceWait = 500; 5 | var resourceWaitTimer; 6 | // 最大等待时间 7 | var maxWait = 1000; 8 | var maxWaitTimer; 9 | // 资源计数 10 | var resourceCount = 0; 11 | // PhantomJS WebPage模块 12 | var page = require('webpage').create(); 13 | // NodeJS 系统模块 14 | var system = require('system'); 15 | // 从CLI中获取第二个参数为目标URL 16 | var url = system.args[1]; 17 | // 设置PhantomJS视窗大小 18 | page.viewportSize = { 19 |     width: 1280, 20 |     height: 1014 21 | }; 22 | // 获取镜像 23 | var capture = function(errCode){ 24 |     // 外部通过stdout获取页面内容 25 | 26 |     console.log(page.content); 27 |     // 清除计时器 28 |     clearTimeout(maxWaitTimer); 29 |     // 任务完成,正常退出 30 |     phantom.exit(errCode); 31 | }; 32 | // 资源请求并计数 33 | page.onResourceRequested = function(req){ 34 |     resourceCount++; 35 |     clearTimeout(resourceWaitTimer); 36 | }; 37 | // 资源加载完毕 38 | page.onResourceReceived = function (res) { 39 |     // chunk模式的HTTP回包,会多次触发resourceReceived事件,需要判断资源是否已经end 40 |     if (res.stage !== 'end'){ 41 |         return; 42 |     } 43 |     resourceCount--; 44 |     if (resourceCount === 0){ 45 |         // 当页面中全部资源都加载完毕后,截取当前渲染出来的html 46 |         // 由于onResourceReceived在资源加载完毕就立即被调用了,我们需要给一些时间让JS跑解析任务 47 |         // 这里默认预留500毫秒 48 |         resourceWaitTimer = setTimeout(capture, resourceWait); 49 |     } 50 | }; 51 | // 资源加载超时 52 | page.onResourceTimeout = function(req){ 53 |     resouceCount--; 54 | }; 55 | // 资源加载失败 56 | page.onResourceError = function(err){ 57 |     resourceCount--; 58 | }; 59 | // 打开页面 60 | page.open(url, function (status) { 61 |     if (status !== 'success') { 62 |         phantom.exit(1); 63 |     } else { 64 |         // 当改页面的初始html返回成功后,开启定时器 65 |         // 当到达最大时间(默认5秒)的时候,截取那一时刻渲染出来的html 66 |         maxWaitTimer = setTimeout(function(){ 67 |             capture(2); 68 |         }, maxWait); 69 |     } 70 | }); 71 | --------------------------------------------------------------------------------