【通大】统一身份认证登录并顺手查询寝室剩余电量加自动消息推送的一个小Demo

作者 by nkdns / 2022-03-13 / 暂无评论 / 347 个足迹

这个Demo供大家消遣学习


废话不多说直接上个代码

##基于统一身份认证的寝室电费查询系统,支持所有校区(只要你该填的不填错)
##该认证方法使用学校所有可以使用统一身份认证登录的网页平台(因为方法都一样虽然没试过/狗头)
##现在默认认证的是在线缴费平台,有其他平台需要自己改。认证都在 loing_authserver_ntu() 这个函数里面了,返回值是一个带通行证的连接,用对象get一下获取cookie就行(我就这么干的)

首先引用库requests,json,sys分别是用于get和post请求、json编码、sys控制错误的时候结束程序;execjs用于执行统一身份认证用来加密明文密码的js脚本;lxml中的etree是用来进行xpath定位网页的部分内容

import requests,json,sys
import execjs
from lxml import etree

然后是一堆变量的初始化,我备注的应该挺清楚了自己看吧

#学工号
reusername=""
#统一身份登录的密码
repassword=""
#填所在校区。校区的名字可以在下面chooseSchool里看
youschool=""
#填寝室楼名称
rebuilding=""
#填写寝室号通常楼号+寝室门号。充过电费应该都知道是什么意思
reroom=""
#设定一个电力阈值
lowpower=30
#消息推送的密钥 申请地址(http://www.pushplus.plus/)免费的
pushplus_parameters=""
#是否一对多推送
group=False
#以上为true时必填
groupid=''

chooseSchool=[                  #不需要修改
              {'school':'启东校区','eletype':'1','sign':'qidong'},
              {'school':'啬园校区','eletype':'2','sign':'zhuxiaoqu'},
              {'school':'钟秀校区','eletype':'3','sign':'zhongxiu'},
              {'school':'启秀校区','eletype':'4','sign':'qixiu'}
]
session2=requests.session()
#建立一个会话对象

接下来主要部分来了,我先抓取对应的stal(加密用)和lt、execution这些在登录的请求包中是必须的,至于作用后两个不太清楚,但是不重要。

def loing_authserver_ntu(reusername,repassword):     #利用统一身份认证,返回票据
    session=requests.session()
    login_url='http://authserver.ntu.edu.cn/authserver/login?service=http://pay.ntu.edu.cn/signAuthentication?url=openUserPayProList'
    #利用get取网页中的stal,lt,execution,
    thpage_text=session.get(login_url).text
    staltree = etree.HTML(thpage_text)
    thstal=staltree.xpath('//*[@id="pwdDefaultEncryptSalt"]/@value')
    thlt=staltree.xpath('//*[@id="casLoginForm"]/input[1]/@value')
    thexecution=staltree.xpath('//*[@id="casLoginForm"]/input[3]/@value')

这里开始保存统一身份认证平台用来加密的js脚本,并执行它,传入明文密码和stal,加密密码。

    encrypt_script_url = 'http://authserver.ntu.edu.cn/authserver/custom/js/encrypt.js'
    js = requests.get(encrypt_script_url).text
    ctx = execjs.compile(js)
    password = ctx.call('_ep', repassword, thstal[0])

构建请求的内容

    longin_data={
        'username':reusername,
        'password':password,
        'lt':thlt[0],
        'dllt':'userNamePasswordLogin',
        'execution':thexecution[0],
        '_eventId':'submit',
        'rmShown':'1'
    }
    login_res=session.post(login_url,longin_data,allow_redirects=False)

开始尝试从服务器响应头部取出登录用的令牌

    try:
        backhead=login_res.headers['Location']
    except KeyError:
        print('账号密码错误或需要验证码!')
        sys.exit(0)
    return(backhead)

至此登录的部分结束了
接下来这个函数是用来结合校区,找寝室楼号,根据楼号找寝室号的

def find_list(num,content):                              #从列表中找到并返回需要的字典
    if num==1 :
        for i in chooseSchool:
            if i['school']==content:
                return i
    elif num==2:
        for i in content:
            if i['buildingname'] == rebuilding:
                return i
    elif num==3:
        for i in content:
            if i['roomname']==reroom:
                return i['roomid']
    return('x')

这里是返回寝室楼号的列表的,用来给find_list函数使用这个列表是包含字典的,find_list是返回其中一个对应的字典。因为不管是查楼还是查寝室都要之前的数据所以干脆返回字典方便调用。

def get_buildinglist(schoolcontent):                         #获取该校区的寝室楼列表
    get_bulist_url='http://pay.ntu.edu.cn/querybuildingList'
    if schoolcontent == 'x':
        print('在 南通大学 没有找到这个校区!')
        sys.exit(0)
    list_post_data={
        'eletype':schoolcontent['eletype'],
        'factorycode':'E017',
        'sign':schoolcontent['sign']
    }
    blist=session2.post(get_bulist_url,list_post_data)
    if '系统正在结账' in blist.text:
        print('系统正在结账,无法查询')
        sys.exit(0)
    blistj=json.loads(blist.text).get('buildinglist')
    return(blistj)

顾名思义,获取房间列表的。和取楼差不多不来说了

def get_roomslist(schoolcontent,buildingcontent):            #获取该寝室楼的房间列表
    get_roomlist_url='http://pay.ntu.edu.cn/queryRoomList'
    if buildingcontent == 'x':
        print(f'在 {youschool} 中没有找到这个寝室楼!')
        sys.exit(0)
    list_post_data={
        'buildingid':buildingcontent['buildingid'],
        'schoolid':schoolcontent['eletype'],
        'factorycode':'E017',
        'sign':schoolcontent['sign']
    }
    rlist=session2.post(get_roomlist_url,list_post_data)
    rlistj=json.loads(rlist.text).get('roomlist')
    return(rlistj)

顾名思义,获取电的。他的返回信息里面balance的值就是剩余电量

def get_power(schoolcontent,buildingcontent,roomid):          #取得剩余电力
    POST_url='http://pay.ntu.edu.cn/querySydl'
    if roomid == 'x':
        print(f'在{youschool} {rebuilding} 中没有找到这个寝室号!')
        sys.exit(0)
    requestbody = {
        'room_id':roomid,
        'loudong_id':buildingcontent['buildingid'],
        'xiaoqu_id':schoolcontent['eletype'],
        'factorycode':'E017',
        'sign':schoolcontent['sign']
    }
    res=session2.post(POST_url,data=requestbody)
    ress=res.json()
    return ress['balance']

这个是消息推送模块pushplus提供的服外,具体的去看人家的接口文档

def sentmsg(surplus_power):                                 #低电量发信
    sentmsg_url='http://www.pushplus.plus/send'
    if group:
        requestbody={
            'token':pushplus_parameters,
            'title':'寝室电力预警',
            'topic':groupid,
            'content':f'{youschool}{reroom}寝室还剩 {surplus_power} 度电,已经低于你设置的 {lowpower} 度电的值'
        }
    else:
        requestbody={
            'token':pushplus_parameters,
            'title':'寝室电力预警',
            'content':f'{youschool}{reroom}寝室还剩 {surplus_power} 度电,已经低于你设置的 {lowpower} 度电的值'
        }
    ressent=requests.post(sentmsg_url,data=requestbody)
    print(json.loads(ressent.text).get('msg'))

主函数,调用上面几步

def main():
    backhead=loing_authserver_ntu(reusername,repassword)
    #登录获取302的url其中包含通行证
    session2.get(backhead)
    #上面这步get是为了向session对象中添加通过账户验证的cookie
    schoolcontent=find_list(1,youschool)
    buildinglist=get_buildinglist(schoolcontent)
    buildingcontent=find_list(2,buildinglist)
    roomslist=get_roomslist(schoolcontent,buildingcontent)
    roomid=find_list(3,roomslist)
    surplus_power=get_power(schoolcontent,buildingcontent,roomid)
    print(f'{youschool}{reroom}寝室还剩 {surplus_power} 度电')
    if surplus_power < lowpower:
        sentmsg(surplus_power)

函数的入口

if __name__ == '__main__':
    main()

代码下载:Demo.zip[3KB]

独特见解