前语:
最近校园开始选课,可是假如选课时刻与自己的事情抵触,这时分就能够运用Python脚本自助抢课,不用再担心手速跟不上,抢啥中啥。不用再接受抢不中想去的课,最终被调剂到各种“灭绝师太”手中苦苦挣扎的苦楚。
正文:
抢课的第一步即是模仿登录,需求模仿登录后保存登录信息然后再进行操作。
并且整个流程是比较简单,这是因为正方教务体系是比较旧的,全文的IP地址部分遮挡,请换成你们校园的IP地址。
测验登录
首先咱们翻开校园的教务体系,随意输入,然后提交表单,翻开Chrome的开发者工具中的Network准备抓包
把css 图片之类的过滤掉,发现了default.aspx这个东西
假如你们校园教务体系不运用Cookie则会是这样
咱们能够发现,真实的恳求地为
http://110.65.10.xxx/(bdq1aj45lpd42o55vqpfgpie)/default2.aspx
随后咱们发现这个网址括号围起来的一串信息有点怪异,并且每次进入的时分信息都不一样,通过资料查询,这是一种ASP.NET不运用Cookie会话办理的技能。
不运用 Cookie 的 ASP.NET 会话办理
那这样就很好办了,咱们只需求登录时记录下这个数据即可坚持登录状况。
通过测试发现,咱们能够随意假造一个会话信息即可一向坚持登录状况,可是为了表现模仿登录的科学性,咱们需求先获取该会话信息。
假如你们校园教务体系运用Cookie则会是这样
服务器会回来一个Cookie值,然后在本地保存,这与下面的会不相同。
获取会话信息(不运用Cookie)
这儿咱们要运用requests库,并且要假造header的UA信息
通过测试发现,咱们只访问校园的IP地址,会主动重定向至有会话信息的网址,所以咱们先访问一下IP地址。
class Spider:
def __init__(self, url):pp
self.__uid = ''
self.__real_base_url = ''
self.__base_url = url
self.__headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36',
}
def __set_real_url(self):
request = requests.get(self.__base_url, headers=self.__headers)
real_url = request.url
self.__real_base_url = real_url[:len(real_url) - len('default2.aspx')]
return request
上面获取的url即为带有会话信息的网址,保存的url格局为
your_ip/(bdq1aj45lpd42o55vqpfgpie)/
保存为这样的格局是因为咱们要访问其他地址
获取会话信息(运用Cookie)
有些校园的教务体系是运用Cookie的,咱们只需求首次get恳求时保存Cookie即可,然后此后一向运用该cookie
def get_cookie():
request = requests.get('http://xxx.xxx.xxx.xxx') #以某教务体系为比如
cookie = requets.cookie
return cookie
而requests中运用Cookie很简单
只需求这样
def use_cookie(cookie):
request = requests.get('http://xxx.xxx.xxx.xxx',cookie=cookie)
因为咱们校园采用的是无Cookie方案,所以下面的代码均没有发送Cookie,假如你的校园采用了Cookie,只需求像我上面这样发送Cookie就行了。
而假如你们校园运用Cookie,就不用获取带有会话信息的地址了,直接存储Cookie即可。
验证码的处理
剖析r回来的文本信息
发现验证码的标签的资源地址为 src=”https://juejin.im/post/7068163188050624542/CheckCode.aspx” ,咱们能够直接requests然后下载验证码图片,下载图片的一种优雅的方式如下
def __get_code(self):
request = requests.get(self.__real_base_url + 'CheckCode.aspx', headers=self.__headers)
with open('code.jpg', 'wb')as f:
f.write(request.content)
im = Image.open('code.jpg')
im.show()
print('Please input the code:')
code = input()
return code
上面的代码把图片保存为code.jpg,Python有一个Image模块,能够实现主动翻开图片
这样验证码就展示出来了,咱们人工输入或许转入打码平台皆可
登录数据的结构
这是上面抓的登录post的数据包,
发现有信息无法被解码,应该是gb2312编码,查看解码前的编码
然后将不能解码的代码复制能够解码的当地
发现%D1%A7%C9%FA编码解码后为学生
这也就对应了学生选项的登录
学号和暗码和验证码能够清楚明了地知道是哪些信息,可是咱们发现有__VIEWSTATE这一项
查找一下,这是一个表单躲藏信息,咱们能够用BeautifulSoup库解析能够得出该一项数据的值
这是完整的登录数据包,
def __get_login_data(self, uid, password):
self.__uid = uid
request = self.__set_real_url()
soup = BeautifulSoup(request.text, 'lxml')
form_tag = soup.find('input')
__VIEWSTATE = form_tag['value']
code = self.__get_code()
data = {
'__VIEWSTATE': __VIEWSTATE,
'txtUserName': self.__uid,
'TextBox2': password,
'txtSecretCode': code,
'RadioButtonList1': '学生'.encode('gb2312'),
'Button1': '',
'lbLanguage': '',
'hidPdrs': '',
'hidsc': '',
}
return data
登录
假如登录完成了,如何判别是否登录成功呢?咱们从登录成功回来的界面发现有姓名这一标签,而咱们等一下也是需求学生姓名,所以咱们用这个依据来判别是否登录成功。
代码如下,进行了验证码用户名和暗码的提示信息判别
def login(self,uid,password):
while True:
data = self.__get_login_data(uid, password)
request = requests.post(self.__real_base_url + 'default2.aspx', headers=self.__headers, data=data)
soup = BeautifulSoup(request.text, 'lxml')
try:
name_tag = soup.find(id='xhxm')
self.__name = name_tag.string[:len(name_tag.string) - 2]
print('欢迎'+self.__name)
except:
print('Unknown Error,try to login again.')
time.sleep(0.5)
continue
finally:
return True
获取选课信息
接下来便是获取选课信息了,这儿咱们以校公选课为比如,点击进去,进行抓包,headers没有什么好留意的,咱们只用关注get发送的包即可
发现有学号与姓名与gnmkdm这一项,姓名咱们需求编码为gb2312的方式才干进行传送
这儿咱们留意headers需求新增Referer项也便是当时访问的网址,才干进行恳求
def __enter_lessons_first(self):
data = {
'xh': self.__uid,
'xm': self.__name.encode('gb2312'),
'gnmkdm': 'N121103',
}
self.__headers['Referer'] = self.__real_base_url + 'xs_main.aspx?xh=' + self.__uid
request = requests.get(self.__real_base_url + 'xf_xsqxxxk.aspx', params=data, headers=self.__headers)
self.__headers['Referer'] = request.url
soup = BeautifulSoup(request.text, 'lxml')
self.__set__VIEWSTATE(soup)
留意到上面有一个设置VIEWSTATE值的函数,这儿等下在选课结构数据包的时分会讲
模仿选课
随意选一门课,然后提交,抓包,看一下有什么数据发送
前三个值能够在原网页中input标签中找到,因为前两项为空,就不获取了,而第三项咱们运用soup解析获取即可,因为这个操作是每恳求一次就改变的,咱们写成一个函数,每次恳求完成就设置一次。
def __set__VIEWSTATE(self, soup):
__VIEWSTATE_tag = soup.find('input', attrs={'name': '__VIEWSTATE'})
self.__base_data['__VIEWSTATE'] = __VIEWSTATE_tag['value']
而其他数据,咱们通过查找呼应网页就能够知道他们是干什么用的,这儿我只阐明咱们要用的数据。
TextBox1为查找框数据,咱们能够用这个来查找课程,dpkcmcGrid:txtPageSize为一页显示多少数据,通过测试,服务器最多呼应200条。
值得留意的是ddl_xqbs这个校区数据信息,我地址的校区的数字代号为2,也许不同校园设置有所不同,需求自己设置一下,也能够从网页中获取
下面是根底数据包,因为咱们查找课程与选择课程都要运用这个根底数据包,所以咱们直接在init函数里边新增
self.__base_data = {
'__EVENTTARGET': '',
'__EVENTARGUMENT': '',
'__VIEWSTATE': '',
'ddl_kcxz': '',
'ddl_ywyl': '',
'ddl_kcgs': '',
'ddl_xqbs': '2',
'ddl_sksj': '',
'TextBox1': '',
'dpkcmcGrid:txtChoosePage': '1',
'dpkcmcGrid:txtPageSize': '200',
}
然后咱们关注一下这条数据,咱们查找一下,发现这是课程的提交选课的代码,所以咱们也能够直接从网页中获取,而on表示选项被选上
kcmcGrid:_ctl2:xk:'on'
查找课程
课程有许多信息,比如姓名,上课时刻,地址,这些东西确认好了才知道选的是哪门课,所以咱们先新建一个类来存储信息
class Lesson:
def __init__(self, name, code, teacher_name, Time, number):
self.name = name
self.code = code
self.teacher_name = teacher_name
self.time = Time
self.number = number
def show(self):
print('name:' + self.name + 'code:' + self.code + 'teacher_name:' + self.teacher_name + 'time:' + self.time)
有了这个类,咱们就能够进行查找课程了,详细代码看下面代码,解析网页内容就不细讲了。
def __search_lessons(self, lesson_name=''):
self.__base_data['TextBox1'] = lesson_name.encode('gb2312')
request = requests.post(self.__headers['Referer'], data=self.__base_data, headers=self.__headers)
soup = BeautifulSoup(request.text, 'lxml')
self.__set__VIEWSTATE(soup)
return self.__get_lessons(soup)
def __get_lessons(self, soup):
lesson_list = []
lessons_tag = soup.find('table', id='kcmcGrid')
lesson_tag_list = lessons_tag.find_all('tr')[1:]
for lesson_tag in lesson_tag_list:
td_list = lesson_tag.find_all('td')
code = td_list[0].input['name']
name = td_list[1].string
teacher_name = td_list[3].string
Time = td_list[4]['title']
number = td_list[10].string
lesson = self.Lesson(name, code, teacher_name, Time, number)
lesson_list.append(lesson)
return lesson_list
进行选课
选课咱们只要将lesson_list传入即可,这便是咱们之前创立的Lesson类的实例的列表,’Button’的内容为’ 提交 ‘,这两边各有一个空格,完事后咱们能够进行发送恳求进行选课。
这儿咱们用正则提取了错误信息,比如选课时刻未到、上课时刻抵触这些错误信息来提示用户,咱们还解析了网页的已选课程,这儿也不细讲了,都是根底的网页解析。
def __select_lesson(self, lesson_list):
data = copy.deepcopy(self.__base_data)
data['Button1'] = ' 提交 '.encode('gb2312')
for lesson in lesson_list:
code = lesson.code
data[code] = 'on'
request = requests.post(self.__headers['Referer'], data=data, headers=self.__headers)
soup = BeautifulSoup(request.text, 'lxml')
self.__set__VIEWSTATE(soup)
error_tag = soup.html.head.script
if not error_tag is None:
error_tag_text = error_tag.string
r = "alert\('(.+?)'\);"
for s in re.findall(r, error_tag_text):
print(s)
print('已选课程:')
selected_lessons_pre_tag = soup.find('legend', text='已选课程')
selected_lessons_tag = selected_lessons_pre_tag.next_sibling
tr_list = selected_lessons_tag.find_all('tr')[1:]
for tr in tr_list:
td = tr.find('td')
print(td.string)
总结
这次咱们完成了模仿正方教务体系选课的进程,因为这个教务体系技能比较陈旧,所以比较好弄,事实上抢课的时分用Fiddler即可完成操作,因为咱们只需求提前登录然后记录网址即可。
因为不同校园的正方教务体系有可能不同,所以上面许多细节都是需求修改的。
以上便是本文的所有内容了,欢迎点赞支持~大家需求完整的项目源码的能够私信我哟!
这行蓝色的字体也行wo~