简单试了一下 CasperJS ,基于PhantomJS / SlimerJS 引擎。

可以不搞图形窗口出来就网页截图,非常方便,值得一用。

示例集合:tests/suites/casper

写个小demo练手,把指定歌曲批量加入百度音乐收藏:baidu_music

Windows下安装过程

casperjs

在http://casperjs.org/下载zip文档,解压,假设目标目录为 d:\dev\casperjs

d:\dev\casperjs\bin加入PATH环境变量

phantomjs

在http://phantomjs.org/下载zip文档,解压,假设目标目录为 d:\dev\phantomjs

d:\dev\phantomjs加入PATH环境变量

slimerjs

在http://slimerjs.org/下载zip文档(standalone版本),解压,假设目标目录为 d:\dev\slimerjs

d:\dev\slimerjs加入PATH环境变量

新增一个环境变量SLIMERDIR,取值为d:\dev\slimerjs

新增-个环境变量SLIMERJSLAUNCHER,取值为d:\dev\slimerjs\xulrunner\xulrunner.exe

如果提示Couldn’t read application.ini,参考:Making casperjs work with slimerjs on Windows

把 d:\dev\slimerjs\slimerjs.bat 中的 SET SLIMERDIR=%~dp0 改成

if not exist %SLIMERDIR% (
SET SLIMERDIR=%~dp0
)

目前发现的问题

网页内容比较长的时候,windows下有时会crash掉

casperjs取https内容,或 post到https的表单失败

windows下参考issue-49

可以试试casperjs --ignore-ssl-errors=yes code.jscasperjs --ssl-protocol=any code.js

以登录百度为例

//初始化
//打log
//var casper = require('casper').create({logLevel: 'debug', verbose: true});

//不下载图片,不下载flash等等,可加速网页载入
var casper = require('casper').create({
  pageSettings: {
        loadImages: false, // do not load images
        loadPlugins: false // do not load NPAPI plugins (Flash, Silverlight, ...)
    }
});

//指定user-agent
casper.userAgent('Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:18.0) Gecko/20130119 Firefox/18.0');

//文件处理
var fs = require('fs');

//命令行参数
var usr = casper.cli.get(0);
var passwd = casper.cli.get(1);
var cookie_file = casper.cli.get(2);

//控制台输出
console.log("login baidu" );

//打开一个页面,有时要wait 1s再点击,不然有些js没load完,点击不生效
var login_url = 'https://passport.baidu.com/v2/?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2F';

casper.start(login_url);
casper.wait(1000, function(){
    this.click('#pass-user-login');
});


//提交一个表单
casper.thenEvaluate(function(usr,passwd) {
    document.querySelector('#TANGRAM__3__userName').setAttribute('value', usr);
    document.querySelector('#TANGRAM__3__password').setAttribute('value', passwd);
}, { 'usr' : usr, 'passwd' : passwd });

casper.wait(1000, function () {
    this.click('#TANGRAM__3__submit');
});


//把cookie写入文件
casper.wait(1000, function () {
    var cookies = JSON.stringify(phantom.cookies);
    fs.write(cookie_file, cookies, 644);
    console.log("write cookie file : " + cookie_file +"\n");
});

//执行
casper.run();

以查找百度音乐为例

//从文件读入cookie
var cookie_file = casper.cli.get(0);
var data = fs.read(cookie_file);
phantom.cookies = JSON.parse(data);

//根据xpath表达式定位dom元素
var x = require('casper').selectXPath;
//...

var artist = "周华健";
var title="风雨无阻";
casper.then(function(){
        var song_x = artist ? '//a/em[text()="' + artist + '"]//ancestor::div[@class="song-item clearfix"]' : '';
        song_x +="//span[@class='song-title']//a[@title='" + title + "']";
        var collect_x = x(song_x);
        if (this.exists(collect_x)) {
            var id = this.getElementAttribute(collect_x,'href');
            song_id = id.replace(/#.*/, '').replace(/^.*\//, '');
        }
    });

以收藏百度音乐为例

casper.then(function(){
        if(song_id==0) return;

        var collect_url = 'http://music.baidu.com/song/' + song_id;
        this.thenOpen(collect_url, function(){
            console.log("visit url : " + collect_url);
            var collect_x = x('//span[text()="收藏"]/parent::span/parent::a');
            if (this.exists(collect_x)) {
                console.log("click collect button : "+song_id);
                this.click(collect_x);
            }

            this.wait(1000, function() {
                var artist = this.getElementAttribute('span[class="author_list"]', 'title')
                var title = this.fetchText('span[class="name"]');
                status = this.fetchText('div[class="song-page-share clearfix"] span span');
                status = status.replace('分享','');
                console.log("song "+ artist + "" + title +" 》 : " + status+"\n");
            });
        });
    });

支持指定FRAME点击其中的元素

这个参考github上官方给的例子就行了 tests/suites/casper/frames.js

不像perl的Win32::IEAutomation,点击frame里面的 yyy 总是失败,哎。。。

特定event出现时的处理

看官方介绍 Hook in the deep using events,包括各种触发事件,例如mousemove,弹alert,page载入完毕等等

casper.on('http.status.404', function(resource) {
    this.log('Hey, this one is 404: ' + resource.url, 'warning');
});

打印变量详情

require('utils').dump(x);

载入JQquery或其他本地JS

远程DOM能用:casper.page.injectJs('/path/to/jquery.js');

casperjs run着就一直能用:

var casper = require('casper').create({
    clientScripts: ["includes/jquery.min.js"]
});

设置Referer

var casper = require('casper').create();

casper.start().then(function() {
    this.page.customHeaders = {
        "Referer" : "http://video.ustc.edu.cn/"
    };
});

casper.thenOpen('http://202.38.64.10/').run();

设置默认等待时间

casper.options.waitTimeout = 1000*60*3;

设置默认视窗大小

casper.options.viewportSize = {width: 1200, height: 750};

等某个元素出现再执行

casper.waitForSelector('#some_id', function() {
    var url = this.getCurrentUrl(); //获取当前url
    console.log('now url : ' + url);
    fs.write('now.html', this.getHTML(), "w");
});

预先检查url地址,决定是否访问

casper.on('resource.requested', function(requestData, request) {
    if ((requestData.url.indexOf('www.xxx.com') > -1)){
        //只载入url地址中包含www.xxx.com的页面
        casper.log('Request: ' + requestData.url, 'info');
    }else{
        casper.log('Skipped: ' + requestData.url, 'info');
        request.abort();
    }
    });

读入数组,按序执行

casper.eachThen(url_list, function(item){
    var  url=item.data;
    this.thenOpen(url, function(){
        this.sendKeys("input[id='someinput']", 'xxx');
    }).thenEvaluate(function(){
        somejs_func();
    }).wait(1000, function(){
            fs.write(out_f, this.getHTML("div[id='somediv']"), "w");
    });
});

windows下提示ssl错误

添加命令行参数

--ignore-ssl-errors=true --ssl-protocol=any --web-security=no


Published

20 February 2013

Categories

Tags


Share On