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
33
34
35
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 月