PhantomJs+MutationObserver实现动态页面数据抓取

时间:2021-08-19 09:14:48

       IT行业,支撑业务的变化需要优秀的大量的数据,我们需要适应数据的动态变化,拿到这些动态变化的数据,分析,然后提供给自己的项目,支撑公司的业务。最近,就碰到这种,需要获取网页上不断变化的数据,只有在数据发生变化的时候,才取这个变化的值,并将其存放到库中。

       其实PhantomJs,乍看这个名字,还以为是什么Js,其实吧,它就是一个没有页面的浏览器,它跟其他浏览器的最大的区别就在于它没有界面,内核是用WebKit,这是PhantomJs的官网http://phantomjs.org/ 。PhantomJs的中文资料很少,介绍的也很简单,基本上都是官网的示例,说是在的,这些对我一点都没有帮助,不过,人要学会变通,也就只能将这些简单的进行拼装组合,支撑自己复杂的业务。PhantomJs仅仅只是一个浏览器而已,并不能在数据变化的时候主动告诉我,这是变化的数据。所以,这还需要MutationObserver的支持。

       MutationObserver,乍看名字,也许你就能想到他的实现原理是什么,在我看来,就是个观察者模式,当然,JS具体的实现细节,我不知道,我也没查过。其实计算机很多东西都是相通的,从名字看,也许就能看到他的作用或者能大体猜测到他的实现原理上。

安装

       PhantomJs的使用,需要先安装,详细可以参见官网,官网介绍的很详细,就不累述了。我在使用的时候用到了PhantomJs的以下方法。


使用方法简介

       当然是我使用过的方法的一个简介,其他的,还请移步官网。

       page.onConsoleMessage      监听所有的console.log消息

    page.onConsoleMessage = function(msg){
console.log(msg);
};
      

      page.onLoadFinished       界面加载完成之后,进行页面动态数据抓取

    page.onLoadFinished = function(status){
console.log('---------start-----------');
page.evaluate(getContent,"test");
};


      page.open    界面打开,我用到了它的两个参数

   page.open(url, function (status) {   
//Page is loaded!
if (status !== 'success') {
console.log('can not start');
} else {
}
});
   

     page.evaluate()         支持js操作

   page.evaluate(getContent,postUrl);
     getContent为js的方法名,postUrl为该方法需要传递的参数

     

      剩下的就是MutationObserver对动态数据变化的监听了。

    function getContent(url) {
console.log("---------start fetch------------"+url+"---");
var tar = $('#MarketGrid');
var MutationObserver = window.MutationObserver|| window.WebKitMutationObserver|| window.MozMutationObserver;
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var text=$(mutation.target).parents(".ipe-Market").find(".ipe-Market_ButtonText").text();
console.log(text);
})
});
observer.observe(tar[0], {
attributes: true,
childList: true,
characterData: true,
characterDataOldValue: true,
attributeOldValue:true,
subtree: true});
}


完整的Js为:   

system = require('system')     
address = system.args[1];//获得命令行第二个参数 接下来会用到
var page = require('webpage').create();
var url = address;

page.onConsoleMessage = function(msg){
console.log(msg);
};

page.onLoadFinished = function(status){
console.log('---------start-----------');
var postUrl=getUrl();
page.evaluate(getContent,"test");
};

page.open(url, function (status) {
//Page is loaded!
if (status !== 'success') {
console.log('can not start');
} else {
}
});
function getContent(txt) {
console.log("---------start fetch------------"+txt+"---");
var tar = $('#MarketGrid');
var MutationObserver = window.MutationObserver|| window.WebKitMutationObserver|| window.MozMutationObserver;
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
var text=$(mutation.target).parents(".ipe-Market").find(".ipe-Market_ButtonText").text();
console.log(text);
})
});
observer.observe(tar[0], {
attributes: true,
childList: true,
characterData: true,
characterDataOldValue: true,
attributeOldValue:true,
subtree: true}
);
}


运行

       我安装的是Windows版本的PhantomJs,运行的时候,需要进入到对应的bin目录下,然后使用命令格式为:phantomjs  xxx.js  http地址  。


问题及解决方案

       说说我在这中间遇到的一些问题吧。

        第一个问题:实时监测动态数据变化

        刚开始,我并不理解PhantomJs是个什么东西,其实我就是不信这就是一个浏览器,它跟其他浏览器的区别就是没有界面,其他浏览器功能,PhantomJs基本上都有,所以在当MutationObserver在其他浏览器产生作用的时候,当和PhantomJs结合的时候,却不能产生相应的效果的时候,我一度怀疑,这个利用这两个东西动态抓取数据的功能不能实现,原因是在 page.evaluate()中执行MutationObserver的时候,这个里面的页面是死页面,数据根本就不会变化。

        老大否定了我,也是后来老大通过他的手段发现,当运行到page.evaluate()时候,这个时候页面还不存在,还没有那些可能会发生变化的html元素。老大就是老大啊,不得不佩服。

       第二个问题:PhantomJs内存飙升,CPU占用高

       这个问题的原因,在于PhantomJs在运行过程中,因为page.evaluate()中的js方法的问题,导致PhantomJs占用内存越来越大,最终在达到1.5G左右,自动关闭退出。解决办法我就不用说了吧,调试js就好了,找到导致原因的那段js就可以了。

      第三个问题:看到数据动态变化了,但是并没有获取到动态变化的数值

      这个问题的原因其实跟第一个问题有点类似,为什么类似呢?因为我遇到的这两个问题,都跟具体的http请求页面有关,这个问题的出现,是因为里面的html元素的原因,在检测到变化后,并没有拿到值,这个问题隐藏的很深,如果不细致观察很难发现。

       第四个问题:因为PhantomJs启动后,是一个进程,那检测到的数据变化值如何传入到项目中

       解决办法类似的有两个,第一:Ajax请求到项目服务;第二:长期挂起线程,并保持一个活跃状态,或者使用main函数。

       第五个问题:管理问题

       当前,这还涉及到另外的一些问题,如果利用不使用其他额外的工具封装类的话,将面临启动一个数据抓取服务,将启动一个PhantomJs的进程,启动10个的话,将启动10个PhantomJs进程,什么时候启动,什么时候删除PhantomJs的进程,这些都是问题。一个进程大约占用内存50m,如果是10个,就是500m,但是,你能手动去启动,手动去删除吗?显然这是不合理的,至于如何解决,以后告知。


总结

       利用PhantomJs和MutationObserver实现动态网页数据抓取工作,光是调通这个PhantomJs和MutationObserver,用了差不多一周的时间,不断的改造,当然这不是最终版本,其实最终版本比这稍微复杂。PhantomJs的作用还有很多,在数据抓取这个行业中,我想,PhantomJs以后会大有前途的。其实,PhantomJs也有对应的封装工具—PhantomJsDriver,只是我查了下这个封装工具类的API,并没有适合我们这种特殊需求的,所以也就没有深入研究,PhantomJsDriver的中文资料貌似也很少。

       今天老大还跟我说,为什么叫“饭桶”呢?我默默的笑了,也许它在运行上的确很饭桶吧,哈哈。。。。。

       在正式步入工作这个环境,进入互联网这个行业,特别是在最近的一年,我深刻体会到,如果不留心思考,不尝试阅读英文资料,对英文深深抵触,那我就别想在这个行业继续混下去了。处处留心,学着怎么去做一个人,我常常对自己说的话就是,不管在工作还是在生活中,静下心来,身为一个女生,更要理智多于感性,看看自己能够成为一个什么样的人。