开始本文之前,期待您了解页面与服务器的交互知识,会更容易理解。
需求
-
某通有个活动,只要每天签到等各种操作就能累积积分,随着等级提升每月可以领到 20M - 300M的流量等。
-
作为码农的你当然不愿意以正常的姿势去完成这些事情,所以你想要程序代替你完成这些事情。((^__^) 嘻嘻……)
效果
-
理想:
- 下午六点
- 你收到一份邮件,里面躺着截图和报告,写着你的号码累积的积分,签到天数,优惠等。
- 你可以置之不理,或者回复操作:
- 登录需要验证码时,回复验证码登录。
- 领取当月流量等。
-
本文:
- 下午六点
- 你收到一份邮件,告诉你已完成签到。
准备工作
- superagent,模拟登录,触发页面
- nodemailer,发送邮件
- later,定时执行任务
- *async,排队完成任务(可选,某些任务有先后顺序)
- *phantomJS,网页截图(可选)
目录规划
/app.js
程序入口
/config.js
账号配置信息等
/controller/
autoCheckIn.js
自动签到等任务
sendEmail.js
发送邮件
task.js
定时任务
0. 配置账号信息-config.js
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
模块代理发送邮件。
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)
,防止页面被转向。
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
包装定时任务,方便调用。
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
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('======', '自动签到服务运行中..', '======');
最后
到此简单几步已实现了想要的核心功能,很好玩,充满了想象…
本文重点在于分享思路,略过了大量细节,无源代码,所需的内容均在在本文中 :)
- superagent,模拟登录,触发页面
- nodemailer,发送邮件
- later,定时执行任务
- *async,排队完成任务(可选,某些任务有先后顺序)
- *phantomJS,网页截图(可选)
技术: node.js+很多模块
时间: 2015年02月
博客: think2011
叼叼的
不错,收藏了!
good
机智
机智哥 get
哈哈,孺子可教也,将来之栋梁
顶
写的很详细,Mark
漂亮~
你好像只签到了,不加成长值的。加成长值的是任务,我没说错吧。
机智,回去把代码落地,看看效果
@coolicer 写这个不是为了实现例子,只是一个想法,所以也没有提供完整的源代码。
头都洗湿了,干嘛不洗身子
= =我上个月一直在学PHP cURL也是为了模拟17wo的签到,后来发现只要拿到cookie就OK,模拟登录总是会被17wo服务器拒绝,现在也没搞明白,最近想学NodeJS,随便一搜就找到了这篇文章,希望和Lz多学习交流,成长值是靠完成任务的,光签到只是第一步,而有一个派发流量的任务,直接向服务器提交数据是无效的 T_T
楼主,如果有登录验证码,怎么破
@scplay 表示已经搞定了,模拟登录的原因上面有提到,因为会有一次跳转。
@njaulj 可以用tessearct来识别验证码,成功率挺高的。
收藏
牛B,mark
光学字符识别(OCR,Optical Character Recognition) Tesseract 很高端的样子
nice
mark
Mark
我想写的是浏览器插件。。。 自豪地采用 CNodeJS ionic
zou
这个歌666666啊啊··