这次学习的目标是实现115.com的自动登录、签到、摇一摇。115是国内流行的网盘。容量大,可分享的资源丰富。115网站为吸引用户,设置了签到功能和摇一摇功能。本篇希望写出一个能自动登录签到的python程序。搜索了一下,网上已经有高手
9deadblue@gmail.com写好:
妖妖舞娘为一个Google App Engine(GAE)应用,利用GAE的调度系统实现了定时签到、领取空间等工作。但现在115已经升级了安全登录管理,不知道这个程序还能用否。1.静态分析
首先看看115首页源文件中,查找与登录相关的代码如下:
var goto = "http://115.com";
//... ...部分省略
;(function(){
// 登录
var $doms = $('#js-passwd,#js-account');
Common.bindLabelHide($doms);
$("#js-account").addVerify("notnull",null,"输入115网盘帐号/手机/邮箱");
$("#js-passwd").addVerify("notnull",null,"输入你的密码");
$doms.keydown(function(e){
if(e.keyCode!=13){
valiSuccess($(this));
}
})
setTimeout(function(){
$("#js-account").focus();
if($.trim($("#js-account").val())!=""){
$("#js-passwd").focus();
}
},200)
$("#js-submit").click(function(){
if(Common.validate.validate($doms,valiError,valiSuccess)){
var code = "";
Common.setLoginData({goto:goto});
Common.login($("#js-account").val(),$("#js-passwd").val(),code,1,$("#js-remember_pwd").is(":checked"),function(json){
if(json.state){
$("#js-passwd").val(Common.encode_safe_pwd($("#js-passwd").val()));
$("#js-login_form").attr("action",json.goto)[0].submit();
setTimeout(function(){
location.href = json.goto;
},1000);
//location.href = json.goto;
}else{
if(json.err_name){
var $t = $doms.filter("[name="+json.err_name+"]");
if($t.length){
valiError(json.err_msg,$t.focus());
return;
}
}
$.alertTip(json.err_msg);
}
});
//登录
}
return false;
});
}());
当输入用户名和密码后,网页先校验用户名和密码的长度非空,调用common.js进行登录。
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
1 /**
2 * User: linweilong(TGL)
3 * Date: 2013-04-18 13:26
4 */
5
6 ;(function(){
7 var common ;
8 if(!window["Common"]){
9 common = window["Common"] = {};
10 }else{
11 common = window["Common"];
12 }
13 if(!common.passportUrl){
14 common.passportUrl = "";
15 }
16
17 /**
18 * 文本框有内容的时候隐藏对应的label
19 */
20 ;(function(){
21 var doms=[],labs=[],timer=null;
22
23 /**
24 * 文本框有内容的时候隐藏对应的label
25 * @param domIds jquery对象 #id数组 #id逗号分隔
26 */
27 common.bindLabelHide=function(domIds,labLs){
28 var tmp=domIds.jquery?domIds:domIds.join?$(domIds.join(",")):$(domIds);
29
30 if(labLs){
31 labLs=labLs.jquery?labLs:labLs.join?$(labLs.join(",")):$(labLs);
32 for(var i= 0,c=tmp.length;i<c;i++){
33 doms.push(tmp.eq(i));
34 labs.push(labLs.eq(i));
35 }
36 }else{
37 tmp.each(function(){
38 var lab=$("[for="+this.id+"]");
39 if(lab.length>0)
40 {
41 doms.push($(this));
42 labs.push(lab);
43 }
44 });
45 }
46
47 if(!timer){
48 timer=setInterval(function(){
49 for(var i= 0,c=doms.length;i<c;i++)
50 {
51 try{
52 labs[i][doms[i].val()==""?"show":"hide"]();
53 }catch(e){
54
55 }
56 }
57 },50);
58 }
59
60 }
61 }());
62
63 /**
64 * sha1相关
65 */
66 ;(function(){
67
68 var sha1 = common.sha1 = function(str,raw)
69 {
70 var hexcase = 0;
71 var chrsz = 8;
72 str = utf16to8(str);
73
74 function utf16to8(str)
75 {
76 var out, i, len, c;
77
78 out = "";
79 len = str.length;
80 for(i = 0; i < len; i++)
81 {
82 c = str.charCodeAt(i);
83 if ((c >= 0x0001) && (c <= 0x007F))
84 {
85 out += str.charAt(i);
86 }
87 else if (c > 0x07FF)
88 {
89 out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
90 out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
91 out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
92 }
93 else
94 {
95 out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
96 out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
97 }
98 }
99 return out;
100 }
101
102 function hex_sha1(s)
103 {
104 return binb2hex(core_sha1(str2binb(s),s.length * chrsz));
105 }
106
107 function str_sha1(s)
108 {
109 return binb2str(core_sha1(str2binb(s),s.length * chrsz));
110 }
111
112 function binb2hex(binarray)
113 {
114 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
115 var str = "";
116 for(var i = 0; i < binarray.length * 4; i++)
117 {
118 str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
119 hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
120 }
121 return str;
122 }
123
124 function binb2str(bin)
125 {
126 var str = "";
127 var mask = (1 << chrsz) - 1;
128 for(var i = 0; i < bin.length * 32; i += chrsz)
129 str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
130 return str;
131 }
132
133 function str2binb(str)
134 {
135 var bin = Array();
136 var mask = (1 << chrsz) - 1;
137 for(var i = 0; i < str.length * chrsz; i += chrsz)
138 bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
139 return bin;
140 }
141
142 function safe_add(x, y)
143 {
144 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
145 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
146 return (msw << 16) | (lsw & 0xFFFF);
147 }
148
149 function rol(num, cnt)
150 {
151 return (num << cnt) | (num >>> (32 - cnt));
152 }
153
154 function sha1_ft(t, b, c, d)
155 {
156 if(t < 20)
157 return (b & c) | ((~b) & d);
158 if(t < 40)
159 return b ^ c ^ d;
160 if(t < 60)
161 return (b & c) | (b & d) | (c & d);
162 return b ^ c ^ d;
163 }
164
165 function sha1_kt(t)
166 {
167 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514;
168 }
169
170 function core_sha1(x, len)
171 {
172 x[len >> 5] |= 0x80 << (24 - len % 32);
173 x[((len + 64 >> 9) << 4) + 15] = len;
174
175 var w = Array(80);
176 var a = 1732584193;
177 var b = -271733879;
178 var c = -1732584194;
179 var d = 271733878;
180 var e = -1009589776;
181
182 for(var i = 0; i < x.length; i += 16)
183 {
184 var olda = a;
185 var oldb = b;
186 var oldc = c;
187 var oldd = d;
188 var olde = e;
189
190 for(var j = 0; j < 80; j++)
191 {
192 if(j < 16)
193 w[j] = x[i + j];
194 else
195 w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
196 var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j)));
197 e = d;
198 d = c;
199 c = rol(b, 30);
200 b = a;
201 a = t;
202 }
203
204 a = safe_add(a, olda);
205 b = safe_add(b, oldb);
206 c = safe_add(c, oldc);
207 d = safe_add(d, oldd);
208 e = safe_add(e, olde);
209 }
210 return Array(a, b, c, d, e);
211 }
212
213 if (raw == true)
214 {
215 return str_sha1(str);
216 }
217 else
218 {
219 return hex_sha1(str);
220 }
221 }
222
223
224 var encode_list = [0,2,4,16];
225 function decode_sha1_safe_pwd(pwd){
226 var old = pwd;
227 if(pwd.length==48){
228 pwd = pwd.split("");
229 var i, c, n, p,tmp;
230 tmp = 0;
231 for(i=0,c=pwd.length;i<c;i++){
232 tmp += parseInt(pwd[i],16);
233 }
234 tmp = (parseInt(pwd[0],16) - (tmp%2)*2);
235 if(tmp<0) tmp+=16;
236 pwd[0] = tmp.toString(16);
237
238 for(i= encode_list.length-1;i>=0;i--){
239 pwd.reverse();
240 n = encode_list[i];
241 p = pwd[n];
242 tmp = parseInt(p,16);
243 tmp = pwd.splice(n+tmp,1);
244 if(tmp[0]!=pwd.pop()){
245 break;
246 }
247 }
248 return pwd.join("");
249 }
250 return sha1(old);
251 }
252 //common.decode_safe_pwd = decode_sha1_safe_pwd;
253 common.encode_safe_pwd = function(pwd){
254 var tmp;
255
256 pwd = decode_sha1_safe_pwd(pwd);
257 pwd = pwd.split("");
258 var i, c, n,p
259 for(i= 0,c=encode_list.length;i<c;i++){
260 n = encode_list[i];
261 p = pwd[n];
262 tmp = parseInt(p,16);
263 pwd.splice(n+tmp,0,p);
264 pwd.push(p);
265 pwd.reverse();
266 }
267 tmp = 0;
268 for(i=0,c=pwd.length;i<c;i++){
269 tmp += parseInt(pwd[i],16);
270 }
271 tmp = ((tmp%2)*2+parseInt(pwd[0],16))%16;
272 pwd[0] = tmp.toString(16);
273 return pwd.join("");
274 }
275
276 common.sha1_pwd = function(pwd,account,vcode){
277
278 //return sha1( sha1( hexchar2bin(sha1(pwd))+hexchar2bin(sha1(account)) ) + vcode.toUpperCase());
279 return sha1( sha1(decode_sha1_safe_pwd(pwd)+sha1(account) ) + vcode.toUpperCase());
280 }
281 }());
282
283 /**
284 * uniqid
285 */
286 ;(function(){
287
288 common.uniqid = function (prefix, more_entropy) {
289 if (typeof prefix == 'undefined') {
290 prefix = "";
291 }
292
293 var retId;
294 var formatSeed = function (seed, reqWidth) {
295 seed = parseInt(seed, 10).toString(16); // to hex str
296 if (reqWidth < seed.length) { // so long we split
297 return seed.slice(seed.length - reqWidth);
298 }
299 if (reqWidth > seed.length) { // so short we pad
300 return Array(1 + (reqWidth - seed.length)).join('0') + seed;
301 }
302 return seed;
303 };
304
305 if (!this.php_js) {
306 this.php_js = {};
307 }
308 if (!this.php_js.uniqidSeed) { // init seed with big random int
309 this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
310 }
311 this.php_js.uniqidSeed++;
312
313 retId = prefix; // start with prefix, add current milliseconds hex string
314 retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
315 retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
316 if (more_entropy) {
317 // for more entropy we add a float lower to 10
318 retId += (Math.random() * 10).toFixed(8).toString();
319 }
320
321 return retId;
322 }
323 }());
324
325 /**
326 * login 登录
327 */
328 ;(function(){
329 var cache = {
330 loginData : {
331 login:{
332 "ssoent":"A1",
333 "version":"2.0",
334 "ssoext":common.uniqid()
335 }
336 },
337 verifyData:{
338 form:{
339 "ssoent":"A1",
340 "version":"2.0",
341 "ssoext":common.uniqid()
342 }
343 }
344 }
345 common.setLoginData = function(data){
346 cache.loginData = $.extend(true,cache.loginData,data);
347 }
348 common.login = function(acount,pwd,vcode,safe,remember,callback,safe_login){
349 var pdata = $.extend(true,{},cache.loginData);
350 if(!pdata.login){
351 pdata.login = {};
352 }
353 pdata.login["ssoln"] = acount;
354 pdata.login["ssopw"] = common.sha1_pwd($.trim(pwd), $.trim(acount),pdata.login.ssoext);
355 pdata.login["ssovcode"] = pdata.login.ssoext;
356 pdata.login["safe"] = safe?1:0;
357 pdata.login["time"] = remember?1:0;
358 pdata.login["safe_login"] = safe_login?1:0;
359 common.ajax({
360 url:common.passportUrl + "/?ct=login&ac=ajax&is_ssl=1",
361 type:"post",
362 data:pdata,
363 dataType:"json",
364 success:function(json){
365 if(callback){
366 callback(json);
367 }else{
368
369 }
370 }
371 })
372 }
373
374 common.loginVerify = function(data,callback){
375 var pdata = $.extend(true,{},cache.verifyData);
376 pdata.form["code"] = data.code;
377 pdata.form["answer"] = data.answer;
378 pdata.form["question"] = data.question;
379 if(data.trust){
380 pdata.form["trust"] = "trust";
381 }
382 pdata["token"] = data.token;
383 pdata["method"] = data.method
384 common.ajax({
385 url:common.passportUrl + "/?ct=login&ac=vip&is_ajax=1&is_ssl=1",
386 type:"post",
387 data:pdata,
388 dataType:"json",
389 success:function(json){
390 if(callback){
391 callback(json);
392 }else{
393
394 }
395 }
396 })
397 }
398 }());
399
400 /**
401 * waitLogin 第三方登录等待登录成功
402 */
403 ;(function(){
404 var cache = {};
405 function doget(){
406 if(doget.ajaxing) return;
407
408 cache.timer&&clearTimeout(cache.timer);
409 cache.timer = false;
410 doget.ajaxing = true;
411 $.ajax({
412 url:common.passportUrl + "/?ct=ajax&ac=islogin&is_ssl=1",
413 cache:false,
414 dataType:"json",
415 complete:function(){
416 doget.ajaxing=false;
417 },
418 success:function(json){
419 if(json.state){
420 cache.callback&&cache.callback(json);
421 }else{
422 cache.timer&&clearTimeout(cache.timer);
423 cache.timer = setTimeout(function(){
424 doget()
425 },1500);
426 }
427 }
428 })
429 }
430 function start(){
431 if(cache.timer) return;
432 doget();
433 }
434 common.waitLogin = function(callback){
435 cache.callback = callback;
436 setTimeout(function(){
437 start()
438 },3000);
439 }
440 }());
441
442
443 /**
444 * 扩展jQuery
445 */
446 ;(function(){
447 $.extend($, {
448 loadTip: function(text,container,timer){
449 if($.isNumeric(container)){
450 timer=container;
451 container=null;
452 }
453 if(top.window.Core&&top.window.Core.MinMessage){
454 top.window.Core.MinMessage.Show({
455 text: text,
456 type: "load",
457 window:container? { warp:container}:null,
458 timeout: timer
459 });
460 }else{
461 alert(text);
462 }
463
464 },
465 successTip: function(text,container,timer){
466 if($.isNumeric(container)){
467 timer=container;
468 container=null;
469 }
470 if(top.window.Core&&top.window.Core.MinMessage){
471 top.window.Core.MinMessage.Show({
472 text: text,
473 type: "suc",
474 window:container? { warp:container}:null,
475 timeout: timer||2000
476 });
477 }else{
478 alert(text);
479 }
480
481 },
482 infoTip: function(text, container,timer){
483 if($.isNumeric(container)){
484 timer=container;
485 container=null;
486 }
487 if(top.window.Core&&top.window.Core.MinMessage){
488 top.window.Core.MinMessage.Show({
489 text: text,
490 type: "inf",
491 window:container? { warp:container}:null,
492 timeout: timer||2000
493 });
494 }else{
495 alert(text);
496 }
497
498 },
499 alertTip: function(text, container,timer){
500 if($.isNumeric(container)){
501 timer=container;
502 container=null;
503 }
504 if(top.window.Core&&top.window.Core.MinMessage){
505 top.window.Core.MinMessage.Show({
506 text: text,
507 type: "war",
508 window:container? { warp:container}:null,
509 timeout: timer||2000
510 });
511 }else{
512 alert(text);
513 }
514 },
515 hideTip:function(){
516 top.window.Core.MinMessage.Hide();
517 },
518 closeTip:function(){
519 top.window.Core.MinMessage.Hide();
520 },
521 confirm:function(text,content,callback){
522 var opt;
523 if(typeof text!="string"){
524 opt=text;
525 }else{
526 if(typeof content=="function")
527 {
528 callback=content;
529 content="";
530 }
531 opt={
532 text:text,
533 content:content,
534 callback:callback
535 };
536 }
537 top.window.Core.Message.Confirm(opt);
538 }
539 });
540 }());
541
542 /**
543 * loadBridge
544 */
545 ;(function() {
546 var cache = {}, loading = {};
547 common.loadBridge = function(url, callback) {
548 if (cache[url]) {
549 callback && callback(cache[url]);
550 } else if (loading[url]) {
551 if (callback) {
552 loading[url].push(callback);
553 }
554 } else {
555 var guid = "bridge_"+(+new Date());
556 var $if;
557 loading[url] = [];
558 if (callback) loading[url].push(callback);
559 window[guid] = function(ajax, win) {
560 cache[url] = ajax;
561 window[guid] = null;
562 var cbs = loading[url];
563 var cb;
564 loading[url] = null;
565 try {
566 delete window[guid];
567 delete loading[url];
568 } catch (e) {}
569 if (cbs) {
570 while (cb = cbs.shift()) {
571 try {
572 cb(ajax);
573 } catch (e) {}
574 }
575 }
576 };
577 var src = url;
578 if (src.indexOf("?") >= 0) {
579 src += "&";
580 } else {
581 src += "?";
582 }
583 src += "ajax_cb_key=" + guid;
584 $(function() {
585 $if = $("<iframe src='" + src + "' style='display:none;'></iframe>").appendTo($(document.body));
586 });
587 }
588 };
589 })();
590
591 ;(function(){
592 common.ajax = function(){
593 var args = Array.prototype.slice.call(arguments);
594 if(common.mustBridge){
595 common.loadBridge(common.bridgeUrl,function(ajax){
596 common.ajax = ajax;
597 common.ajax.apply(null,args);
598 });
599 }else{
600 common.ajax = $.ajax;
601 common.ajax.apply(null,args);
602 }
603
604 }
605 }());
606
607 ;(function(){
608 var token="";
609 common.initToken = function(t){
610 token = t;
611 }
612
613 common.getToken = function(callback){
614 if(token){
615 callback&&callback(token);
616 return;
617 }
618 common.ajax({
619 url:common.passportUrl + "/?ct=login&ac=get_token",
620 dataType:"json",
621 success:function(json){
622 if(json.state){
623 token = json.sign;
624 callback&&callback(token);
625 }else{
626 alert(json.err_msg);
627 }
628 }
629 })
630
631 }
632 }());
633 }());
634
2.动态分析,用http analyser记录登录全过程的HTTP数据流。验证自己对登录过程的理解。
根据我的分析,大致描述115网盘登录过程
(1)查看第一条post命令(http://passport.115.com/?ct=login&ac=ajax&is_ssl=1),截图如下
![](/images/cppblog_com/huyutian/post01.JPG)
登录时需要post的参数有
Parameter Name Value
login[ssoent] A1
login[version] 2.0
login[ssoext] *************(13位)
login[ssoln] ***********(用户名,明码)
login[ssopw] *************(加密后密码,40位)
login[ssovcode] *************(13位,同ssoext)
login[safe] 1
login[time] 0 (是否勾选5天内免登录)
login[safe_login] 0
goto http://115.com
common.js中计算13位(16进制字符串)ssoext:前8位是当前时间秒值,后5位是一个小于123456789的随机数
ssovcode与ssoext一样。
ssopw采用了sha1算法,sha1( sha1(sha1(pwd)+sha1(account)) + ssovcode.toUpperCase())
返回页面包含重定向网址链接“http:\/\/passport.115.com\/?ct=login&ac=gotos&goto=http%3A%2F%2Fpassport.115.com%2F%3Fct%3Dlogin%26ac%...”
并设置记录登录状态的cookies信息(ssoidA1,ssoinfoA1,OORA,OOFA)
(2)post消息到刚才获得的重定向网址,登录完成。
Parameter Name Value
account ***********(用户名,明码)
passwd
************************(密码用Common.encode_safe_pwd函数加密)
(3)访问115个人中心,获取用户信息
GET命令 http://my.115.com/?ct=ajax&ac=get_user_aq&js_return=abc
返回状态var abc = {"state":true,"data":{"uid":"********","uname":"******",......},......}
测试了一下,这一步似乎不能省略。否则下面签到时不会返回token值
(4)GET命令 http://115.com/?ct=event&ac=get_active_param&_=1391498007778(时间戳) 获取签到状态信息
返回签到状态{"state":true,"is_take":true,"is_take_token":"a1f1ad317b680d0f3ce34153a247383e","is_checkin":"11","this_turn":"11",...}
(5)POST命令 http://115.com/?ct=ajax_user&ac=checkin 签到
Parameter Name Value
token
a1f1ad317b680d0f3ce34153a247383e(刚才服务器返回的token)
返回签到状态{"state":true,"data":{"this_turn":11,"this_turn_space":"11GB","next_turn":12,"next_turn_space":"12GB"}}
(6)摇一摇 GET命令 http://115.com/?ct=yao (记住要携带cookie)
查看返回的源文件,可以看到如下片段,记录下take_token串值,后面摇一摇时要用到
/*
@摇一摇获取数据
*/
var take_token = 'daadb242dfbb9756a16323c6ef548321'
, is_take = Number('1')
, is_allow = Number('1')
, is_login = Number('********')
, is_vip = Number('1')
, is_go = true
, is_me = 1;//Number('');
继续向下可以看到与摇一摇有关代码片段如下:
![](/Images/OutliningIndicators/ContractedBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
1 _swf.start();
2 _isyao = true;
3 var url = '?ct=yao&ac=guest_get_space';
4 if(is_login) {
5 url = "?ct=ajax_user&ac=pick_spaces&u=1&token=" + take_token;
6 }
7 if(is_take || !is_login) {
8 $.ajax({
9 url: url,
10 type: 'GET',
11 dataType: 'json',
12 success: function(r) {
13 if(r.state) {
14 var DATA = new Date();
15 if(!is_login) {
16 dom.yao_text.html('恭喜你! 本次摇奖获得 <b>'+r.data.space+'MB</b> 空间!<p style="text-align:center;"><a href="'+r.data.go_url+'" target="_blank"><img src="/static/images/yyl-button.gif" /></a></p>');
17 jia.pic = 'http://115.com/static/images/not_reg_pic.jpg';
18 jia.title= '我今天在#115网盘摇摇乐#获得了'+r.data.space+'MB永久免费空间!真是太幸运啦!115网盘超大免费空间,安全备份你的电脑文件,永久保存云端,并可无限扩容!你也一起来玩吧: http://115.com/?ct=yao&ac=guest '
19 }else {
20 dom.yao_text.html('恭喜你!本次摇奖获得 <b>'+r.picked+'</b> 空间'+ ( DATA.getHours() < 12 ? ',12:00后你可摇第二次!' : '!' )+tips);
21 jia.pic = 'http://115.com/static/images/yaoayo/'+r.picked_num+'.jpg';
22 jia.title = '我使用#115网盘摇摇乐#摇奖游戏摇到了'+r.picked+'永久空间!115网盘免费空间可无限扩容,每天摇两次,次次有惊喜!今天你摇了吗?你也一起来玩吧: http://115.com/?ct=yao 【115,改变分享!】';
23 }
24 $("#js-jiathis").show();
25 }else {
26 var text = '你今天摇一摇领空间的机会已用完,请明天再试!'+ tips;
27 if(r.msg && r.msg_code && r.msg.length) {
28 text = r.msg;
29 }
30 dom.yao_text.html(text);
31 $("#js-jiathis").hide();
32 }
33 }
34 (6)GET命令摇一摇http://115.com/?ct=ajax_user&ac=pick_spaces&u=1&token=
daadb242dfbb9756a16323c6ef548321
返回摇一摇获得的空间信息{"state":true,"picked":"2728MB","picked_num":2728,"flag":false,"total_size":"22910570MB","used_percent":"0%","exp":793}
到这里整个115登录、签到、摇一摇的过程分析完了。下面就是如何用python来模仿以上过程了。首先需要有一个python下方便好用的http包。