前言
我家大儿之前苦于在成都大学开设的免费健身房抢不到预约名额,于是给他写了一个抢课的爬虫。最先是使用Python写的一个简易的爬虫程序,后来准备学Java之后决定把这个项目给搬运过来,当一个Java项目练练手。
开源项目链接:https://github.com/Savlgoodman/CDUgym
已发布可执行Jar包:https://github.com/Savlgoodman/CDUgym/releases
一.项目分析
1.分析我的Python代码
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
| import requests import json import time import os import re from datetime import datetime import keyboard
DIRT_PATH = os.path.dirname(os.path.abspath(__file__))
CURSEPAGEPATH = DIRT_PATH + r"\cursepage.txt" BOOKPAGEPATH = DIRT_PATH + r"\bookpage.txt" COOKIEPATH = DIRT_PATH + r"\cookie.txt"
def main(a, b, c, d, e): print("程序开始运行,按 Esc 键退出循环") total_times = 0 url_date_pre = "https://www.styd.cn/m/e74abd6e/default/search?date=" url_date_next = "&shop_id=612773420&type=1"
def find_curse_id(date): url1 = url_date_pre + date + url_date_next date_data1 = "date=" + date + "&shop_id=612773420&type=1"
print("无课或满了 正在监听中") time.sleep(inval_time) total_times += 1 if total_times >= MAX_TIMES: running = False except KeyboardInterrupt: print("程序被手动中断")
search_loop(a, b, c, d, e) print("程序已退出")
|
可以看Python写得是相当的屎山,如果需要看一下Python的源代码可以移步到我的下载中心去下载查看学习。
我的Python项目虽然只有一个py文件,但是其内部主要含有一些基本函数:
1.从目录的cookie.txt中获取用户cookie。(是的,需要用户手动输入)
2.定义了一个Curse_Info类,用于存储需要抢课的课程的信息。
3.由用户输入的指定日期中,循环访问该日期下的网页。该网页存在三种情况:①当日课程未发布;②当日课程已发布,目标课程未满;③当日课程已发布,目标课程已满。当出现①和③的情况的时候,需要继续循环等待目标课程的出现或者有人退课。
如图在这一天内发布了四个课程,我们需要的是晚上的健身中心的课程。
4.从课程目录界面获取目标课程的信息:课程的编号
如图可以找到在response中的course_link前的一个课程链接,里面有我们需要的课程id。当然,如果课程已满,此处的链接是javascript:alert(‘预约已爆棚,下次请赶早~’)
5.通过课程id,前往课程详情的页面获取预约所需要的元素:
我使用了正则表达式来搜索所需要的用户身份信息和课程信息:
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
| def find_card_id(text): meber_card_id = "114514" card_cat_id = "114514" curse_id = "114514" Return_List = [ "114514", "114514", "114514", ] pre_str = r'card_id="' end_str = r'"' pattern = re.compile(f"{re.escape(pre_str)}(.*?){end_str}") match = pattern.search(text) if match: meber_card_id = match.group(1) pre_str = r'cat_id="' pattern = re.compile(f"{re.escape(pre_str)}(.*?){end_str}") match = pattern.search(text) if match: card_cat_id = match.group(1) pre_str = r'course_id" value="' pattern = re.compile(f"{re.escape(pre_str)}(.*?){end_str}") match = pattern.search(text) if match: curse_id = match.group(1) Return_List[0] = meber_card_id Return_List[1] = card_cat_id Return_List[2] = curse_id return Return_List
|
6.下单预约课程
通过实践发现下单操作是通过访问https://www.styd.cn/m/e74abd6e/course/order_confirm,并传输一个订单信息,其中包括课程id,学校id,会员卡id等信息,所以我通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def book_curse(class_id): order_url = r"https://www.styd.cn/m/e74abd6e/course/order_confirm" book_url_raw = r"https://www.styd.cn/m/e74abd6e/course/order?id=" book_url = book_url_raw + class_id book_page_res = requests.get(book_url, cookies=cookies, headers=header) with open(BOOKPAGEPATH, "w", encoding="utf-8") as f: f.write(book_page_res.text) id_list = find_card_id(book_page_res.text) order_data = f"member_card_id={id_list[0]}&card_cat_id={id_list[1]}&course_id={id_list[2]}&class_id={class_id}¬e=&time_from_stamp=0&time_to_stamp=0&quantity=1&is_waiting=" order_res = requests.get( order_url, data=order_data, cookies=cookies, headers=header ) order_res_text_json = json.loads(order_res.text) print(order_res_text_json["msg"]) print("order success")
|
这个函数来实现了下单请求
2.构建Java代码项目结构
我的项目结构如下:
由于苦于不想面对黑框框,这里简单的用JavaSwing写了一个简单的窗体。主要分为了两个窗体,一个是登录界面,登录之后就可以进入预约面板。整个项目实现了UI与功能分离,主要功能的实现通Main包内的MainAPI中的函数进行调用,不过这里只实现了单次访问网页,查找是否有剩余课程,如果有就预约课程。为了方便前后通讯,MainAPI内函数的返回值为预约情况,循环部分写到了BookFrame中,让其中的文本框更好的实现日志输出。
3.实现类与对象的思想
由于Java没有方便的requests库,我便随手写了一个我自己的”request”类:
在这个类中,需要先new一个request对象,然后对这个对象实现一些访问的方法,这个有三种构建方法:url,header,cookie,method方法;带数据的构造方法:url,header,cookie,method,data;单url构造方法:获取用户头像时使用。
这里举一个带data发包的方法的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public String requestWithData() throws IOException{ URL url1 = new URL(url); HttpURLConnection conn = (HttpURLConnection) url1.openConnection(); conn.setRequestMethod(Method); conn.setRequestProperty("User-Agent", UserAgent); conn.setRequestProperty("Cookie",Cookie); conn.setDoOutput(true); conn.getOutputStream().write(data.getBytes()); BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); return response.toString(); }
|
如此便简单的获取了url 的response。
在获取课程信息的方法里面,同样使用了正则表达式来查找各个课程的状态和id,然后进入目标课程检测,当检测到目标课程有空位时,便构造BookCurse类,传入Curse_id来进行访问课程详情页获取下单所需数据。
二.项目简要
1.主界面截图
2.登录后截图
3.抢课界面
4.抢课测试
三.项目总结
随便水了一篇文章,具体没怎么细讲我的项目,但是代码很简单都看得懂,现在在攻克手机号登录获取cookie的问题,并添加更多的功能。
TODO:
1.添加用户选择界面,轻松切换不同的用户
2.添加手机号登录
3.试图将抢课程序挂载到服务器中,无需开启电脑,随时随地查看抢课状态