( 原文地址:https://0xffff.one/d/494 )

又做了下调参侠 (╯‵□′)╯︵┻━┻

前言·

前几天搞打卡的时候就想做这个了,但一时没做出来,今天终于算是搞定了。

目的是实现下面这样的验证码的自动识别,然后自动登录sso/ssp,然后实现自动的打卡。

基于Github上的几个项目做的,传送门:

  • CNN_captcha:比较现成的验证码识别的机器学习项目。
  • any-captcha:由于CNN_captcha的验证码和sso上的验证码差别比较大,所以又找了个用来生成训练集的项目。
  • scnu-service:不知道哪个兄弟写的一个集成学校一些应用的项目,ssp的登录参考了一下里面的成绩查询(不过ssp的登录和成绩系统的登录还是有点差别的- -)。

环境·

  • Python3:不用多说了。
  • Tensorflow:机器学习必备。
  • PyTorch:CNN_captcha是基于这个的,而且它的准确率好像特别的高?装的时候注意如果是gpu版本的话,不同版本的cuda是对应不用版本的torch的,不然好像是装不上的。如果不打算训练的装cpu版应该就可以了。
  • 其他python的模块:requests、re之类的,缺哪个装哪个就好了,具体有什么我也忘了- -

验证码生成·

CNN_captcha是自带了generate_captcha.py这个文件生成验证码的,不过用它自带的generate生成的训练过后发现对sso上的验证码一个都识别不到- -,所以才找到了any-captcha这个项目,里面的icp_factory生成的验证码就跟我想要的很像了,但是还是有点差别,所以代码还需要改一下:

  • 首先一个是字数,这个通过修改config文件夹下的icp.json里的num改成4(其实看文档就知道怎么改了);
  • 然后是字体,网上找了一下找到个叫“方正雅士黑 简 medium”的字体跟sso的比较相似,于是就用这个了,下载下来后修改icp.json里的fonts;
  • 然后是字体的颜色,sso上的是没有那么鲜艳的,可以在sso上下载几张图片下来取个样,然后把icp.json里的colors替换掉即可;
  • 背景颜色同上;
  • 然后是图片大小,icp.json里改成长100宽50,然后字体大小也大概调一下;
  • 最后是背景的干扰线,一个是sso上的干扰线是比较细的(实际是1像素),还有干扰线数量是两条,颜色两条不一样,而且两条线端点是不重合的。这里除了干扰线数量可以在icp.json里改外其他都需要改代码,找到model文件夹里的captcha.py的draw_noise_curve函数位置,改成下图那样:

然后生成的验证码就跟sso上的有点像了(实测这一步才是对准确率影响最大的- -)

(虽然好像也不是太像 (╯‵□′)╯︵┻━┻x2)

训练·

首先建议在生成验证码的时候就指定好训练集和测试集的文件夹位置,不然图片数量太多的话打开文件夹会卡住的,这个改generate_captcha.py的代码就好了;然后是训练集的数量,我自己是取(训练集:测试集=10:1)的,但测试机好像是要128的倍数(大概是batch还是那啥的大小取了128),训练集的话试过5w和30w+结果其实是差不多的(我自己弄的大概50左右,就是用sso上下下来的验证码50个,5w训练时中了25个,30w+训练时中了26个。。。)。

训练的话可以直接用CNN_captcha的代码,首先是parameters.py里面的训练参数可以自己改,做ML的应该都知道的吧,反正我不是做ML的(逃)。训练集和测试集的路径要改train.py里的代码,parameters.py里的是假的- -,改好后运行main.py训练,训练时好像要把visdom开起来,不然会报一个什么鬼拒绝访问的错误,运行下面命令:

1
python -m visdom.server

训练好的模型放在model文件夹里的captcha文件夹里,resNet_new.pth那个应该就是最终的了。如果不把model/captcha里面的东西删掉的话,模型时可以多次训练的。

sso登录·

userTest.py是对里面选定的文件夹里的验证码测试正确率的,改一下就可以写成个函数用来返回文字的验证码。

验证码的使用方式是:

  • 首先通过https://sso.scnu.edu.cn/AccountService/user/rancode.jpg获取一个图片的验证码。
  • 然后通过上面说的模型识别到文字的验证码后可以通过https://sso.scnu.edu.cn/AccountService/user/checkrandom.html来测试验证码是否正确。
  • 最后通过https://sso.scnu.edu.cn/AccountService/openapi/auth的API把sso的用户名/学号、密码和验证码送过去就可以登录了(注意验证码是最新获取的图片的验证码)。
  • (具体的看代码吧)。

ssp的登录是可以通过sso登录的,操作:

  • 首先requests访问一次ssp主页可以在cookies里获取一个ASP.NET_SessionId。
  • 登录sso调用openapi/auth时,把response_type设为code、把redirect_url设置为https://ssp.scnu.edu.cn/LogBySso.aspx的话,返回的东西的headers里就带有一个location(含有一个叫code的应该是识别码的东西)。
  • 把上面得到的ASP.NET_SessionId填到cookies里,然后post访问一下上面的location就登录成功。登录成功后会又一个key值,可以用正则匹配一下,返回的页面里的连接都是含key的(不得不吐槽一下,怎么给我返回的都是静态的页面?(╯‵□′)╯︵┻━┻x3)。

自动打卡·

登录成功获取key值后,访问https://ssp.scnu.edu.cn/opt_rc_jkdk.aspx?key=your_own_key&fid=55(以下简称jkdkUrl吧- -)就是打卡的页面。

  • 打卡的话首先要进入一个叫“开始打卡的页面”,把’ctl00cphrightcph_righte_ok’设置为on,把’ctl00cphrightcph_rightok_submit’设置为 ‘开始填报’ ,然后post过jkdkUrl。其实有一些参数(EVENTTARGET、EVENTARGUMENT、LASTFOCUS、VIEWSTATE、VIEWSTATEGENERATOR、EVENTVALIDATION)不知道要不要填的,代码里还是把它填了,因为之前调试的时候太多玄学问题了,(╯‵□′)╯︵┻━┻x4
  • 然后打卡的话先把下面参数填好,post过jkdkUrl就好,成功的话返回的页面一打开就会有 打卡成功 的alert弹窗。
1
2
3
4
5
6
7
8
9
10
11
12
13
'__EVENTTARGET': doc("[id='__EVENTTARGET']").val(), # 不知道要不要填
'__EVENTARGUMENT': doc("[id='__EVENTARGUMENT']").val(), # 填的话是跳转前的页面里可以找到
'__LASTFOCUS': doc("[id='__LASTFOCUS']").val(), # 把对应的value拿过来就好
'__VIEWSTATE': doc("[id='__VIEWSTATE']").val(), # 下同
'ctl00$cph_right$e_atschool': e_atschool, # 当天是否在校
'ctl00$cph_right$e_location': e_location, # 当天所在地
'ctl00$cph_right$e_observation': e_observation, # 医学观察情况
'ctl00$cph_right$e_health$0': e_health_0, # 当天健康情况(这个的参数名不知道要不要改)
'ctl00$cph_right$e_temp': e_temp, # 当天实测额温
'ctl00$cph_right$e_describe': e_describe, # 症状、就诊及特殊情况说明
'ctl00$cph_right$e_submit': '提交保存',
'__VIEWSTATEGENERATOR': doc("[id='__VIEWSTATEGENERATOR']").val(),
'__EVENTVALIDATION': doc("[id='__EVENTVALIDATION']").val()

集成代码·

把我写好的脚本发一下(带训练好的模型):https://drive.google.com/file/d/1UuwwaBQ3nOK2iimdQ04-9d2L4Qdvk7wG/view?usp=sharing

用法:

首先在config.py里把自己的账号密码和打卡的表单填好(因为打卡系统的表单每天都变的,所以过几天不知道又要不要改代码了 (╯‵□′)╯︵┻━┻x5),然后运行main.py即可。

config里有个ran_temp就是虚拟体温计了(你懂的)。

由于模型的识别率只有50%,所以会尝试10次验证码的识别,如果都不过的话就会报错并退出,这时可以尝试再次运行或者自己训练个更好的模型hhhh。

正常来说运行完后是这样的,会把每次尝试的验证码,成功后的session和key打印成log(不想要log的可自己改代码)

然后会把post后的页面存成content.html,可以通过页面查看有没有打卡成功,如果成功的话打开content.html就会有打卡成功的弹窗,然后显示打完卡的信息。

Ps:~~由于代码是今天打完卡后才写完的,所以实际结果不知道会不会有问题(逃)~~实测可用

另外给一下我训练时改过的代码,和两个训练好的模型(准确率都差不多的):https://drive.google.com/file/d/1Xqmy6qhNLoLiAhwejj0FEqkisaqMNzgc/view?usp=sharing