0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Python也有这么强大的用处哦!用Python脚本自助抢课

马哥Linux运维 来源:未知 作者:李倩 2018-07-06 11:20 次阅读

最近学校开始选课,但是如果选课时间与自己的事情冲突,这时候就可以使用Python脚本自助抢课,抢课的第一步即是模拟登录,需要模拟登录后保存登录信息然后再进行操作。

而且整个流程是比较简单,这是因为正方教务系统是比较旧的,全文的IP地址部分遮挡,请换成你们学校的IP地址。

尝试登录

首先我们打开学校的教务系统,随便输入,然后提交表单,打开Chrome的开发者工具中的Network准备抓包

把css 图片之类的过滤掉,发现了default.aspx这个东西

如果你们学校教务系统不使用Cookie则会是这样

我们可以发现,真实的请求地址为http://110.65.10.xxx/(bdq1aj45lpd42o55vqpfgpie)/default2.aspx

随后我们发现这个网址括号围起来的一串信息有点诡异,而且每次进入的时候信息都不一样,经过资料查询,这是一种http://ASP.NET不使用Cookie会话管理的技术。

不使用 Cookie 的 ASP.NET 会话管理

那这样就很好办了,我们只需要登录时记录下这个数据即可保持登录状态。

经过测试发现,我们可以随便伪造一个会话信息即可一直保持登录状态,但是为了体现模拟登录的科学性,我们需要先获取该会话信息。

如果你们学校教务系统使用Cookie则会是这样

服务器会返回一个Cookie值,然后在本地保存,这与下面的会不相同。

获取会话信息(不使用Cookie)

这里我们要使用requests库,并且要伪造header的UA信息

经过测试发现,我们只访问学校的IP地址,会自动重定向至有会话信息的网址,所以我们先访问一下IP地址。

classSpider:

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="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):

whileTrue:

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:

returnTrue

获取选课信息

接下来就是获取选课信息了,这里我们以校公选课为例子,点击进去,进行抓包,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'

搜索课程

课程有很多信息,比如名字,上课时间,地点,这些东西确定好了才知道选的是哪门课,所以我们先新建一个类来存储信息

classLesson:

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)

returnself.__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

ifnot error_tag isNone:

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即可完成操作,因为我们只需要提前登录然后记录网址即可。

由于不同学校的正方教务系统有可能不同,所以上面很多细节都是需要修改的。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • python
    +关注

    关注

    51

    文章

    4675

    浏览量

    83466
  • 脚本
    +关注

    关注

    1

    文章

    372

    浏览量

    14635

原文标题:用 Python 搞定正方教务系统之抢课篇

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于Python脚本的R语言的函数

    本文介绍了采用创建一个Python脚本,用该脚本模仿R风格的函数的方法来方便地进行统计。 是用R语言还是用Python语言?这是一个旷日持久的争论。在此,我们可以尝试采用折中路线:创建
    的头像 发表于 10-12 09:33 1858次阅读
    基于<b class='flag-5'>Python</b><b class='flag-5'>脚本</b>的R语言的函数

    10个杀手级的Python自动化脚本

    今天浩道跟大家分享10个日常工作中用到的python自动化脚本。让你感受一番python简单强大之处!
    发表于 11-28 11:07 521次阅读

    13个用于日常编程的高级Python脚本

    今天浩道跟大家分享13个用于日常编程的高级python脚本。让你不会写脚本的也能感受到其魅力!
    发表于 12-09 11:47 512次阅读

    通过Python脚本实现WIFI密码的自动猜解

    本文将记录学习下如何通过 Python 脚本实现 WIFI 密码的自动猜解。
    的头像 发表于 01-25 10:46 704次阅读
    通过<b class='flag-5'>Python</b><b class='flag-5'>脚本</b>实现WIFI密码的自动猜解

    Python这么火,那有啥优势呢?

    创建功能强大且整洁的应用程序。 而Python则不能自夸拥有如此多的框架。的最多的是Django和Flask。 但是,我们可以向你保证,由于Python社区的不断发展,这种局面很难快
    发表于 05-09 13:30

    Python与C#对比

    Python和C#语言都是功能强大的有前途的编程语言,那么两者有什么区别呢?1. Python原本就被设计成类似英语表达一样,所以语法简单,没有像句法括号和大量的修饰词,易读易学;
    发表于 05-14 17:14

    python写一个建模的脚本-微带天线的模型建立与仿真的过程搞定了

    大多数联合仿真的脚本都是使用的Matlab进行编程,网上也有不少现成的api,因为对python比较熟悉,且python除了数值计算其他的功能也相当
    发表于 02-19 16:51

    0基础入门Python爬虫实战

    情况及薪资水平……诸如此类,不胜枚举。只要是信息,只要你能看到它,想批量获取它,都可以使用爬虫来实现。但,自学爬虫存在不少障碍,你可能会有这些困扰:看了书,上了,却依然不会爬虫自学遇到困难,一卡就是半天
    发表于 07-25 09:28

    如何在 IIS 中执行 Python 脚本

    如何在 IIS 中执行 Python 脚本 Python 是一种解释脚本语言,概念类似 Microsoft Visual Basic Scripting Edition (VBSc
    发表于 02-23 15:13 1139次阅读

    如何使html网页与python脚本进行通信

    现在运行python脚本并转到http://localhost/或http://localhost/并开始按下按钮,您应该会在python脚本上看到输入。
    的头像 发表于 11-04 10:12 7609次阅读

    快速安装 Python 解释器

    1.1【环境】快速安装 Python 解释器 Python 是一门解释性脚本语言,因此要想让你编写的代码得以运行,需要先安装 CPython 解释器。 根据你电脑的系统以及位数不同,安装步骤
    的头像 发表于 02-15 16:57 1949次阅读
    快速安装 <b class='flag-5'>Python</b> 解释器

    Python】如何将Python脚本打包成exe可执行文件

    Python实用技巧】如何将Python脚本打包成exe可执行文件?
    的头像 发表于 08-18 12:40 1.8w次阅读
    【<b class='flag-5'>Python</b>】如何将<b class='flag-5'>Python</b><b class='flag-5'>脚本</b>打包成exe可执行文件

    python入c难,由c入python易?

    Python 是一种脚本语言,所见即所得,比较符合人的阅读习惯,就像,读一般文字。Python用于互联网和自动化测试的开发。
    发表于 02-02 10:22 737次阅读

    Python怎么玩转JS脚本

    本项目旨在让大家了解如何用Python来执行JS脚本,其主要目的是在进行数据 分析时,需要利用爬虫获取数据,有时会遇到JS混淆加密反爬取难点,此时我们需 要获取网页JS加密代码将其转换为Python代码运行,从而破解JS加密
    的头像 发表于 02-23 16:26 766次阅读
    <b class='flag-5'>Python</b>怎么玩转JS<b class='flag-5'>脚本</b>

    使用Python脚本实现自动化运维任务

    许多运维工程师会使用 Python 脚本来自动化运维任务。Python 是一种流行的编程语言,具有丰富的第三方库和强大的自动化能力,适用于许多不同的领域。
    的头像 发表于 04-08 10:36 1278次阅读