刚刚进入假期,学校开放了期末成绩查询,奈何正方教务的各种卡顿。很多学弟学妹问有没有好办法能够解决,闲来无事写了一段自动获取的爬虫程序练练手。
环境
- Python3.7
- Jupyter
主要库
- requests
- BeautifulSoup
- PIL
- pandas
一、分析及获取登录提交数据
首先观察网页,由于这个地方比较简单,我们就使用Chrome的开发者工具中的Network抓包。
把css 图片之类的过滤掉,发现了包含学号、密码等内容的表单被提交到default2.aspx
分析POST过去的字段
总共提交了9个参数(因学校而异,不过关键字段都差不多),其中txtUserName是学号、TextBox2是密码、RadioButtonList1是学生选项(登录身份选项),还有个txtSecretCode是验证码。
除了这些常规数据外,我发现还有__VIEWSTATE这种特殊字段,这里做一个简要介绍。
__VIEWSTATE:
ViewState是 http:// ASP.NET 中用来保存WEB控件回传时状态值一种机制。在WEB窗体(FORM)的设置为runat="server",这个窗体(FORM)会被附加一个隐藏的属性_VIEWSTATE。_VIEWSTATE中存放了所有控件在ViewState中的状态值。
ViewState是类Control中的一个域,其他所有控件通过继承Control来获得了ViewState功能。它的类型是system.Web.UI.StateBag,一个名称/值的对象集合。
当请求某个页面时, http:// ASP.NET 把所有控件的状态序列化成一个字符串,然后做为窗体的隐藏属性送到客户端。当客户端把页面回传时, http:// ASP.NET 分析回传的窗体属性,并赋给控件对应的值。
现在我们知道这个字段肯定是不可缺少的,那么它们可以从哪获取到呢?
我们右键查看网页的源代码,在源代码中发现了type为hidden的输入框,它们的值正是我们所需要的
在这个地方,我们可以构造一个login_info字段作为登录数据:
# post的登录数据
login_info = {
"__VIEWSTATE": viewState,
"txtUserName": user,
"TextBox2": pwd,
"txtSecretCode": checkCode,
"RadioButtonList1": "%D1%A7%C9%FA",#学生选项
"Button1": "",
"lbLanguage": ""
}
对于值的获取这里采用BeautifulSoup这个库来解析
re
这样我们就获得了能够提交的数据字段,可以进行登录操作了,验证码部分我们在后面单独介绍。
二、学生个人成绩信息获取
我们将获取好的字段作为登录数据提交至http://******/default2.a0spx,这里中间是每个学校有所不同,需要进行修改。
头部部分构建
#头部部分构建
header = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
"Referer": "http://218.56.144.61/",
"Host": "218.56.144.61",
"Cache-Control": "max-age=0"
}
现在我们可以获取到学生个人页面的html信息
req = requests.session().post(url='http://******/default2.a0spx', data=data, headers=header)
geren_html = req.text
#print(geren_html)
用简单的正则写了一个欢迎语来表示登录成功(常规骚操作)
z_name = re.compile('<span id="xhxm">(.*?)</span></em>',re.S)
name = re.findall(z_name,geren_html)
print('登录成功!'+name[0],'欢迎您')
之后观察个人成绩这个位置的源码和抓取的包
这里虽然有链接但是如果用header的话...
就会这样,所以我们还是老老实实一步步来。
还是先分析包
我们可以直接找到成绩查询的url:http://*******/xscjcx.aspx?xh=xxxxxx&xm=xxx&gnmkdm=N121605
此URL中xh是学号,xm是经过URL编码的学生姓名。
后面我们要做的和前面所讲的差不多,首先要通过GET方法获取页面源代码,从中取得__VIEWSTATE的值,然后再次POST过去。
data={
'btn_zcj':'%C0%FA%C4%EA%B3%C9%BC%A8',#学年成绩:btn_xn 历年成绩:btn_zcj
'ddlXN':'',
'ddlXQ':'',
'__EVENTVALIDATION': '',
'__EVENTTARGET':'',
'__EVENTARGUMENT' :'',
'__VIEWSTATE':'',
'hidLanguage':'',
'ddl_kcxz':'',
}
#生成绩页面获取关键字段
cj_html_1=S.get('http://*****/xscjcx.aspx?xh='+xh+'&xm='+xm+'&gnmkdm=N121605',headers=header)
soup=BeautifulSoup(cj_html_1.text,'lxml')
#print(soup)
value3=soup.find('input', attrs={'name': '__VIEWSTATE'})['value']
data['__VIEWSTATE']=value3
cj_html_2 = S.post('http://218.56.144.61/xscjcx.aspx?xh='+xh+'&xm='+xm+'&gnmkdm=N121605',data=data,headers=header)
#print (cj_html_2.text)
我们就获取到了包含学生的个人成绩信息的网页源码
之后进行简单的清洗和处理就OK,这部分是一个见仁见智的问题,我们就不展开说明了。
处理之后进行下步分析或者直接导出都没问题
在使用爬虫访问的时候记得一定要留一点余地
import time
time.sleep(1)
毕竟就算着急也尽量不要给别人添麻烦嘛~
三、验证码自动处理
验证码自动识别这一块,第三方的pytesseract库很方便,但是识别率感人。
import pytesseract
from PIL import Image
image = Image.open('vcode.png')
vcode = pytesseract.image_to_string(image)
print (vcode)
因此我们使用百度提供的文字识别api
此处特别感谢本专业一位学长的提醒、指导以及提供的部分源码。
具体实现方式参考
python调用百度通用文字识别接口进行验证码识别 - IT技术讨论 - CSDN博客
在进行识别前要先对图片进行预处理,提高识别效率。
二值化
# 二值化算法
def binarizing(img,threshold):
pixdata = img.load()
w, h = img.size
for y in range(h):
for x in range(w):
if pixdata[x, y] < threshold:
pixdata[x, y] = 0
else:
pixdata[x, y] = 255
return img
转化为灰度图
# 转化为灰度图
image = image.convert('L')
去线
# 去除干扰线算法
def depoint(img): #input: gray image
pixdata = img.load()
w,h = img.size
for y in range(1,h-1):
for x in range(1,w-1):
count = 0
if pixdata[x,y-1] > 245:
count = count + 1
if pixdata[x,y+1] > 245:
count = count + 1
if pixdata[x-1,y] > 245:
count = count + 1
if pixdata[x+1,y] > 245:
count = count + 1
if count > 2:
pixdata[x,y] = 255
return img
处理完毕后使用接口即可获得准确率较高一些的识别结果
总结
1、由于已经过了查询成绩的高峰期,因此没能进行登录崩溃的情况测试。具体思路大概是提取崩溃提示页面中的关键词然后返回进行继续访问。
2、由于时间有限,验证码识别方面仅仅考虑了便捷性。在后期可以考虑使用有监督的学习方式训练一个自己的模型。
3、本案例只是一个小实验,之后进行小规模的调整即可完成抢课等更多元的功能。
最后,
爬虫有风险,使用需谨慎~
以上
有帮助的话点个赞再走吧~
完整源码Github地址:
Vinne6/zfjw_progithub.com相关引用:
用 Python 实现模拟登录正方教务系统抢课blog.csdn.net Python模拟登陆正方教务系统并抓取成绩单 - lovealways - 博客园www.cnblogs.com