** 编写背景:**
曾经在网上找了一下广大的抢课脚本,发现不是太老了没法用(因为更新了新教务系统),就是只能抢一门课(无语了,只能抢一门课,我要你这个脚本有什么用?)。
** 编写原因 :**
1.广大选课系统的土豆服务器每次选课都要崩溃。
2.每次当我进入系统之后都至少是抢课开始之后的半个小时了。
3.我每次都只能选其他同学选剩下的课,这点最气。
4.最近选课系统开放,然后因为刚刚开学的原因又比较闲。
所以用两天时间肝出了一个选课脚本出来。
** 实现流程:**
1.在选课系统试运行的时候,准备发包的data表单的内容。
2.在抢课开始前5到6分钟左右时更新cookie。
3.在抢课快要开始时使用异步协程向服务器发送要选课程的包。
所以脚本至少要运行3次,在上述三个时间节点。
** 实现思路:**
服务器在正式开放选课时并不会结束session,一般的cookie都能使用1200秒,所以5到6分钟前更新的cookie还能继续使用。
另外因为选课服务器不靠谱,要崩溃,所以只能采用循环轰炸的方式多次向服务器发包,凭借数量优势,希望能选上课吧。
** 实现细节:**
1.脚本使用selenium模拟登录,查找网页上的信息。
2.使用了多进程,一个进程更新cookie,一个进程异步发包抢课。
3.使用了异步协程。
看得懂代码的同学可以自己调一下包的数量,但是出了事可不能找我哈。
脚本的使用不需要什么门槛,只要会安装python环境就差不多够了。
脚本的使用教程放在github上了。
github链接
教程的图片太多,很多显示不了,这里不一一插入了,请自行到上面的github链接查看。
# 适用于gzhu新教务系统
# 谨献给745各位靓仔
# 邮箱 lihao_deng@qq.com
# 本人不对因使用此脚本而产生的任何后果或损失负责
# 如果遇到bug,重新运行脚本或者重启电脑可能可以解决问题
import json
import re
import sys
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.common.by import By
import selenium.webdriver
from multiprocessing import Process
import os
from requests.cookies import RequestscookieJar
import urllib.parse
from selenium.webdriver.common.action_chains import ActionChains
import asyncio
import aiohttp
# 设置为全局变量方便更改
driver_path = r"D:environmentvariablemsedgedriver.exe"
def cookies_prepare(xuhao, mima):
while True:
# edge无头模式
edge = {
"ms:edgeOptions": {
'extensions': [],
'args': [
'--headless',
'--disable-gpu'
]}
}
driver = selenium.webdriver.Edge(driver_path, capabilities=edge)
try:
driver.get('https://newcas.gzhu.edu.cn/'
'cas/login?service=https%3A%2F%2Fnewmy.gzhu.edu.cn%2Fup%2Fview%3Fm%3Dup')
try:
# 智能等待
WebDriverWait(driver, 10, 0.5).until(ec.visibility_of_element_located((By.ID, 'un')))
except Exception as e:
print(e)
driver.find_element_by_id('un').send_keys(xuhao)
driver.find_element_by_id('pd').send_keys(mima)
driver.find_element_by_id('index_login_btn').click()
try:
WebDriverWait(driver, 10, 0.5).
until(ec.visibility_of_element_located((By.XPATH,
'//img[@src="/up/resource/image/home/gz/app/jwxt.png"]')))
except Exception as e:
print(e)
driver.find_element_by_xpath('//img[@src="/up/resource/image/home/gz/app/jwxt.png"]').click()
driver.get('http://jwxt.gzhu.edu.cn/jwglxt/'
'xsxk/zzxkyzb_cxZzxkYzbIndex.html?gnmkdm=N253512&layout=default&su='+xuhao)
try:
WebDriverWait(driver, 10, 0.5).until(ec.visibility_of_element_located
((By.XPATH, '//div[@]')))
except Exception as e:
print(e)
# 得到dict的cookie
dictcookies = driver.get_cookies()
# json.dumps和json.loads分别是将字典转换为字符串和将字符串转换为字典的方法
# json.loads仅支持元素用双引号括住的字典
jsoncookies = json.dumps(dictcookies)
with open('./cookies.txt', 'w') as file:
# 将字符串cookie保存至txt文件中
file.write(jsoncookies)
file.close()
print('cookies updated')
# 等待1100秒,差不多20分钟更新一次cookie
time.sleep(1100)
driver.quit()
except Exception as e:
print(e)
print('cookies updating failed!')
driver.quit()
# 准备学号与密码
def xuehao_mima():
xuhao_path = './xuhao.txt'
mima_path = './mima.txt'
xuhao_txt = os.path.exists(xuhao_path)
mima_txt = os.path.exists(mima_path)
# 如果没有xuhao.txt就要求用户输入学号与密码
if not xuhao_txt:
xuhao = input('请输入学号:')
mima = input('请输入密码:')
with open(xuhao_path, 'w') as xuhao_file:
xuhao_file.write(xuhao)
xuhao_file.close()
with open(mima_path, 'w') as mima_file:
mima_file.write(mima)
mima_file.close()
# 如果没有mima.txt就要求用户输入学号与密码
elif not mima_txt:
xuhao = input('请输入学号:')
mima = input('请输入密码:')
with open(xuhao_path, 'w') as xuhao_file:
xuhao_file.write(xuhao)
xuhao_file.close()
with open(mima_path, 'w') as mima_file:
mima_file.write(mima)
mima_file.close()
# 如果xuhao.txt与mima.txt都存在,就读取这两个文件中储存的学号密码
else:
with open('./xuhao.txt', 'r') as xuhao_file:
xuhao = xuhao_file.read()
xuhao_file.close()
with open('./mima.txt', 'r') as mima_file:
mima = mima_file.read()
mima_file.close()
return xuhao, mima
def main():
xuhao, mima = xuehao_mima()
data_txt = os.path.exists('./data.txt')
# 如果没有data.txt选课信息表单,就要求用户输入选课信息,生成表单
if not data_txt:
edge = {
"ms:edgeOptions": {
'extensions': [],
'args': [
'--headless',
'--disable-gpu'
]}
}
driver = selenium.webdriver.Edge(driver_path, capabilities=edge)
driver.get(
'https://newcas.gzhu.edu.cn/cas/login?service=https%3A%2F%2Fnewmy.gzhu.edu.cn%2Fup%2Fview%3Fm%3Dup')
try:
WebDriverWait(driver, 10, 0.5).until(ec.visibility_of_element_located((By.ID, 'un')))
except Exception as e:
print(e)
driver.find_element_by_id('un').send_keys(xuhao)
driver.find_element_by_id('pd').send_keys(mima)
driver.find_element_by_id('index_login_btn').click()
try:
WebDriverWait(driver, 10, 0.5).
until(ec.visibility_of_element_located((By.XPATH,
'//img[@src="/up/resource/image/home/gz/app/jwxt.png"]')))
except Exception as e:
print(e)
page = driver.page_source
judge = re.findall('融合门户', page)
if len(judge) != 0:
print('融合门户登录成功!')
try:
driver.find_element_by_xpath('//img[@src="/up/resource/image/home/gz/app/jwxt.png"]').click()
except Exception as e:
print(e)
if len(judge) == 0:
print('融合门户登录失败!')
print('请检查学号密码是否输入正确,n如输入错误,请删除当前目录下xuhao.txt和mima.txt文件n然后重新运行程序!')
else:
print('unknown error!')
print('已成功登录融合门户,但不能找到教务系统图标按钮!')
print('重新运行程序或重启电脑或许能解决问题!')
sys.exit(0)
# get的网址是选课网站的网址
driver.get(
'http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbIndex.html?gnmkdm=N253512&layout=default&su='
+ xuhao)
print('正在转到选课页面>>>')
try:
WebDriverWait(driver, 10, 0.5).until(ec.visibility_of_element_located((By.XPATH,
'//div[@]')))
except Exception as e:
print(e)
dictcookies = driver.get_cookies()
jsoncookies = json.dumps(dictcookies)
with open('./cookies.txt', 'w') as file:
file.write(jsoncookies)
file.close()
print('cookies updated')
# j表示data表单生成成功,0为假,1为真。
j = 0
# i表示是第一次循环.0为假,1为真
i = 1
while True:
# 第一次循环,判断是否处于选课阶段
if i == 1:
source = driver.page_source
# 通过页面信息判断是否处于选课阶段
judge = re.findall("不是选课阶段", source)
if len(judge) != 0:
break
# 通过课程名称进行选课操作
course_name = input('请完整复制课程名并粘贴于此处,示例:(180111005)地理教学技能 - 1.0 学分n注意!课程名的左右不要留有空格!n')
# kch_id为课程名称中的数字id,如:180111005
kch_id = course_name.split(')')[0][1:]
try:
WebDriverWait(driver, 10, 0.5).until(ec.visibility_of_element_located((By.XPATH,
"//button[@name='reset']")))
except Exception as e:
print(e)
# execute_script可以在selenium中执行js命令
driver.execute_script('document.getElementsByName("reset")[0].click();')
course_classification = int(input('请输入课程类别:n主修课程请输入1,板块课体育请输入2,通识选修请输入3,其他特殊课程请输入4n'))
if course_classification == 1:
driver.find_element_by_xpath('//ul[@]/li[1]').click()
elif course_classification == 2:
driver.find_element_by_xpath('//ul[@]/li[2]').click()
elif course_classification == 3:
driver.find_element_by_xpath('//ul[@]/li[3]').click()
elif course_classification == 4:
driver.find_element_by_xpath('//ul[@]/li[4]').click()
sendkeys_button = driver.find_element_by_xpath(
'//input[@placeholder="请输入课程号或课程名称或教学班名称查询!"]')
# ActionChains能模拟鼠标移动,点击,拖拽,长按,双击等等操作...
ActionChains(driver).move_to_element(sendkeys_button).click().send_keys(kch_id).perform()
driver.find_element_by_xpath('//button[@name="query"]').click()
try:
WebDriverWait(driver, 10, 0.5).
until(ec.visibility_of_element_located((By.XPATH, "//tr[1]/td[@class='jsxmzc']")))
except Exception as e:
print(e)
# 网页源代码
page = driver.page_source
# 在网页代码中找到教学班的个数
jxb_numbers = re.findall('教学班个数.*">([1-9])', page)
if len(jxb_numbers) == 0:
print('未找到教学班信息,请检查信息是否输入正确')
print('教学班名称示例:(180111005)地理教学技能 - 1.0 学分n注意!名称的左右不要留有空格!n注意!请检查课程类别是否正确!')
print('请在程序提示后,重新输入信息!')
continue
jxb_numbers = jxb_numbers[0]
i = 1
while i <= int(jxb_numbers):
# 不同的教学班的信息在不同序号的tr标签下,依次打印各个教学班的信息
# 老师名字与职称
teacher = driver.find_element_by_xpath("//tr[%d]/td[@class='jsxmzc']" % i).text
# 上课时间
course_time = driver.find_element_by_xpath("//tr[%d]/td[@class='sksj']" % i).text
# 教学班号
course_number = driver.find_element_by_xpath("//tr[%d]/td[@class='jxbmc']" % i).text
print('教学班%d,老师:%s,上课时间:%s,教学班号:%sn' % (i, teacher, course_time, course_number))
i += 1
# jxbmc为教学班号
jxbmc = input('请从上面的教学班中选择并复制粘贴要选择的教学班的教学班号,'
'示例:(2021-2022-2)-131800701-1n注意!教学班号的左右不要留有空格!n')
# 通过教学班号找到jxb_ids的信息
tobeprocessed_jxb_ids = driver.find_element_by_xpath(
'//td[@ and contains(text(), "%s")]/../td[@]/button' % jxbmc)
.get_attribute('onclick')
jxb_ids = tobeprocessed_jxb_ids.split(',')[1][1:-1]
# 下面是处理得到kcmc的函数
course_name = course_name.split(')')
course_name[0] = course_name[0] + ')'
strings = course_name[1].split(' ')
strings1 = urllib.parse.quote(strings[0])
strings2 = urllib.parse.quote(strings[-1])
# kcmc通过urlencode编码,但是,kcmc只有部分编码,需要注意
kcmc = course_name[0] + strings1 + '+-+1.0+' + strings2
# 下面为通过js找到属性值的函数
rwlx = driver.execute_script("document.getElementById('rwlx')['value']")
rlkz = driver.execute_script("document.getElementById('rlkz')['value']")
rlzlkz = driver.execute_script("document.getElementById('rlzlkz')['value']")
xkxnm = driver.execute_script("document.getElementById('xkxnm')['value']")
xkxqm = driver.execute_script("document.getElementById('xkxqm')['value']")
xklc = driver.execute_script("document.getElementById('xklc')['value']")
kklxdm = driver.execute_script("document.getElementById('kklxdm')['value']")
zyh_id = driver.execute_script("document.getElementById('zyh_id')['value']")
njdm_id = driver.execute_script("document.getElementById('njdm_id')['value']")
xkkz_id = driver.execute_script("document.getElementById('xkkz_id')['value']")
# 下面是用xpath找属性值的函数
cxbj = driver.find_element_by_xpath("//input[@name='cxbj']").get_attribute('value')
xxkbj = driver.find_element_by_xpath("//input[@name='xxkbj']").get_attribute('value')
# 下面是完整的data表单的内容
# sxbj与qz的属性没有在页面中找到,通过抓包获得
data = {
"jxb_ids": jxb_ids,
"kch_id": kch_id,
"kcmc": kcmc,
"rwlx": rwlx,
"rlkz": rlkz,
"rlzlkz": rlzlkz,
"sxbj": "1",
"xxkbj": xxkbj,
"qz": "0",
"cxbj": cxbj,
"xkkz_id": xkkz_id,
"njdm_id": njdm_id,
"zyh_id": zyh_id,
"kklxdm": kklxdm,
"xklc": xklc,
"xkxnm": xkxnm,
"xkxqm": xkxqm}
# 存储data表单
with open('./data.txt', 'a') as data_file:
# dict类型的内容不能用write函数
data_file.write(str(data) + 'n')
data_file.close()
# j=1 表示抢课信息录入成功
j = 1
# i=0表示不是第一循环
i = 0
print('选课内容添加成功!')
judge_break = input('是否继续添加选课内容[y/n]?:')
if judge_break == 'n':
break
driver.quit()
if j == 1:
print('data表单准备完成,抢课信息录入完毕。')
elif j == 0:
print('选课系统未开放,无法录入抢课信息,请在选课系统开放后再运行此脚本')
# 如果有抢课信息表单,则继续运行程序
else:
print('已存在抢课信息,如需重新选择抢课信息,请停止运行程序n并删除此脚本当前目录下的"data.txt"文件,然后重新运行程序')
# 创建两个进程,一个用来每二十分钟更新一次cookie, 一个用来发送表单抢课
process = [Process(target=cookies_prepare, args=(xuhao, mima)),
Process(target=qiangke, args=(xuhao,))]
[p.start() for p in process]
[p.join() for p in process]
def qiangke(xuhao):
print('准备开始抢课!')
# 读取保存的cookie
cookie_txt = os.path.exists('./cookies.txt')
if not cookie_txt:
time.sleep(20)
with open('./cookies.txt', 'r') as file:
listcookies = json.loads(file.read())
file.close()
jar = RequestscookieJar()
jar.set(listcookies[0]['name'], listcookies[0]['value'])
# 读取保存的data表单,并通过async异步的方式发送
with open('./data.txt', 'r') as data_file:
tobeprocessed_data = data_file.read()
data_file.close()
datas = tobeprocessed_data.split('n')
while True:
tasks = []
n = 0
while n < 10:
i = 0
while i < len(datas)-1:
data = eval(datas[i])
coroutine = submit_package(xuhao, data, jar)
task = asyncio.ensure_future(coroutine)
tasks.append(task)
i += 1
n += 1
loop = asyncio.get_event_loop()
result_datas = loop.run_until_complete(asyncio.wait(tasks))
# i表示cookie失效,需要重新发包
i = 0
for result_data in result_datas[0]:
if result_data.result() == 'cookie out of date':
i = 1
break
if i == 1:
with open('./cookies.txt', 'r') as file:
listcookies = json.loads(file.read())
file.close()
jar = RequestscookieJar()
jar.set(listcookies[0]['name'], listcookies[0]['value'])
else:
break
loop.close()
print('抢课完毕!')
async def submit_package(xuhao, data, jar):
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62 ",
"refer": "http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzb_cxZzxkYzbIndex.html?"
"gnmkdm=N253512&layout=default&su="+xuhao}
url = "http://jwxt.gzhu.edu.cn/jwglxt/xsxk/zzxkyzbjk_xkBcZyZzxkYzb.html?gnmkdm=N253512&su="+xuhao
params = {"gnmkdm": "N253512",
"su": xuhao}
try:
# ClientSession别忘了跟()
async with aiohttp.ClientSession() as session:
async with await session.post(url, headers=headers, data=data, params=params, cookies=jar, timeout=5)
as response:
contant = await response.text()
contant = contant[-4:]
if contant == '-1"}':
print('对不起,该教学班已无余量,不可选!')
elif contant == '"0"}':
print('所选教学班的上课时间与其他教学班有冲突!')
elif contant == '"1"}':
print('恭喜你,选课成功!')
elif contant == 'tml>':
print('cookie已失效!请等待cookie更新')
# 等待另一个进程更新cookie
await asyncio.sleep(10)
return 'cookie out of date'
else:
print(contant)
await asyncio.sleep(5)
# 未知内容,重新运行程序发送表单
await submit_package(xuhao, data, jar)
except asyncio.TimeoutError:
# 超时服务器仍未返回值,则重新运行程序发送表单
await submit_package(xuhao, data, jar)
except Exception as e:
print(e)
await asyncio.sleep(5)
# 未知错误,重新运行程序发送表单
await submit_package(xuhao, data, jar)
if __name__ == '__main__':
main()
广州大学gzhu抢课脚本
运行环境:适用于广大新版教务系统 https://newcas.gzhu.edu.cn/cas/login?service=https%3A%2F%2Fnewmy.gzhu.edu.cn%2Fup%2F
谨献给745宿舍全体靓仔,祝各位靓仔学业顺利
邮箱 lihao_deng@qq.com
本人不对因使用此脚本而产生的任何后果或损失负责
如果遇到bug,重新运行脚本或者重启电脑可能可以解决问题
python 3.9
需要安装的库requests
selenium
os
用如下代码安装(如果安装不了,请自行百度):
pip3 install requests pip3 install selenium pip3 install os
如果想使用anaconda ,请自行百度
下面是使用此脚本的教程:- 转到edge://settings/help 查看你的Microsoft Edge版本。
访问"Microsoft Edge驱动程序"。
在页面的"获取最新版本"部分,找到与你的Microsoft Edge的版本编号相匹配的,然后选择其x64版本并下载。
下载完成后,将可执行 msedgedriver.exe 文件放到任意位置(建议直接放到D盘根目录)。
右键单击可执行msedgedriver.exe文件,选择复制文件地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P5JDdS4s-1645974306589)(image-20220126003123925.png)]
6.复制粘贴文件地址,覆盖driver_path = r"D:environmentvariablemsedgedriver.exe"里的"D:environmentvariablemsedgedriver.exe"。如图,位置可能在selenium_course_selector.py文件的第22行,也有可能在selenium_course_selector.py文件的第8行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qiHoQRPb-1645974306590)(image-20220227152531167.png)]
7.在自己的浏览器登录教务系统选课界面,找到所有自己想选的课程,并完整复制其名称
如图所示,示例:(130101303)学术研究与交流 - 1.0 学分
注意!不要把两边的空格复制进去!从"("开始复制,到"学分"的"分"字结束。
可以把课程名称先保存在txt文件中,方便后面运行程序时复制粘贴。
因为运行脚本时如果使用浏览器登录自己的教务系统账号,脚本的登录状态将会失效。
同时需要记住该门课程的类别:是主修课程,板块课体育,通识选修还是其他特殊课程?需要记住。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8E0sRpFL-1645974306590)(image-20220226164903042.png)]
8.运行selenium_course_selector.py程序。
9.根据提示输入学号和密码(学号与密码保存在本地,即selenium_course_selector.py同级目录下)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6wXfxVsm-1645974306590)(image-20220226165425474.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-feJzXWWZ-1645974306591)(image-20220226165445385.png)]
如果输入错误,请停止运行程序,并在selenium_course_selector.py同级目录下手动删除xuhao.txt与mima.txt文件,然后重新运行程序。
10.等待一段时间,出现提示后,根据提示操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fVQzadzA-1645974306591)(image-20220227153712981.png)]
11.请在选课开始前几天,选课系统试运行的时候,提前运行一次脚本录入信息,以方便在抢课开始时迅速开始抢课。
12.请在正式选课开始前5到6分钟时使用一次脚本,以更新cookie。
11.请在选课开始前几天,选课系统试运行的时候,提前运行一次脚本录入信息,以方便在抢课开始时迅速开始抢课。
12.请在正式选课开始前5到6分钟时使用一次脚本,以更新cookie。
13.然后在正式选课开始时前一分钟运行脚本进行抢课。



