其实之前有写过一篇帖子了 旧帖地址(知乎)
在之前使用教务系统的过程中,偶然一次发现登上教务系统后再退出来的后的登录网址竟然不需要验证码,想着之前有写过教务系统的爬虫模拟登录,没验证码的岂不是更好干(之前那次折腾了好久hhh,后面还是用selenium实现的成绩爬取,相比直接爬取,selenium的性能还是比不上毕竟selenium本来就是用来搞自动化的hhh),然后今天闲着就重写了相同的功能并实现了成绩的爬取。
简单写一下思路:
- 百度直接搜索出来的教务系统网址是需要验证码的,登录上去之后,再退出来就得到不需要验证码的登录网址
-
点开我们可以发现是需要验证码的
-
登录上去之后再退出,下图箭头所指:
-
退出之后,是不是神奇的发现验证码不见了(见下图)哈哈哈,这就是我们要的。
-
- 正式开始我们的分析
- 要分析,肯定是得要看网页源代码滴(鼠标右击查看网页源代码),当然还有F12提供的开发者调试工具
2.让我们来看看网页源代码- 我们直接找到表单位置,可以注意到当点击登陆时会触发函数
submitForm1()
==> 好嘞,这不好办吗?接下来就让我们来顺腾摸瓜 - 我们复制函数的名称,并直接ctrl+F查找到函数的位置
- 仔细看看函数,不难发现函数取得到了表单中我们输入的账号密码后都调用了
encodeInp()
函数,然后组成了一个用变量encoded
保存的字符串。
3.输入一个错误的账号密码,点击登录看看开发者调试工具里面network的情况 - 我们可以注意到红框中的那个post请求
- 点开后,我们可以看到
cookie
,也不难发现encoded
字样,于是我们可以猜测提交的表单中就只有这一个数据,且是登录验证的唯一一个数据(比之前那个贴简单太多了,自己流下了心酸的泪水) - 接着分析,关掉浏览器,重新打开这个不要验证码的网址(重新获得服务端分配的一个cookie),输入跟刚刚错误的账号密码再看看那个
encoded
有没有变化,因为根据自己上次的经验encoded
是根据每次分配的cookie不同而变化的.....然后我们发现是没有变化的!!!是没有变化的!!!是没有变化的!!!(重要的事情说三遍,这还不好办嘛???)
- 我们直接找到表单位置,可以注意到当点击登陆时会触发函数
- 继续顺腾摸瓜(战术性滑稽脸)
- 上面说到点击网页上的登录按钮时会触发函数
submitForm1()
,然后在这个函数中又会调用对账号密码进行加工(其实是加密hhh)的encodeInp()
函数,我们来找到encodeInp()
函数的位置,相同的操作CTRL+F,结果发现函数不在这儿。 - 既然函数不在该页面的源代码中,肯定是引进来的js文件,我们在html的头部去找引入的文件,结果发现只引入了一个js文件,那么我们可以肯定
encodeInp()
函数就在该文件中了。 - 在点开的
conwork.js
文件中搜素encodeInp()
函数,找到了!!!
- 上面说到点击网页上的登录按钮时会触发函数
- 仔细看看
conwork.js
文件- 看了好一会儿后,发现自己根本看不懂,想着反正是调用的这个文件中的函数,那不如自己去改改代码调用
encodeInp()
函数,看能不能调用成功,输出跟调试工具中一样的encoded
,如果是一样的我们目的就达到了,加密的过程就是在submitForm1()
中调用encodeInp()
函数,且跟cookie没关系,也就是说每个人的账号密码最后经过加密出来的字符串每次都是固定的。 - 这里我就用了自己的账号密码,结果可以发现是一样的。
- 看了好一会儿后,发现自己根本看不懂,想着反正是调用的这个文件中的函数,那不如自己去改改代码调用
- 要分析,肯定是得要看网页源代码滴(鼠标右击查看网页源代码),当然还有F12提供的开发者调试工具
- 接下来我们就可以写python代码了
- 我们先把
conwork.js
文件的代码和经过更改的且python的execjs库能调用的submitForm1()
函数放在一个同一个js文件里面(这里我把函数名称换了一下,换成了encode
) - 写代码的思路我就不说了,代码贴在下面了,不过这次我倒是惊奇的发现不加header都能直接登陆。
- 我们先把
import requests
import execjs
from bs4 import BeautifulSoup
class Login:
def __init__(self):
self.url = 'http://jiaowu2.hufe.edu.cn/jsxsd/'
header = {
"Content-Type": "text/html;charset=utf-8",
"Vary": "Accept-Encoding"
}
self.session = requests.session()
self.session.get(self.url,headers=header)
self.username = '****'#账号
self.password = '****'#密码
self.GetEncoded()
self.login()
def GetEncoded(self):#该函数获取加密后的字符串
with open(r'conwork.js', encoding='utf-8') as f:
js = execjs.compile(f.read())
self.encoded = js.call('encode', self.username,self.password)
f.close()
return self.encoded
def login(self):
postData = {
'encoded': self.encoded.strip() # 账号密码加密后的东西
}
return self.session.post('http://jiaowu2.hufe.edu.cn/jsxsd/xk/LoginToXk',data=postData)
def GetScore(self):#爬取全部课程成绩
response = self.session.get('http://jiaowu2.hufe.edu.cn/jsxsd/kscj/cjcx_list')
grade_html = response.text
soup = BeautifulSoup(grade_html, 'html.parser')
tr_lable = soup.find_all("tr")[1:]
all_rows = [] # 该列表存放每一个课程的详细信息。
all_rows.append([i.text for i in soup.find_all("th")])
for tr in tr_lable:
tds = tr.find_all('td')
row = []
for td in tds:
row.append(td.text)
all_rows.append(row)
return all_rows
test = Login()
test.login()
print(test.GetScore())#打印爬取的成绩
最后,代码肯定有很多写的不够好的地方,望大家多多包含。嘿嘿嘿~