南开三年
人马上就要润掉了,这三年我攒了不少 scripts, 值得分享一下,至于“感慨万千”什么的废话别指望我编了。
写在最前面——如何提学分绩
显而易见,如果你大一的课分数比别人低,就算你大二大三学年学分绩前三,排名依然会拉垮。提分只有一条路——拼命上选修课,当然这些课是没法用来提保研用的 ABCD 学分绩的。
选修课不止专门的“通识选修课”——绝大多数是根本不值那 240 块钱的大水课,上起来痛苦万分,事实上分数也不会保证 90+,在还有名额的情况下,可以选别的学院的专业课。比如说,我至今上过通信、电子、人工智能自动化等等的课程,其中一门三学分的通信课程还给了我 99 分。
到了期末感觉没自信拿高分?没关系,可以“期末退课”,也就是缓修。学校的精神科医生十分 obliging, 只要你说你压力大精神不正常,他们就会给你开证明,拿给学院教学办,填个表,no questions asked. 缓修同样会在成绩单里被标注为“退课”,也就是说面试的时候有可能被问到,但好歹不影响学分绩。选修课缓修后就不用再重新选了。
接下来是纯技术性内容
tty 联网
没有 GUI? 该死的 NKU_WLAN 网关登录页面非得要 JavaScript? 其实根本没那么麻烦。
curl "http://202.113.18.106:801/eportal/?c=ACSetting&a=Login&loginMethod=1&protocol=http%3A&hostname=202.113.18.106&port=&iTermType=1&wlanuserip=$(ip -f inet addr show wlp0s20f3 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p')&wlanacip=null&wlanacname=zx_&redirect=null&session=null&vlanid=0&mac=00-00-00-00-00-00&ip=$(ip -f inet addr show wlp0s20f3 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p')&enAdvert=0&jsVersion=2.4.3&DDDDD=学号&upass=密码&R1=0&R2=0&R3=0&R6=0¶=00&0MKKey=123456&buttonClicked=&redirect_url=&err_flag=&username=&password=&user=&cmd=&Login="
Some observations:
- GET 无加密明文传输?Someone in the middle could exploit the fact…
- 连续输错不会被 ban, 你猜猜这能用来干什么?
- 我用
$(ip -f inet addr show wlp0s20f3 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p')
获取分配的 IP 地址,你的网卡名字可能不一样。 - 当然也可以改成 requests 综合到其他 Python scripts 里。
SSO 登录
使用 Selenium 实现,麻烦之处在于那个滑块。
try:
driver.find_element(By.CLASS_NAME, 'input.input.n').send_keys(username)
driver.find_element(By.CLASS_NAME, 'input.input.p').send_keys(password)
slider = driver.find_element(By.ID, 'btn')
slide = webdriver.ActionChains(driver)
slide.click_and_hold(slider).move_by_offset(250, 0).release().perform()
driver.find_element(By.ID, 'submitRole').click()
提示:登上去之后不一定继续得用 Selenium,读取一下 cookies 之类的,然后就可以用别的网络库了。
eamis 自动查分
警告:eamis 网站的结构经常改变,而且每学期你的学分绩的 XPath 肯定都不一样(已修学分加了一行)。
(前面的是 SSO 登录部分)
while (credits == PREV_CREDITS):
time.sleep(1200)
#driver.get('https://eamis.nankai.edu.cn/eams/home.action')
driver.get('https://webvpn.nankai.edu.cn/https/77726476706e69737468656265737421f5f64c95347e6651700388a5d6502720dc08a5/eams/home.action')
WebDriverWait(driver, 20).until(expected_conditions.presence_of_element_located((By.PARTIAL_LINK_TEXT, '我的成绩')))
driver.find_element(By.PARTIAL_LINK_TEXT, '我的成绩').click()
WebDriverWait(driver, 20).until(expected_conditions.presence_of_element_located((By.CLASS_NAME, 'toolbar-icon.action-default')))
driver.find_element(By.CLASS_NAME, 'toolbar-icon.action-default').click()
WebDriverWait(driver, 20).until(expected_conditions.presence_of_element_located((By.XPATH, '/html/body/table/tbody/tr/td[3]/div/div/div[3]/table[2]/tbody/tr[9]/th')))
response = driver.find_element(By.XPATH, '/html/body/table/tbody/tr/td[3]/div/div/div[3]/table[2]/tbody/tr[9]/th').text
score = response[response.find(':')+1:response.find(' /')]
credits = driver.find_element(By.XPATH, '/html/body/table/tbody/tr/td[3]/div/div/div[3]/table[2]/tbody/tr[8]/th[3]').text
# ...
from pydub import AudioSegment
from pydub.playback import play
play(AudioSegment.from_wav('/media/d/Audio/Sounds/bell.wav'))
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
root.withdraw()
messagebox.showinfo('GPA', '出分了!%s' % score)
抢课
import threading
import requests
import lxml.html
import sys
from time import sleep
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0',
'Referer': 'https://eamis.nankai.edu.cn/eams/stdElectCourse!defaultPage.action?electionProfile.id=1470',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'Origin': 'https://eamis.nankai.edu.cn',
'DNT': '1',
'Connection': 'keep-alive',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin'
}
params = {
'profileId': '1470',
}
ids = {
# '1': '577323',
'2': '582777',
# '3': '581667',
'4': '582191',
'5': '582690',
'6': '581485'
}
def get_data(id):
return {
'optype': 'true',
'operator0': f'{id}:true:0',
'lesson0': id,
f'expLessonGroup_{id}': 'undefined',
}
cookies = {
'semester.id': '4263',
'srv_id': '45e1a8e1244b5f919fa64489a5313cf7',
'JSESSIONID': '85926576E34B0B91CD68A68CCCB64D6A.std6',
}
def submit(data):
while True:
response = requests.post(
'https://eamis.nankai.edu.cn/eams/stdElectCourse!batchOperator.action',
params=params,
cookies=cookies,
headers=headers,
data=data,
verify=False
)
html = lxml.html.fromstring(response.text)
msg = str(html.text_content()).strip()
print(msg[:msg.find('\n')])
sleep(0.44)
if __name__ == '__main__':
# threads = [
# threading.Thread(target=submit, args=(get_data(id),))
# for id in ids.values()
# ]
# for thread in threads:
# thread.start()
# for thread in threads:
# thread.join()
data = get_data('581485')
submit(data)
图书馆抢座
你猜猜什么样的座位需要写 script 来抢?
获取 cookie 和 token
cookie = driver.get_cookie('ic-cookie')['value']
token = driver.execute_script('return JSON.parse(sessionStorage.getItem("userInfo")).token')
获取座位编号
身为一名合格的前端开发人员,你应该知道怎么用 dev tools 记录网络请求并分析其参数,然后写成 curl 或者 requests.
开抢!
稍安勿躁,建议 sleep 到 22.00.00 再启动你的 worker processes, 图书馆服务器的时钟有点慢,我现在只能靠刚开始一瞬间的高并发来抢到座位。
计算平均分和 GPA
注:只适用于 22 级及以前。
#!/usr/bin/python3
import sys
def get_grade_point(mark: float) -> float:
if mark >= 90.:
return 4.
elif mark >= 85.:
return 3.7
elif mark >= 81.:
return 3.3
elif mark >= 78.:
return 3.
elif mark >= 75.:
return 2.7
elif mark >= 72.:
return 2.3
elif mark >= 69.:
return 2.
elif mark >= 66.:
return 1.7
elif mark >= 63.:
return 1.3
elif mark >= 60.:
return 1.
else:
return 0.
def get_scores(filename: str) -> list[tuple[float, float]]:
with open(filename) as f:
lines = [l for line in f.readlines() if (l := line.strip())]
return [(float(line.split()[0]), float(line.split()[1])) for line in lines]
if __name__ == '__main__':
option = sys.argv[1]
score_filename = sys.argv[2]
scores = get_scores(score_filename)
if option == 'gpa':
grade_points = [(credit, get_grade_point(mark)) for credit, mark in scores]
credit_sum = sum((credit for credit, _ in grade_points))
grade_point_sum = sum((credit * grade_point for credit, grade_point in grade_points))
print(f'Total credits: {credit_sum}')
print(f'GPA: {grade_point_sum / credit_sum}')
else:
credit_sum = sum((credit for credit, _ in scores))
mark_sum = sum((credit * mark for credit, mark in scores))
print(f'Total credits: {credit_sum}')
print(f'Average mark: {mark_sum / credit_sum}')
各种论文的 LaTeX 模板
刚写下这个标题我才想到,全校应该只有我一个人坚决抵制 Word 和 PPT 的。需要的可以邮件联系我。
写在最后
一时半会儿能在手边找到的就是这些,当然还有很多因为一月份硬盘坏掉而丢失的(比如我大一处理物理实验的程序,主要是误差计算),或者 obsolete 的。以后发现了再慢慢补上。