不用Selenium如何爬课表。
首先先看下工大的信息门户的网页,分析下:
先用py获取下网站的源码吧。
1 2 3 4 5 6
| import requests
session = requests.session() res = session.get("http://my.hfut.edu.cn/login.portal") with open("1.html", "wb") as f: f.write(res.content)
|
所以验证码是这个死的图片了?这想想都不科学,这个图片访问多少回都不变的…
不知道验证码的请求是什么,那就用Fiddler抓包看看登录过程中访问了什么页面。
显然第15个有点意思,/captchaGenerate.portal
,看来就是他了。
先用浏览器访问下试试,发现每次都不一样,现在十分放心QAQ
下载验证码图片保存到本地肯定不难,直接
1 2 3 4
| captchaImg = session.get("http://my.hfut.edu.cn/captchaGenerate.portal", stream = True).content
with open("captchaImg.jpg", "wb") as f: f.write(captchaImg)
|
打开这个图片也不难,先手动输入吧
1 2 3 4 5 6 7 8
| from PIL import Image
jpg = Image.open(("{}/captchaImg.jpg").format(os.getcwd()))
jpg.show()
captcha_code = input("请输入验证码:") jpg.close()
|
再开下Fiddler,不如先登录下,看看登录过程中访问了什么页面。
嗯,目测就是第12个了,看下表单。
哎呦不错,现在可以构造post内容了(居然明文传输,不怕泄露密码吗
1 2 3 4 5 6 7
| data = { "Login.Token1" : username, "Login.Token2" : password, "captchaField" : captcha_code, "goto" : "http://my.hfut.edu.cn/loginSuccess.portal", "gotoOnFail" : "http://my.hfut.edu.cn/loginFailure.portal" }
|
假如发生了什么错误怎么处理,于是就有了下面这段神奇的代码(一点都不神奇,手动滑稽,上大学了连自己学号密码都记不住🐎,还是说验证码输错了
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| try: loginres = session.post("http://my.hfut.edu.cn/userPasswordValidate.portal", data = data) logcont = loginres.text if logcont.find("验证码非法") != -1 : print("验证码非法") raise RuntimeError("验证码非法") elif logcont.find("用户不存在或密码错误") != -1: print("用户不存在或密码错误") raise RuntimeError("用户不存在或密码错误") print("登陆成功") except: print("登录失败") exit(0)
|
接下来就可以访问本科教务的那个页面了。
1
| session.get("http://jxglstu.hfut.edu.cn/eams5-student/wiscom-sso/login")
|
先看看课表页面的组成,以我的为例,http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-table/info/97097
,貌似最后一项数字有点别的含义嗷
Fiddler抓包发现他是页面跳转的,从http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-table
跳转到那个页面的,所以访问肯定直接访http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-table
就行了。
看下网页源代码
1 2 3
| res = session.get("http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-table") with open("2.html", "wb") as f: f.write(res.content)
|
你会发现它什么都没有…估计是ajax动态生成的…不如开始Fiddler抓包
然后你就发现了这个神奇的网址,/eams5-student/for-std/course-table/get-data?bizTypeId=2&semesterId=74&dataId=97097
这个dataId很显然能拿到,就是访问那个跳转后的网址最后面的,这个semesterId和bizTypeId咋拿到呢
首先吧,底下有一堆转义字符的json,让人很不爽,一开始估计这俩东西就在这里面。
用py解析下这段json试试。
在这里我发现了一种叫unicode-escape的特殊的decode方式,然后就解析出来了。
1 2 3 4 5 6 7
| tableres = session.get("http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-table/get-data?bizTypeId=2&semesterId=74&dataId=97097) src = str(BeautifulSoup(tableres.content, "lxml").select("body script")[1].string) src = '[' + re.search("(?<='\[).*?(?=\]')", src).group() + ']' # src = src[src.find("'[")+1:src.find("]'")+1] src = src.encode().decode("unicode-escape") myjson_d = json.loads(src)
|
这个里面的确有semesterId,但是bizTypeId是什么玩意?
于是我在源码中搜索,很快就找到了。
这不就是ajax的源码吗…allSemesters?是不是在源码里有呢…其实一搜索就发现了
完全不用解析那个辣鸡的json…
访问那个页面出来的是意料之中的json,解析课表的那个js完全暴露在源码中,直接看看就能转换,然后就完事了。
当然,我还想获取下姓名和学号,这个全是静态的,方法基本就用BeautifulSoup直接就能出来,我就不写了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
| import json import os import re
import requests from bs4 import BeautifulSoup from PIL import Image
username = input("请输入学号:") password = input("请输入密码:") term = [ "2019-2020学年第一学期", "2018-2019学年第二学期", "2018-2019学年第一学期", "2017-2018学年第二学期", "2017-2028学年第一学期", "2016-2017学年第二学期", "2016-2017学年第一学期", "2015-2016学年第二学期", "2015-2016学年第一学期", "2014-2015学年第二学期", "2014-2015学年第一学期", "2013-2014学年第二学期", ] print("请选择学期序号:") for i in range(0, len(term)): print(i ,".", term[i]) term = term[int(input())]
session = requests.session() session.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
session.get("http://my.hfut.edu.cn/login.portal")
captchares = session.get("http://my.hfut.edu.cn/captchaGenerate.portal", stream = True) captchaimg = captchares.content
try: with open(username + "captchaimg.jpg", "wb") as f: f.write(captchaimg) except: exit(0)
del f del captchaimg del captchares
jpg = Image.open(("{}/" + username + "captchaimg.jpg").format(os.getcwd())) jpg.show() captcha_code = input("请输入验证码:") jpg.close() del jpg
data = { "Login.Token1" : username, "Login.Token2" : password, "captchaField" : captcha_code, "goto" : "http://my.hfut.edu.cn/loginSuccess.portal", "gotoOnFail" : "http://my.hfut.edu.cn/loginFailure.portal" }
try: loginres = session.post("http://my.hfut.edu.cn/userPasswordValidate.portal", data = data) logcont = loginres.text if logcont.find("验证码非法") != -1 : print("验证码非法") raise RuntimeError("验证码非法") elif logcont.find("用户不存在或密码错误") != -1: print("用户不存在或密码错误") raise RuntimeError("用户不存在或密码错误") print("登陆成功") except: print("登录失败") exit(0)
del captcha_code del logcont del loginres
session.get("http://jxglstu.hfut.edu.cn/eams5-student/wiscom-sso/login")
tableres = session.get("http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-table/")
dataId = tableres.url dataId = dataId[dataId.rfind('/')+1:]
semidarr = BeautifulSoup(tableres.content, "lxml").find(id = "allSemesters").contents for i in range(1, len(semidarr), 2): if semidarr[i].string == term: break semesterId = semidarr[i].attrs["value"]
del i del semidarr
soup = BeautifulSoup(session.get("http://jxglstu.hfut.edu.cn/eams5-student/for-std/student-info/").content, "lxml") namestr = soup.find_all(class_ = "list-group-item text-right")[1].contents[3].text clsstr = soup.find_all(class_ = "rounded info-page")[1].contents[5].contents[35].string
del soup
a = session.get("http://jxglstu.hfut.edu.cn/eams5-student/for-std/course-table/get-data?bizTypeId=2&semesterId=" + str(semesterId) + "&dataId=" + str(dataId))
with open("table.json", "wb") as f: f.write(a.content)
table_json = json.loads(a.content) print(table_json)
table_arr = [ "序号 课程代码 课程名称 教学班代码 教学班名称 课程类型 开课部门 授课教师 日期时间地点人员 已排课时 已选学生数 教材 备注\n" ] c = " " for i in range(0,len(table_json["lessons"])): itm = "" itm += str(i + 1) + c itm += table_json["lessons"][i]["course"]["code"] + c itm += table_json["lessons"][i]["course"]["nameZh"] + c itm += table_json["lessons"][i]["code"] + c itm += table_json["lessons"][i]["nameZh"] + c itm += (table_json["lessons"][i]["courseType"]["nameZh"] if (table_json["lessons"][i]["courseType"] != None) else "") + c itm += table_json["lessons"][i]["openDepartment"]["nameZh"] + c teacherStr = "" for obj in table_json["lessons"][i]["teacherAssignmentList"]: teacherStr += obj["person"]["nameZh"] + "(主讲)" if obj["role"] == "MAJOR" else "(助讲)" if obj["role"] == "MINOR" else "(助理)" if obj["role"] == "ASSISTANT" else "" teacherStr += ' ' itm += teacherStr + c del teacherStr flag = table_json["lessonId2Flag"][str(table_json["lessons"][i]["id"])] if flag == "publish": if table_json["lessons"][i]["scheduleText"]["dateTimePlacePersonText"]["textZh"] != None: itm += table_json["lessons"][i]["scheduleText"]["dateTimePlacePersonText"]["textZh"].replace("\n", "") else: itm += "" elif flag == "noPublish": itm += "未发布" elif flag == "dontNeedSchedule": itm += "不排课" del flag itm += c itm += str(table_json["lessons"][i]["actualPeriods"]) + c itm += str(table_json["lessons"][i]["stdCount"]) + c courseTextbookStat = table_json["courseId2CourseTextbookStat"][str(table_json["lessons"][i]["course"]["id"])] textbookName = '-' if courseTextbookStat != None: if courseTextbookStat["textbook"] != None: textbookName = courseTextbookStat["textbook"]["name"] elif courseTextbookStat["notPublishTextbook"] != None: textbookName = courseTextbookStat["notPublishTextbook"] itm += textbookName + c del textbookName del courseTextbookStat itm += table_json["lessons"][i]["remark"] if table_json["lessons"][i]["remark"] != None else "" table_arr.append(itm) table_arr.append('\n')
with open("table.txt", "w") as f: f.write("姓名:" + namestr + '\n') f.write("学号:" + username + '\n') f.write("学期:" + term + '\n') f.write("班级:" + clsstr + '\n') f.writelines(table_arr)
|