众所周知,因为疫情的原因,很多高校和公司都要求员工每日在微信上进行打卡,来汇报自己的当前身体状态和所处地区。但绝大多数情况下,每天打卡的信息其实是不会变的,我们要做的就是进入公众号——自动登录点进打卡页面——完成打卡,这样重复的操作。
这样的操作在手机上需要花费的时间应该不足一分钟,但依旧每天都会有懒得或者忘了进行操作的人。所以就想到能不能用python写一个脚本,在PC端进行自动打卡呢。
(本操作仅提供思路参考,大家还是要重视防疫打卡操作)
以下所有操作均以某高校页面为例
1.代码前准备
由于微信的普及,所以基本各高校和公司每日打卡都是在微信端进行,所以我们需要通过微信找到我们的登录页。
我们最终希望用Requests模拟发包登录,而平时都是直接用微信进行自动登录。显然在只用脚本的情况下没办法实现微信自动登录跳转,所以我们需要先找到能输入账号密码的页面。(此时没有用Cookie是因为实验之后发现,Cookie有效期不到2小时,没办法支撑每日用同一个Cookie登录打卡)
此时,我们通过PC微信找到该链接:http://****app.i****.info/NYDXY/#/,但是如果直接用浏览器打开该链接,会跳转”请用微信客户端打开链接“的页面。
因此,需要想办法绕过该限制,思路有两个:1.通过Fiddler等抓包软件,找找有没有其他登录页面。2.模拟微信浏览器的请求头和Cookie进入。但是正如前文所说,经过尝试思路2,确实能够完成全流程,但该页面Cookie有效期很短,没办法每日均进行自动打卡,因此我们选择尝试思路1,还是通过账号密码登录。
沿着思路1,我们打开Fiddler,启动抓包。然后回到PC微信,和刚才同样的操作进入打卡页面自动登录,再回到Fiddler,发现刚才进入打卡页面的所有操作已经被记录在软件里了。接下来就是通过url和请求头等信息,来判断是否存在其他的登录页面。
经过分析和尝试,前两个链接都会提示400或其他错误,但尝试到第三个链接https://authserver.******.edu.cn/authserver/login时,会跳转到学校的统一登陆页面,同时还在下方发现微信快捷登录图表。通过该页面输入账号密码后,浏览器自动跳转到了我们所需的打卡页面了。此时,我们实现了PC端模拟登录微信打卡页面。
第二步,我们需要看我们打卡到底在浏览器端是如何完成的。在手动进行打卡操作之后,经过浏览器的F12,看到有四个php提供了post操作,而通常我们的表单数据都是由post方法完成的。排除掉之前就存在的第一二个php,所以我们强烈怀疑就是通过这剩余两个php完成的打卡操作。
具体来看发现,最后一个jump.php实际上返回的是个人基本信息,以供自动填充用的,提交的表单也与打卡无关。
但到了倒数第二个jump.php,通过其提交的Form数据,我们发现就是通过这个php我们完成了打卡操作。因此,我们最终的目标,就是通过该php来完成我们的自动打卡。至此,我们的代码前分析全部完成,接下来进入代码环节。
2.代码实现(Cookie+纯requests版本)
( 本文使用requests包进行python代码实现。)
因为我们已经通过浏览器进行过登录,获得了Cookie,所以我们先尝试通过Cookie进行直接打卡。先通过浏览器F12获得Cookie和其余请求头,然后根据需要放在Python文件中。(这里的请求头大家可以选择需要的来放,一般加上Cookie和User-Agent即可,如果失败还可以尝试将User-Agent换为Android或ios的模拟请求)
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36','Cookie': '浏览器上找到的Cookie'
}
然后再根据From Data的数据,按照字典格式一一放进Python文件中去。
最后再尝试post即可。发现最后返回值和浏览器返回值相同,打卡成功。
3.代码实现(账号密码+Selenium+Requests版本)
前文已经实现在知道Cookie的前提下,可以直接通过固定php页面进行表单上传打卡。但是由于Cookie有效期极短,所以我们明显需要有一个每次打卡前自动获取Cookie的方法。
回到一开始的Login页面,同样我们通过F12抓包,看每次登录时提交的表单具体值有些什么。在用户名和密码,我们尝试性的输入123456之后点击提交,会发现在login文件下会POST一个表单,里面所包含的就是每次登录要传给服务器的信息了。
但是从表单我们看到,除了我们填写的账号密码,还有包括lt,execution等信息。经过查找,我们发现这些信息是在加载网页时就自动生成的,所以我们可以通过request.session()的方法进入页面后,再通过正则表达式将所需信息找出来,和账号密码一起形成表单。
但是我们还可以看到,password明显是经过加密处理后的信息,所以我们不能直接明文提交密码,要想办法将密码进行处理。而这个加密后的密码,是我们点击提交后网页自动将我们的明文密码进行的加密,所以大概率就是一个js的处理,因此需要找到网页上加密的js文件。查找login页面和js之后,最终找到是encrypt.js这个文件完成了明文密码的加密。
按逻辑来说,我们此时就可以查找传递该js所需参数,得到加密后的密码,连同其他信息一起post给login就可以完成我们的登录和cookie获取了。但是这个js真是又长又混淆,以现在的水平暂时不能找到其所需参数。所以换了个思路,通过Selenium来模拟登录,直接获取Cookie。
(这里如果可以看懂这个js加密的话,是完全可以不依靠Selenium获取Cookie的,大家有兴趣可以尝试一下)
Selenium就比较简单了,通过分析登录页面,找到需要Input的地方,获取Xpath,再通过Selenium填写,并获取Cookie就可以了。
def get_cookie(url_, user_):driver = webdriver.Chrome()driver.get(url_)driver.find_element_by_xpath('//*[@id="username"]').send_keys(user_['name'])driver.find_element_by_xpath('//*[@id="password"]').send_keys(user_['password'])driver.find_element_by_xpath('//*[@id="casLoginForm"]/p[4]/button').click()sleep(3)cookie_ = driver.get_cookies()[0]driver.quit()return cookie_['name']+'='+cookie_['value']
获取cookie后,和之前操作相同,提交表单即可,完整代码如下。
import requests
from selenium import webdriver
from time import sleepdef get_cookie(url_, user_):driver = webdriver.Chrome()driver.get(url_)driver.find_element_by_xpath('//*[@id="username"]').send_keys(user_['name'])driver.find_element_by_xpath('//*[@id="password"]').send_keys(user_['password'])driver.find_element_by_xpath('//*[@id="casLoginForm"]/p[4]/button').click()sleep(2)cookie_ = driver.get_cookies()[0]driver.quit()return cookie_['name']+'='+cookie_['value']user = {'name': ******', 'password': '******'}
cookie = get_cookie('https://authserver.******.edu.cn/authserver/login?service=http://s******app.i******.info/jinzhi/index.php', user)headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.193 Safari/537.36','Cookie': cookie
}data = {'mymethod': 'POST','myurl': '','id': '','t1': '否','t2': '','t3': '否','stsfyc': '否','stsfycxq': '','dqszdpro': '','dqszdcity': '','dqszdreg': '','sfdgyq': '否','dgyqqt': '[]'
}url = 'http://******app.i******.info/jinzhi/jump.php'
s = requests.session()
response = s.post(url=url, headers=headers, data=data)
html = response.text
print(html)
4.总结
其实在类似需求中,代码部分相对而言占比更少,重要的地方在于找到提交的表单数据和页面链接