phantomJS+nodeJS+nginx完美解决前后端分离SEO问题

来源:互联网 发布:淘宝产品摄影报价 编辑:程序博客网 时间:2024/05/27 12:21
此文至项目已经采用前后端分离,但遇到SEO问题的工程师们。

写在前面:
公司网站属于信息类网站,在项目立项的时候想降低前后端开发的耦合性,于是就采用了前后端分离的做法,这种方式在开发期间确实便捷了不少,前端负责界面和数据渲染,后台负责API接口开发和文档编写,一切都来得那么的有序。但是当运营部开始投百度广告的时候问题来了,百度的spider只会爬取页面数据,不会爬取执行JS后的页面数据,问题已经出现,不可能叫程序猿们重新撸一次代码吧,不管是从人力物力财力角度出发,公司都不会同意这样的事情发生,无限头大中。。。。

正文:
为了解决问题,就必须先要知道问题出在哪里?我们先来看看前后端分离spider工作流程,JS并不会执行。
前后端分离spider工作流程
于是我想到是否能在spider和web服务器之间加一个自己的spider帮助其他spider抓取执行JS后的页面数据。
这里写图片描述
朝着这个思路,的确找到存在这样一种spider程序可以抓取执行JS后的页面数据,phantomJS下载地址:http://npm.taobao.org/dist/phantomjs/,入门教程:http://javascript.ruanyifeng.com/tool/phantomjs.html,phantomJS是一个类似于nodeJS的程序,执行JS代码,下面是抓取指定数据的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();page.settings.loadImages = false;  //为了提升加载速度,不加载图片  // NodeJS 系统模块var system = require('system');// 从CLI中获取第二个参数为目标URLvar 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(requestData, request) {    resourceCount++;    clearTimeout(resourceWaitTimer);    //过滤页面不想加载的链接,我这里是过滤了我页面的百度统计代码和cnzz统计代码,你可以自定义自己的过滤规则(正则表达式)    var _url = requestData['url'];    if ((/.+?(baidu|cnzz).+?/gi).test(_url)){        //console.log('Skipping:'+requestData['url']+'------------------------------------------------------------------------------');        request.abort();    }else{        //console.log('NoSkipping:'+requestData['url']+'-----------------------------------------------------------------------------------------');    }};// 资源加载完毕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('https://www.yuejia.me', function (status) {    if (status !== 'success') {        phantom.exit(1);    } else {        // 当改页面的初始html返回成功后,开启定时器        // 当到达最大时间(默认5秒)的时候,截取那一时刻渲染出来的html        maxWaitTimer = setTimeout(function(){            capture(2);        }, maxWait);    }});
然后执行js,怎么执行我就不想详细说了,作为程序猿的基础知识还是要了解,实在不知道的在后面留言,我详细回复你。

这里写图片描述
执行后就会得到执行了JS的页面数据了,然后我们需要把phantomjs的数据返回给代理服务,在了解后发现,nodejs可以直接调用phantomJS,当然你也可以用Java执行命令的方式来调用phantomJS,这里就用nodejs来做为phantomJS的中转件了,nodeJS下载地址:http://nodejs.cn/download/
下面是nodeJS作为服务器,监听8081,端口传递请求url到phantomJS抓取数据的JS,其中.replace(‘baiduPC[E]-QUYU’,”)是我在测试的时候发现不知道为什么如果后面链接加了这个玩意儿,phantomJS就会爬取失败,所以我加把它替换了,如果知道的朋友麻烦留言说下,谢谢啦。

// 引入NodeJS的子进程模块var child_process = require('child_process');var http = require("http");http.createServer(onRequest).listen(8081);function onRequest(req, res) {   // 完整URL    var url = req.originalUrl.replace('baiduPC[E]-QUYU','');    console.log("url:"+url)    // 预渲染后的页面字符串容器    var content = '';    // 开启一个phantomjs子进程    var phantom = child_process.spawn('phantomjs.exe', ['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('load error');                res.write('加载失败');                break;            case 2:                console.log('timeout: '+ url);                res.write(content);                break;            default:                res.write(content);                break;        }         res.end();    });}

到这里我们的nodeJS+phantomJS的配置就已经完成了,不过记得把phantomJS page.open(‘https://www.yuejia.me‘, function (status){…中的’https://www.yuejia.me‘替换成参数url,及page.open(url, function (status){…让需要抓取的页面从nodeJS传入,接下来就是需要把爬虫的请求转发到我们的nodeJS了,我这里采用了主流的Nginx来做
以下是nginx监听请求是否爬虫请求,是就转发到nodeJS去,不是就去取我们正常的页面数据。

location / {            proxy_set_header  Host            $host:$proxy_port;            proxy_set_header  X-Real-IP       $remote_addr;            proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;            #爬虫特殊处理                 if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot"){                 proxy_pass   http://spider_server;            }            root   c:/webapps/website;            index  index.html index.htm;       }

结束语:
前后端分离确实是在开发的时候给我们带来便捷,不过在API安全方面 SEO方面还有些欠缺或者说存在问题。这个对SEO检索出的方案个人觉得效果不是最好的,有时候会加载超时或者加载错误等等,不过这也是目前来说觉得最好的解决方案之一了,如果有更好的方案,希望大家留言,谢谢啦,关于前后端分离API通信安全问题我个人采用的是JWT验证的方式,感兴趣的朋友可以去看看我另外一篇文章,前后端问题API安全校验之JWT

原创粉丝点击