node.js实现模拟登录,自动签到领流量。

开始本文之前,期待您了解页面与服务器的交互知识,会更容易理解。

需求

  • 某通有个活动,只要每天签到等各种操作就能累积积分,随着等级提升每月可以领到 20M - 300M的流量等。

  • 作为码农的你当然不愿意以正常的姿势去完成这些事情,所以你想要程序代替你完成这些事情。((^__^) 嘻嘻……)

效果

  • 理想:

    1. 下午六点
    2. 你收到一份邮件,里面躺着截图和报告,写着你的号码累积的积分,签到天数,优惠等。
    3. 你可以置之不理,或者回复操作:
      • 登录需要验证码时,回复验证码登录。
      • 领取当月流量等。
  • 本文:

    1. 下午六点
    2. 你收到一份邮件,告诉你已完成签到。

准备工作

  1. superagent,模拟登录,触发页面
  2. nodemailer,发送邮件
  3. later,定时执行任务
  4. *async,排队完成任务(可选,某些任务有先后顺序)
  5. *phantomJS,网页截图(可选)

目录规划

/app.js 程序入口
/config.js 账号配置信息等
/controller/
autoCheckIn.js 自动签到等任务
sendEmail.js 发送邮件
task.js 定时任务

0. 配置账号信息-config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module.exports = {
    // 某通账户
    "accounts": [
        {
            "user": 15500000000,
            "password": 123456
        },
        {
            "user": 15500000001,
            "password": 123456
        }
    ],

    // 邮件账户
    "email": {
        "host": 'smtp.163.com',
        "user": 'xxx@163.com',
        "password": 123456,
        "toUser": 'xxx@qq.com'
    }
};

1. 发送邮件-sendEmail.js

这里没什么特别的,通过 nodemailer 模块代理发送邮件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var email = require('../config.js').email;
var nodemailer = require('nodemailer');

var transporter = nodemailer.createTransport({
  host: email.host,
  secureConnection: true, // use SSL
  auth: {
    user: email.user,
    pass: email.password
  }
});

/**
 * 发送邮件
 * @param contents
 */
module.exports = function (contents) {
  transporter.sendMail({
    from: email.user,
    to: email.toUser,
    subject: 'checkIn success!',
    text: contents || 'is test!'
  }, function (error, response) {
    if (error) {
      console.log(error);
    } else {
      console.log("Message sent: " + response.response);
    }

    transporter.close(); // 如果没用,关闭连接池
  });
};

2.1 一些调查工作

在模拟登录前,你通过抓包工具来收集登录所需的数据。

  • 因为页面会跳转,所以通过断点调试来暂停页面。
  • 接着查看发出的数据。

2.2 模拟登录和访问页面-autoCheckIn.js

  • 不确定服务器会接收哪些信息,所以你把大部分headers字段都写上了,尽可能模拟真实。
  • 另外值得注意的是,你发现总是拿不到cookie,原来是页面重定向了,加上了 .redirects(0),防止页面被转向。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
var request = require('superagent');
var sendEmail = require('./sendEmail');

var headers = {
    Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    Origin: 'http://wap.17wo.cn',
    'X-FirePHP-Version': '0.0.6',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36',
    'Content-Type': 'application/x-www-form-urlencoded',
    DNT: 1,
    Referer: 'http://wap.17wo.cn/Login.action',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.6,en;q=0.4,sr;q=0.2'
};

var origin = 'http://17wo.cn',
    urls = {
        login: origin + '/Login!process.action',
        checkIn: origin + '/SignIn.action?checkIn=true'
    };


/**
 * 自动签到
 * @param account {object}
 * @constructor
 */
function AutoCheckIn(account) {
    this.account = account;

    this.cookie = {
        value: null,
        expires: null
    };

    this.init();
}

AutoCheckIn.prototype = {
    constructor: AutoCheckIn,

    init: function () {
        var that = this;

        that.checkIn(function () {
            sendEmail(that.account.user + ',签到完毕。 ' + new Date());
            console.log('======', '签到完毕,' + that.account.user, '======');
        });
    },

    // 验证登录,如果凭证没过期,无需重新验证
    _verify: function (cb) {
        Date.now() > this.cookie.expires ? this._login(cb) : cb(this.cookie);
    },

    // 登录
    _login: function (cb) {
        var that = this;

        request
            .post(urls.login)
            .set(headers)
            .type('form')
            .send({
                backurl: null,
                backurl2: null,
                chk: null,
                chkType: 'on',
                loginType: 0,
                mobile: that.account.user,
                password: that.account.password
            })
            .redirects(0) // 防止页面重定向
            .end(function (result) {
                var cookie = result.headers['set-cookie'];
                that.cookie = {
                    value: cookie,
                    expires: cookie.join().match(/Expires=(.*);/)[1]
                };

                cb(that.cookie);
            });
    },

    // 签到
    checkIn: function (cb) {
        var that = this;

        that._verify(function (cookie) {
            request
                .get(urls.checkIn)
                .set(headers)
                .set('Cookie', cookie.value)
                .end(cb);
        });
    }
};


module.exports = function (account) {
    return new AutoCheckIn(account);
};

3. 定时任务-task.js

包装定时任务,方便调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var later = require('later');

/**
 * 定时任务
 * @param time 参考later的base time
 * @param intervalFn 定时执行的函数
 */
module.exports = function (time, intervalFn) {
    var sched = {schedules: [time]};
    
    // 设定为本地时间
    later.date.localTime(); 
    later.setInterval(intervalFn, sched);
};

4. 整合起来-app.js

1
2
3
4
5
6
7
8
9
10
11
12
var accounts = require('./config').accounts;
var task = require('./controller/task');
var autoCheckIn = require('./controller/autoCheckIn');

// 定时执行
task({h: [18], m: [0]}, function () {
    accounts.forEach(function (v) {
        autoCheckIn(v);
    });
});

console.log('======', '自动签到服务运行中..', '======');

最后

到此简单几步已实现了想要的核心功能,很好玩,充满了想象..

本文重点在于分享思路,略过了大量细节,无源代码,所需的内容均在在本文中 :)

  1. superagent,模拟登录,触发页面
  2. nodemailer,发送邮件
  3. later,定时执行任务
  4. *async,排队完成任务(可选,某些任务有先后顺序)
  5. *phantomJS,网页截图(可选)

技术: node.js+很多模块
时间: 2015年02月