Blog - wingsico

起因

曾在大一的时候就听田大佬写了一个每日自动签到的脚本,觉得很牛逼,不过现在看起来确实和当初田大佬说的那样简单>,<,话不多说,直接开始吧。

准备工作

首先这个签到是我学校的一个工作室的内部网站的签到。一开始认为只需要登录了就算签到了(大一不了解的时候),现在知道了,其浏览器上的流程大概是: 打开登录页面 ——> 输入账号密码 ——> 点击登录 ——> 登录成功&自动完成签到; 其内部实现为: 输入网址 ——> 发送get请求 ——> 拿到返回的html并展示 ——> 输入账号密码 ——> 将账号密码放在请求体内通过post请求发送到对应的登录api ——> 拿到对应api所返回的数据(这里我们只需要token) ——> 对签到的api发送一个post请求,将token放入请求头中发送 ——> 签到成功. 那么如何实现以上操作呢?

实现

首先,打开us.ncuos.com(我们内部的网站), f12打开chrome debugger tools,点击network,钩上presever log,然后输入账号密码,点击登录,成功之后右边就会出现一堆请求,我们找到Name为login的包,找到这几项 这里标清楚了请求的url,method和请求体的类型,根据这三个我们可以很简单的写一个模拟登录来拿到对应的token:

var request = require('request') // 引入request包

var contents = {
  username: '613*****',
  password: '******',
  remember_me: false
} // 请求体,目前为一个对象

var options = {
  url: 'http://us.ncuos.com/api/user/login', // 请求的url
  method: 'POST', // 请求的方式 post
  headers: { // 设置请求头
    'Content-Type': 'application/json; charset=utf-8'
  },
  body: JSON.stringify(contents) // 对象JSON化
}

// 发送一个请求,
request(options, (error, response, body) => {
  if (!error && response.statusCode == 200) {
    var token = response.headers.authorization // 响应头里存储着token
    console.log(token)
  }
})

ok,是不是很简单呢,这样我们就拿到了token,就可以为所欲为了啊嘎嘎,比如爬个帖子内容啥的!哦不,回归正题,签到签到,我们再次回到浏览器,我们登录之后还请求了一些别的数据,比如Name为checkin的就是我们要找的签到的api, 由于这个接口是没有请求体的,所以我们不需要类似上面的contents了,只需要在登录之后再发送一个post请求,设置好请求头就可以了,由于代码比较短,就直接放在登录成功后的if语句内:

request(options, (error, response, body) => {
  if (!error && response.statusCode == 200) {
    var token = response.headers.authorization // 响应头里存储着token
    var params = {
      "url": 'http://us.ncuos.com/api/checkin', // 请求url
      "method": 'POST', // 请求方式
      "headers": { // 请求头
        "Content-Type": "Application/json", // 请求体格式
        "Authorization": token // auth token授权
      }
    }
    request(params, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        console.log(body) // 打印出响应体
      } else {
        console.log(error)
      }
    })
  }
})

写好了之后,保存为checkin.js, 执行node checkin.js,发现打印出来的是null,什么情况,为了查看原因,我们将签到请求做出一些修改:

request(params, (error, response, body) => {
  console.log(response.statusCode, response.statusMessage)
  if (!error && response.statusCode == 200) {
    console.log(body) // 打印出响应体
  } else {
    console.log(error)
  }
})

发现打印了 400,forbidden,null,再打印了一下整个response,发现在error里有更加详细的一个报错:

body: ‘{“error”: “InvalidData”, “message”: “invalid json content”, “status”: 400}’ } ‘{“error”: “InvalidData”, “message”: “invalid json content”, “status”: 400}’

很容易看出,error为非法json格式内容。wtf?啥情况,我这个根本不需要请求体啊,为什么还来个这个错误呢,于是走上了漫漫排查之路。

debugger

我首先先尝试了一下console.log(params),发现headers里的Authorization少了引号: 难道是这个的原因吗?然后我尝试了各种方法还是不能将引号加上,不过JSON.stringify(params) 之后是有引号的,所以觉得不是这个问题。苦思冥想无果之后,求助了一下学长,把情况说清楚之后,学长给出了一个尝试建议: 把headers里面的’Content-Type’去掉试试,去掉之后,我靠真的就成功了。之后探其原因是设置请求头中多余地规定了请求体的格式,但是你并没有也不需要发送请求体(对于这个api),但设置了这个请求头之后就强制的要求了你的格式,所以导致上面的报错信息,非法的content。为了验证这个想法,我把Content-type重新加上,写了一个json格式的请求体,里面数据随便填,果然也成功了!至此,我们的这个签到脚本就完成了。最终代码如下:

var http = require('http')
var request = require('request')

var contents = {
  username: 'username',
  password: 'password',
  remember_me: false
}

var options = {
  url: 'http://us.ncuos.com/api/user/login',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json; charset=utf-8'
  },
  body: JSON.stringify(contents)
}


request(options, (error, response, body) => {
  if (!error && response.statusCode == 200) {
    var token = response.headers.authorization
    var params = {
      "url": 'http://us.ncuos.com/api/checkin',
      "method": 'POST',
      "headers": {
        "Authorization": token
      }
    }
    request(params, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        console.log(body)
      } else {
        console.log(error)
      }
    })
  }
});

但是这样每天我们还是需要去自己执行一下这个脚本,还是不如自己手动去登录一下签到呢,所以等下我们就要实现的是 Linux服务器定时任务,可以每日定时执行这个脚本,就可以每日自动签到了,这个留到下一篇博客讲吧~先去睡觉~