python刷课脚本(正⽅教务系统)
最近在学python,正好做⼀个刷课脚本来练习⼀下。熟悉⼀下python的语法。过程中也遇到不少问题。下⾯就把整个过程记录下来。
python刷课脚本实际上是模拟浏览器的访问过程,因为选课需要不断的刷新,所以这⾥可以利⽤脚本⾃动提交表单,解放双⼿
⼀,对教务系统进⾏分析
这⾥⽤chrome的调试功能f12
⾸先是模拟登录。这⾥我们看到有验证码,对于验证码,我试过python的⽂字识别,但是不理想。所以只能⼿⼯输⼊。
1.验证码处理
观察源码
<img id="icode" src="CheckCode.aspx" onclick="reloadcode();" alt="看不清,换⼀张" title="看不清,换⼀张" border="0" >
这个我们可以看出验证码是通过CheckCode.aspx显⽰的。那么我们就把验证码下载来,保存为图⽚就⾏。
这个是⽐较重要的,因为我们登录上去需要保持登录状态。我们先分析⼀下登录的过程
观察登录的post请求
可以看到,服务器没有存cookie到本地。那么服务器如何保存登录状态呢。于是我根据服务器使⽤的asp,去查相应的资料。果然,asp使⽤了,
它利⽤url:222.201.132.117/(julnrg55o3ukpeqcudqgbl45)/default2.aspx中的(julnrg55o3ukpeqcudqgbl45)来保持登录状态。也就是说我们只要使⽤这个url来访问服务器,就可以保持同⼀登录状态。
3.字符编码处理
我们看到上⾯的表单中有
RadioButtonList1:%D1%A7%C9%FA
观察url编码⽅式,可以看出是gb2312的字符进⾏了url编码。由于python的默认编码的是ascii编码,那么如果我们直接对其进⾏url编码,那么得到的字符是错误的。
我⾃⼰的python设置了utf-8编码,utf-8⽤3个字符代表⼀个汉字,所以url编码之后的结果是:
>>> urllib2.quote('有')
'%E6%9C%89'
所以我们需要把中⽂编码为gbk,然后再进⾏url编码
>>> urllib2.quote('有'.decode('utf8').encode('gbk'))
'%D3%D0'
先把‘有’以utf8进⾏解码,然后编码成gbk,最后再进⾏url编码。
如果默认编码⽅式不是utf8,那么可以这样写:str.decode(ding).encode('gbk')
ding表⽰输出的默认编码
4.获取选课链接
观察源码,我们可以发现选课的链接,但是⾥⾯有个xm参数,⽤的是学⽣的名字,⽽且是中⽂,在url出现的中⽂字符都会被url编码,那么我们要记得进⾏转码。由于不同⽤户的选课链接是不⼀样的,在这⾥我们要使⽤正则表达式在page⾥⾯提取出选课的链接。
href=re.findall(r'xf_xsqxxxk.+?N121103',Page())
其中的学⽣姓名要提取出来,进⾏转码
word=re.findall(u"[\u4e00-\u9fa5]+",str)#⽤于提取字符串中的中⽂
url=re.sub(u"[\u4e00-\u9fa5]+",urllib2.deGBK(chinese)),href[0].decode('utf8'))#替换url中的中⽂
4.接下来获取所有课程
\
<input name="dpDataGrid2:txtPageSize" type="text" value="6" onchange="__doPostBack('dpDataGrid2$txtPageSize','')" language="javascript" id="dpDataGrid2_txtPageSize" class="width30">
观察源码,监听到输⼊框的数据有变化就会刷新页⾯。
以下是提交的数据表单,
'__EVENTTARGET':'dpkcmcGrid:txtPageSize',
'__EVENTARGUMENT':'',
'ddl_ywyl':'',
'__VIEWSTATE':'',#这个viewstate简直⽆⼒吐槽,起码有上万个字符,编辑器都装不下,最后我⽤正则在⽹页源码⾥⾯提取,添加进请求⾥⾯。
'ddl_kcxz':'',
'ddl_kcgs':'',
'ddl_xqbs':'2',
'ddl_sksj':'',
'TextBox1':'',
'dpkcmcGrid:txtChoosePage':'1',
'dpkcmcGrid:txtPageSize':'120'
5,获取到所有课程列表之后
我们先观察提交选课的数据表单
'__EVENTTARGET':'',
'__EVENTARGUMENT':'',
'ddl_ywyl':'',
'__VIEWSTATE':ViewState(page),
'ddl_kcxz':'',
'ddl_kcgs':'',
'ddl_xqbs':'2',
'ddl_sksj':'',
'TextBox1':'',
'dpkcmcGrid:txtChoosePage':'1',
'dpkcmcGrid:txtPageSize':'120',
code:'on',#这⾥的code是课程代码,每门课都不⼀样
'Button1':deGBK('  提交  '),
'dpDataGrid2:txtChoosePage':'1',
'dpDataGrid2:txtPageSize':'120'
现在我们要拿到课程代码,同样是⽤正则表达式
我的思路是先得到所有课程列表,然后在匹配课程,得到课程代码
⼆,总结
整个写的过程,遇到的主要是两个问题,⼀个是编码问题,另⼀个是正则表达式的使⽤。
从理论上看这个脚本⽐较简单,但实际做起来会遇到不少问题。
补充,在研究如何⾃动识别验证码的时候,发现正⽅这个验证码不好处理,后来发现原来可以跳过验证码登录,原理是如果你不请求验证码就不⽤在表单中输
⼊,后台就不验证,看起来是程序员偷懒了,以为浏览器⼀定会请求验证码,但是如果不是浏览器呢,⼀个⼩⼩的脚本就轻松绕过了验证码,再次印证安全领域
的⼀句⽼话:永远不要相信⽤户的输⼊。这也是⼀个⽐较严重的漏洞,因为绕过就可以暴⼒破解账号密码了。
关于机器识别图像,有精⼒再去研究。现在先投机取巧。
最后放上源码
#!/usr/bin/env python
# coding=utf-8
__metaclass__=type
import urllib2
import urllib
import time
import sys
import re
reload(sys)
sys.setdefaultencoding('utf-8')
class Spider:
def __init__(self):
self.baseUrl='222.201.132.117/'
self.CheckCodeUrl='CheckCode.aspx'
self.PostUrl='default2.aspx'
self.Agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/40.0.2214.111 Chrome/40.0.2214.111 Safari/537.3        #开启debug
httpHandler = urllib2.HTTPHandler(debuglevel=0)
self.opener = urllib2.build_opener(httpHandler)
self.attempt=0
self.setRealUrl()
def setRealUrl(self):
'获取真实⽹站,包含cookie'
request = urllib2.Request(self.baseUrl)
request.add_header('User-Agent', self.Agent)
urlList=self.opener.open(request).geturl().split('/')
urlList.pop()
self.baseUrl='/'.join(urlList)+'/'
def getCheckCode(self):
'获取验证码'
#设置agent
#设置agent
request = urllib2.Request(self.baseUrl+self.CheckCodeUrl)
request.add_header('User-Agent', self.Agent)
#下载验证码
file=self.opener.open(request)
ad()
path='code.gif'
CheckCode=open(path,'wb')
CheckCode.write(pic)
CheckCode.close()
def setInfo(self):
'设置⾝份信息'
#学号
self.uid=raw_input('输⼊学号:')
#密码
self.pwd=raw_input('输⼊密码:')
#验证码
#CheckCode()
#de=raw_input('输⼊验证码:(验证码在本软件所在⽂件夹)')
def getPage(self):
'登录教务系统,并且获取⽹页'
postdata=urllib.urlencode({
'__VIEWSTATE':'dDwyODE2NTM0OTg7Oz5VGnjXV87Z19Dm3QbgRgvcptEYyA==',                'txtUserName':self.uid,
'TextBox2': self.pwd,
'txtSecretCode':de,
'RadioButtonList1':deGBK('学⽣'),#⽤gbk编码
'Button1':'',
'lbLanguage':'',
'hidPdrs':'',
'hidsc':''
})
headers={
'User-Agent':self.Agent,
'Referer':self.baseUrl
}
req = urllib2.Request(
url = self.baseUrl+self.PostUrl,
data = postdata,
headers = headers
)
try:
response = urllib2.urlopen(req)
ad().decode('gbk').encode('utf-8')#对gbk编码的数据进⾏转码
return page
except urllib2.URLError, e:
self.run()
if hasattr(e, 'code'):
print 'The server couldn\'t fulfill the request.'
print 'Error code: ', e.code
elif hasattr(e, 'reason'):
print 'We failed to reach a server.'
print 'Reason: ', e.reason
else:
print 'No exception was raised.'
# everything is fine
def encodeGBK(self,str):
'对字符串⽤gbk进⾏编码'
return str.decode('utf-8').encode('gbk')
def getSelect(self):
'获取选课的⽹址'
href=re.findall(r'xf_xsqxxxk.+?N121103',Page())
Chinese(href[0].decode('utf8'))
url=re.sub(u"[\u4e00-\u9fa5]+",urllib2.deGBK(chinese)),href[0].decode('utf8'))        return url
def getChinese(self,str):
'从字符串获取中⽂'
word=re.findall(u"[\u4e00-\u9fa5]+",str)
return word[0]
def getViewState(self,page):
'从⽹页中获取viewstate'
return re.search('<input type="hidden" name="__VIEWSTATE" value="(.+?)" />',page).group(1)
def getList(self):
'获取全部课程列表'
url=self.Select()
request=urllib2.Request(url)
request.add_header('User-Agent', self.Agent)
request.add_header('Referer', self.baseUrl+'/xs_main.aspx?xh='+self.uid)
response = self.opener.open(request)
ad().decode('gbk').encode('utf-8')#对gbk编码的数据进⾏转码
postdata=urllib.urlencode({
'__EVENTTARGET':'dpkcmcGrid:txtPageSize',
'__EVENTARGUMENT':'',
'ddl_ywyl':'',
'__VIEWSTATE':ViewState(page),
'ddl_kcxz':'',
'ddl_kcgs':'',
'ddl_xqbs':'2',
'ddl_sksj':'',
'TextBox1':'',
'dpkcmcGrid:txtChoosePage':'1',
'dpkcmcGrid:txtPageSize':'120'
})
headers={
'User-Agent':self.Agent,
'Referer':url
}
request = urllib2.Request(
url = url,
data = postdata,url编码和utf8区别
headers = headers
)
try:
response = self.opener.open(request)
ad().decode('gbk').encode('utf-8')#对gbk编码的数据进⾏转码
return page,url
except urllib2.URLError:
self.run()
def postCourse(self,course,teacher,date):
'获取课程信息,并且提交'
List()

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。