新版ZUCC正方教务系统抢课脚本的流程分析与实现,文章结尾有完整项目的所有代码。
个人博客文章链接初始化CATCH_PLANNED_COURSE.py这个文件
import LOGIN from bs4 import BeautifSEO靠我ulSoup import timeclass PlannedCourse:def __init__(self, account):self.account = accountdef SEO靠我enter_planned_course(self):returndef catch_course(self):returndef choose_course_class(self):returndeSEO靠我f run(self):# 进入计划内选课界面self.enter_planned_course()# 爬取课程信息self.choose_course_class()# 模拟发包抢课self.catSEO靠我ch_course()这里的account对应了登录时的账号,登录时的会话一直保存在account中,在选课的部分会通过这个会话保持和正方教务系统的连接。
首先我们需要知道在正常网页下SEO靠我我们是如何进入计划内选课界面的。
一般情况下我们会点击计划内选课进入,但是这样我们是无法获取到真正的界面的。当我们ctrl+点击计划内选课的时候会新建一个标签页,在这个页面中才是计划内选课的真正网址
下图SEO靠我是直接点击进入的网站
下图是ctrl+点击进入的网站
所以我们登录成功后可以直接像这个网站发送GET请求,account对象的会话内容就会转移到这个页面。
这里可以通过BeautifulSoup解析一下,确SEO靠我保能出现课程种类就好。
def enter_planned_course(self):url = LOGIN.ZUCC.PlanCourageURL + "?xh=" + self.account.acSEO靠我count_data["username"]header = LOGIN.ZUCC.InitHeaderheader["Referer"] = urlresponse = self.account.sSEO靠我ession.get(url=url, headers=header)"""以下两行代码可以用先解析以下,看下当前是否在我们需要的页面"""self.account.soup = BeautifulSSEO靠我oup(response.text, "lxml")#print(self.account.soup)#return response能输出以下内容就证明进入了计划内选课界面
enter_plaSEO靠我nned_course.py会返回一个包含课程信息的response,这个response会以参数的形式出现在choose_course_class.py中,所以对应的地方都要更改
def chooseSEO靠我_course_class(self,response):returndef run(self):# 进入计划内选课界面response = self.enter_planned_course()# SEO靠我爬取课程信息self.choose_course_class(response)# 模拟发包抢课self.catch_course()首先我们需要选择我们要抢什么课,那我们就需要从刚才的网页中把所有的SEO靠我课程信息给获取出来。这个时候就要用到爬虫来帮我们获取这些网页标签。
分析之前得到的response中的网页,可以得到每个课程种类信息是以下面这种结构在html文件中呈现出来的。(下面html样例中的超链SEO靠我接部分我删除了)
<tr><td>301258</td><td>操作系统原理</td><td>必修</td><td> </td><td>3.0</td><td>3.0-0.0</td><td> </tdSEO靠我><td>查看课程介绍</td><td>已选</td><td>1</td> </tr>我们也可以通过Chrome浏览器的F12开发者工具查看。左上角的小箭头点一下后再点击页面中你想要查SEO靠我找的部分就会自动跳转出对应的html部分。
知道它在标签中我们就可以解析这个html,输出所有的标签。
到这一步后还不够,我们还需要进入可选教学班信息部分才能选择课程选课。而这个界面无论是普通的点击还是cSEO靠我trl+点击都不能出现它最原始的网页,这种时候就需要借助Chrome浏览器F12开发者工具中的Network来进行抓包
打开network界面后,点击课程可以看到发送了一些杂七杂八的东西,找到发送时间最SEO靠我长的那个,打开后可以看到这个http数据包的头部,里面有个Request URL,在这里我们可以看到数据包发送给了这个网址,而这个网址的组成就是/clsPage/xsxjs.aspx 加上点击课程的选SEO靠我课课号以及自己账户的学号。那我们就可以通过这个网址让account对象的会话直接模拟发送一样的Get请求,得到对应的页面。当然网址的组成需要修改。
在这之前我们还需要给这一部分做点交互的界面,毕竟需要选SEO靠我择哪个课程种类
# init中加入了两个变量def __init__(self, account):self.account = account# 最终选择的课程种类的urlself.obj_url =SEO靠我 ""# 所有课程种类的urlself.urls = []# 所有开课信息self.course_list=[]# 发送选课数据包的时候要用到self.obj_viewstate=""def chooSEO靠我se_course_class(self, response):self.account.soup = BeautifulSoup(response.text, "lxml")links = selfSEO靠我.account.soup.find_all(name="tr")for num, link in enumerate(links[1:-1]):tds = link.find_all("td")prSEO靠我int("编号:" + str(num + 1) + "\t课程名称: " + tds[1].text)url = "http://" + LOGIN.ZUCC.DOMAIN + "/clsPage/SEO靠我xsxjs.aspx?" + "xkkh=" + \tds[1].find("a").get("onclick").split("=")[1][0:-3] + "&xh=" + self.accounSEO靠我t.account_data["username"]# print(url)self.urls.append(url)# 在这里,所有的url都被存放在self.urls中,只需要判断输入的数值,读取SEO靠我相应的url,发送get请求就行了.n = input("输入编号:")url = self.urls[int(n) - 1]self.obj_url = urlheader = LOGIN.ZUCCSEO靠我.InitHeaderheader["Referer"] = "http://xk.zucc.edu.cn/xs_main.aspx?xh=" + self.account.account_data[SEO靠我username]item_response = self.account.session.get(url=url, headers=header)#print(BeautifulSoup(item_SEO靠我response.text, lxml))return创建一个新的类用来存放选择的课程的开课信息
class PlannedCourseInfo:def __init__(self, num, codeSEO靠我, teacher, time):self.num = str(num)self.code = str(code)self.teacher = str(teacher)self.time = str(SEO靠我time)def show_course_info(self):print("课程编号:" + self.num+ "\t课程代码:" + self.code+ "\t课程教师:" + self.teSEO靠我acher+ "\t课程时间:" + self.time)def __contains__(self, item):if item in self:return Trueelse:return FalSEO靠我se同时我们获得到的课程信息可以通过之前的方式,找到开课信息的元素,将这些元素的内容加入到课程list中
def choose_course_class(self, response):self.accSEO靠我ount.soup = BeautifulSoup(response.text, "lxml")links = self.account.soup.find_all(name="tr")for numSEO靠我, link in enumerate(links[1:-1]):tds = link.find_all("td")print("编号:" + str(num + 1) + "\t课程名称: " + SEO靠我tds[1].text)url = "http://" + LOGIN.ZUCC.DOMAIN + "/clsPage/xsxjs.aspx?" + "xkkh=" + \tds[1].find("aSEO靠我").get("onclick").split("=")[1][0:-3] + "&xh=" + self.account.account_data["username"]# print(url)seSEO靠我lf.urls.append(url)n = input("输入编号:")url = self.urls[int(n) - 1]self.obj_url = urlheader = LOGIN.ZUCSEO靠我C.InitHeaderheader["Referer"] = "http://xk.zucc.edu.cn/xs_main.aspx?xh=" + self.account.account_dataSEO靠我[username]item_response = self.account.session.get(url=url, headers=header)#print(BeautifulSoup(itemSEO靠我_response.text, lxml))item_soup = BeautifulSoup(item_response.text, "lxml")self.obj_viewstate = itemSEO靠我_soup.find_all(name=input, id="__VIEWSTATE")[0]["value"]item_trs = item_soup.find_all(name="tr")for SEO靠我num, item_tr in enumerate(item_trs[1:]):try:tds = item_tr.find_all("td")code = tds[0].find(input).geSEO靠我t(value)teacher = tds[2].texttime = tds[3].textlessen = PlannedCourseInfo(num + 1, code, teacher, tiSEO靠我me)self.course_list.append(lessen)except BaseException:returnreturn做到这里就已经可以获取到这门课的所有可选时间老师教室等等内容,这些SEO靠我内容都存放在PlannedCourse.course_list中。
接下来就是发送选课数据包了。
在发送抢课数据包之前,还需要知道发送数据包的内容。这里要再次使用network的工具,模拟点SEO靠我击提交选课信息,抓取发送的数据包。
这里的数据包发送的是POST请求,直接向后台发送选课数据,能成功发送就可以正常抢课。
发送的数据包主要内容如下:
__EVENTTARGET: 这里指的是两个按钮,看你发SEO靠我送了哪一个,提交按钮是Button1
__VIEWSTATE: 这个是之前某个网页的随机码,这个码在上一部分已经提取过了,存放在了PlannedCourse.obj_viewstate变量中。
__VIESEO靠我WSTATEGENERATOR: 不知道有啥用但是万年不变,就没必要爬取了,就直接写在数据包吧
xkkh: 字面意思选课课号,这个东西存放在PlannedCourse.course_list
最后加上一些SEO靠我交互的内容,将POST请求发送出去就行
def catch_course(self):for info in self.course_list:info.show_course_info()n = inSEO靠我put("输入编号(0退出):")while True:if n == "0":returnelif int(n) < 0 or int(n) > len(self.course_list):prinSEO靠我t("请输入正确的数字")else:breakpost_data = {"__EVENTTARGET": "Button1","__VIEWSTATEGENERATOR": "55DF6E88","RSEO靠我adioButtonList1": "1","xkkh": self.course_list[int(n) - 1].code,"__VIEWSTATE": self.obj_viewstate}whSEO靠我ile True:response = self.account.session.post(url=self.obj_url, data=post_data)soup = BeautifulSoup(SEO靠我response.text, "lxml")try:reply = soup.find(script).string.split("")[1]except BaseException:reply = SEO靠我"未知错误"print(reply + "\t\t" + str(time.strftime(%m-%d-%H-%M-%S, time.localtime(time.time()))))if replSEO靠我y == "选课成功!":return完整项目地址https://github.com/VinceMockghy/zucc_xk_ZhengFang
网站备案号:浙ICP备17034767号-2