casperjs小结

官网http://casperjs.org/

分享http://download.csdn.net/detail/xiarendeniao/5781445


环境:

[dongsong@localhost ~]$ casperjs --version
1.0.0-RC2
[dongsong@localhost ~]$ phantomjs --version
1.7.0

1.casperjs按照start()、then*()、wait*()、open()等流程往下做导航(注意,如果有语法错误,比如少个分号神马的,可能运行时就无任何提示的卡那里了)
  run()方法触发该流程,run()方法可以指定一个onComplete()方法供导航完成时回调
  exit()/die()退出

2.打印html源码可以用debugHTML();打印web页面用debugPage();获取网页快照用capture('xx.png')

3.填写表单或者输入框用fill()做(要求文本框有name属性)或者在evaluate()内部做(evaluate相当于casperjs和网页之间的gate)

  提交表单可以通过fill直接完成(有些提交是js控制的,这种方法就行不通了)或者用click()点击提交按钮

  下面给一个在evaluate内部做的例子(跟普通页面上的js编程类似)

// Querying for "Chuck Norris" on Google
casper.start('http://google.fr/').thenEvaluate(function(term) {
    document.querySelector('input[name="q"]').setAttribute('value', term);
    document.querySelector('form[name="f"]').submit();
}, {
    term: 'Chuck Norris'
});

casper.run();

再给个填没有name属性的input框框的例子:

casper.waitForSelector('#xx', function then() {
			recognizedCode = 'xxx';
			this.evaluate(function(rtCode){
				document.querySelector('input[class="xxx"]').value = rtCode; //1>可以用这种方式填input框框(without name attribute)
				//document.querySelector('a[action-type="submit"]')[0].click(); //2>可以在这里提交哦!
				//__utils__.findOne('input[class="WB_iptxt oauth_form_input oauth_form_code"]').value = rtCode; //1>也可以这样填input框框(without name attribute)
			}, {rtCode:recognizedCode});
			this.click('a[action-type="submit"]'); //2>也可以在这里提交!
		},
		function onTimeout() {
			this.log('wait selector timeout', 'error')
		},
		timeout = 1000
	);
参考: https://groups.google.com/forum/#!msg/casperjs/iybL4kdLqVg/Exw8v2pcUXAJ

4.日志console.log();或者由casperjs对象调用log()/echo()方法
  创建capserjs对象时可以指定日志级别、日志详尽程度、定制事件回调函数(OnError/onResourceReceived/httpStatusHandlers/...):
 
var casper = require("casper").create({
        verbose: true,
        logLevel: "debug",
        onError: function(self,m){
                this.capture("error.png");
                console.log("FATAL:" + m);
                self.exit();
            }
    });

还可以指定casper对象整个导航过程总的运行时间timeout,这样可以防止在程序因为网络原因或程序bug导致一直不退出,对应timeout的处理函数是onTimeout

与整个导航过程timeout和onTimeout相对应的是单步超时时间stepTimeout和单步超时处理函数onStepTimeout

    
5.调试时免不了要打印变量或者对象的信息
require("utils").dump(xxx);

 
6.命令行参数由cli模块操作,详见 http://casperjs.org/cli.html
 
casperObj.cli.has(0)
casperObj.cli.get(3)
casperObj.cli.get('usename')

 
7.selector,跟css选择器规则一样(详见谷歌/度娘)
  #xx                 选取以xx为id的html标签
  xx[attr='value']    选取属性attr值为value的xx类型html标签
  .xx                选取以xx为class的html标签
 
8.事件,用casperObj.on()来注册事件的回调函数,详见 http://casperjs.org/events-filters.html
  要清除对某时间的监控和回调可以把回调函数设为空

  下面给个小例子

casper.on('resource.received', function(resource){
        if (resource.redirectURL) {
            var rUrl = resource.redirectURL;
            if (rUrl.match(/\?code=(\w+)/g)) {
                code = rUrl.substr(rUrl.search(/\?code=(\w+)/g)+6);
                this.log("code=" + code,'info');
                this.clear();
                this.on('resource.received', function(resource) {});
            } else
                return;
        } else
            return;
    });

9.单纯的把timeout设置得很长,而不设置对应的超时函数,那么设置的这个值是不起作用的,可能是casperjs的bug,实践多次证明过这个问题

   比如,使用casperjsObj.waitForSelector()等待某个选择器时想定制timeout那么要提供onTimeout函数才行


10.实践证明一个casperjs程序内部无法创建多个casperjs对象

     那么如果需要同时访问多个站点、且需要用一个站点的某些数据填写另一个站点某个form、且form页面在重新请求时不一样(比如验证码,用back()倒回去验证码就不一样了),怎么办呢?

     鸟人的解决方案是自己提供一个页面内嵌两个frame分别装载目标站点;这里又有个问题是casperjs对象不能直接获取iframe内部的标签,需要用casperjsObj.page.switchToChildFrame(0/1/2)进入iframe、用casperjsObj.page.switchToParentFrame()回到上级iframe或者全局的位置(需要注意的是这两个函数和其他普通js程序一样、只有在导航过程中的某个function内部才有效,否则会被casperjs忽视,casperjs只认start/open/thenOpen/run/then/wait*/each/..等导航,普通js程序需要在这些导航对应的function内部填充)

 

11.capserjsObj.captureSelector()会有误差,可以根据casperjsObj.getElementBounds()获取要拍照的选择器的边界然后人工修订后把边界值传入casperjsObj.capture()拍照


12.控制导航跳向某一步、查看有多少步,可以用label/goto/dumpSteps函数,函数实现和用法详见https://github.com/yotsumoto/casperjs-goto


13.循环控制,我们不可避免的需要重复做一些动作,把所有的URL放到一个数组里面用each函数来处理当然不错,但是很有局限性,如果要重复的动作不是打开页面而是点击某个按钮呢?

鸟人想到的解决办法是用waitFor()+Wait()来实现循环,结果不太理想,要嘛卡死直到timeout,要嘛飞快的死循环

//loop:隔一段时间就刷新一次简历(用waitFor和wait实现循环,貌似不靠谱儿!)
        /*
        casper.waitFor(function check(){
                        this.wait(10000,
                                function() {
                                        this.click('a[title="简历刷新"]');
                                        this.log('refreshed my resume');
                                }
                        );
                        return false;
                },
                function then() {},
                function onTimeout() {this.log('timeout: refresh loop competed.', 'error');},
                timeout = 120000
        );*/

网上寻来的解决方案是递归调用,casperjsObj调用run函数时可以传入一个结束时执行的函数,在这个函数里面可以加入【我们的循环体】和【递归的run调用】

参考:https://github.com/n1k0/casperjs/blob/master/samples/dynamic.js

function refresh()
{
        this.wait(10000,
                function() {
                        this.click('a[title="简历刷新"]');
                        this.log('refreshed my resume');
                }
        );
        this.run(refresh);
}
casper.run(refresh);

14.日志中出现”Unsafe JavaScript attempt to access frame with URL”的info信息,启动casperjs时候加上参数--web-security=false(allow cross-domain XHR)即可(参见http://stackoverflow.com/questions/18960791/cant-run-a-basic-test-with-casperjs)

要治本的话,参见http://stackoverflow.com/questions/4324108/unsafe-javascript-attempt-to-access-frame-with-url,who care~

你可能感兴趣的:(业务-网络爬虫,业务-信息检索)