最近正在驾校学习,学生通过一个约课系统进行选课学习。每天只能抢课两次,因为只有周六周日有时间学习,所以每到星期四放课时的时候就要进行抢课,手慢了可能就会浪费一周的时间。基于此,我想通过Python实现一个抢课脚本,每到时间点就就行自动约课。
分析与实现
模拟登录
-
约车系统是在微信端公众号,所以首先要找到了约课系统的链接
这一步很简单,直接打开公众号,然后选择复制链接就可以实现,最后他的链接如下:http://wechat.exc360.com/ukeywechat/student/index -
接下来就是登录了,捕捉到的链接如下http://wechat.exc360.com/ukeywechat/student/login
使用chrome 的F12抓包请求:
我们可以看到最重要的有几个参数
请求头:
以下请求头经过测试是必须包含的
{
“Content-Type”:“application/x-www-form-urlencoded; charset=UTF-8”,(数据的格式,表示是表单的形式)
"Referer:http":"//wechat.exc360.com/ukeywechat/student/login",(应该是来源网址,这个就是我之前没有弄所以一直登录失败)
"User-Agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"(伪装浏览器)
"X-Requested-With":"XMLHttpRequest"(这表示是ajax请求)
}
参数:
{
"password":""
"username":""
}
最后就是cookie信息了,这一步也是非常重要的:
{
"JSESSIONID":"B9581181A83392FCE075CABE004062F2",
"stjy-exc-user-type":"student"
}
可以从上图中看到请求头里面有很长的cookie,其实目前我们需要的就只有JSESSIONID和stjy-exec-user-type这两个cookie,而另一个token就是我们sign这一步所需要获取到的最重要的元素。但是因为我之前没有正常退出,所以重新登录的时候就直接获取到这个token。
如果成功响应,那么在响应头里面就会重新发送一个包含token的cookie信息,这个token就是我们后面所有操作的凭证了,所以一定要确保获取到了token值。
- 代码
在这部分的代码实现上:
1). 我遇到了一个难点,http请求中cookie的处理,通过查找资料,最终我选择cookielib这个python包来实现
2). http请求我最终选择了urllib2,因为它的可定制性会让代码实现起来比较轻松。
代码如下:
# 这一步骤是为了获取请求头里面的cookie信息
def login():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
'Content-Type': 'application/json',
}
filename = "cookie.txt"
# 声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
tmp_cookie = cookielib.MozillaCookieJar(filename)
# 利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
# tmp_cookie = urllib2.MozillaCookieJar(cookie)
handler = urllib2.HTTPCookieProcessor(tmp_cookie)
opener = urllib2.build_opener(handler)
# print(zip(headers.keys(), headers.values()))
opener.addheaders = zip(headers.keys(), headers.values())
urllib2.install_opener(opener)
# 发送请求
urllib2.urlopen(login_url)
# 正式发送登录请求
def sign():
sign_url = "http://wechat.exc360.com/ukeywechat/student/sign"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'Referer': 'http://wechat.exc360.com/ukeywechat/student/login'
}
filename = "cookie.txt"
# 声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
tmp_cookie = cookielib.MozillaCookieJar(filename)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(tmp_cookie))
# 这里需要填写用户名和密码
data = urlencode({"username": "", "password": ""})
req = urllib2.Request(sign_url, data=data, headers=headers)
response = opener.open(req)
token = json.loads(response.read())['token']
选课
成功登录之后就比较简单了:
- 查询所有的课程,筛选自己需要的课程
http://wechat.exc360.com//ukeywechat/student/bespoke/jplist
访问这个URL可以查询到所有的相关课程
通过分析:- token:在登录成功之后可以获取到
- 日期,时间:这是我们需要选择的课程的时间,是自定义的
- 请求头:除了Referer需要改一下其他部分可以是一样的。
- 响应:当成功返回之后我们会接收到一个html字符串文本,然后就可以根据自己的需要找到自己需要的课程。
- 这一步最重要的就是获取到pid也就是课程号。
课程的html文本如下:
<li class="fl" name="bespoke" id="145797" hours="1" datestr="11月22日" date="08:00-09:00" km="2" prize="0" minute="60" coachname="顾春雷C1">
<h2 class="ft12">科目二 08:00~09:00</h2>
<ul class="img_box_all">
<li><img src="/ukeywechat/resource/student/images/pople_d.png" alt=""></li>
</ul>
<div style="margin-top: 6px;font-size: 10px;overflow: hidden;">
<i class="fl"></i><span class="fl">
(60分钟)</span>
</div>
</li>
这部分的完整代码如下:
def chooseClass(token):
no_student_list = []
class_url = "http://wechat.exc360.com/ukeywechat/student/bespoke/jplist"
filename = "cookie.txt"
os.remove('john.txt')
f_handle = open("john.txt", "a")
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'Referer': index_url + token
}
# 声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
tmp_cookie = cookielib.MozillaCookieJar(filename)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(tmp_cookie))
data = {
"token": token,
"date": "2017-11-26",
"km": "",
"startime": "2017-11-26%2006:00:00",
"endtime": "2017-11-26%2021:00:00"
}
req = urllib2.Request(class_url, data=urlencode(data), headers=headers)
response = opener.open(req)
response_html = response.read()
# 解析该文本,获取所选教练的所有空课程,解析出pid,
# pid,日期,练车时间,教练
bs = BeautifulSoup(response_html, "html5lib")
# class_list = bs.findAll("li", {'name': 'bespoke', 'coachname': '教练名'})
class_list = bs.findAll("li", {'name': 'bespoke', 'coachname': '教练名'})
if len(class_list) == 0:
log("课程还没有发布,请稍后再试!")
exit()
for class_info in class_list:
tmp_class_info = {}
class_type = class_info.findAll('h2')[0].contents[0]
class_id = class_info.attrs['id']
class_datestr = class_info.attrs['datestr'].decode('utf-8')
class_date = class_info.attrs['date']
class_coachname = class_info.attrs['coachname'].decode('utf-8')
f_handle.write(class_id + " " + class_datestr + " " + class_coachname + " " + class_date + class_type)
# 查询该课程是否已经被选
img_src = class_info.find('img')
if img_src.attrs['src'] == "/ukeywechat/resource/student/images/pople_d.png":
f_handle.write(" " + "no student" + "\n")
tmp_class_info['id'] = class_id
tmp_class_info['datetime'] = class_type
no_student_list.append(tmp_class_info)
else:
f_handle.write(" " + "has student" + "\n")
print(no_student_list)
return [no_student_list[2], no_student_list[3]]
- 确认选课
直接访问下面这个url就可以了
url:http://wechat.exc360.com/ukeywechat/student/bespoke/ok
{
"token":""
"pid":""
"spick_up":0 //(是否接送,默认是0)
}
Tips:
这里可能有很多响应码,目前我只遇到过406表示当天选课已经超过,后期遇到了其他的可以添加进去,反正200表示选课成功应该是没有问题的
代码如下:
def finalClass(token, id_list):
confirm_url = "http://wechat.exc360.com/ukeywechat/student/bespoke/ok"
filename = "cookie.txt"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest',
'Referer': index_url + token
}
# 声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
tmp_cookie = cookielib.MozillaCookieJar(filename)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(tmp_cookie))
for pinfo in id_list:
data = {
"token": token,
"pid": pinfo['id'],
"spick_up": 0
}
req = urllib2.Request(confirm_url, data=urlencode(data), headers=headers)
response = opener.open(req)
response_html = response.read()
status = json.loads(response_html)['status']
if status == '406':
log("当前的预约次数已经超过了本日限制")
exit()
else:
log("课程" + pinfo['datetime'] + "选择成功!")
完整代码可以访问:yoooknight.github
总结:这是我第一次将所学习的知识用于实际的生活中,前前后后写了半个月左右,刚开始在cookie那里卡住,停止了一段时间,后面想了想不应该半途而废,就又重新开始。从哪里跌倒就从哪里爬起。
最后虽然这个脚本不是很完美,但完成了自己的需求,还是很有成就感的。
所以很多事情一定多思考,多学习,不要害怕困难,坚持下去一定会迈向成功的。
ps:
该文章同步发布在简书:https://www.jianshu.com/p/863753481d11