引言
随着高速铁路时代的来临,世界发达地区高速铁路网络的不断完善与发展,高速铁路运输成为各国重要的交通方式之一。同时铁路的高速发展正在影响着人们的出行方式。
“铁路12306”是中国铁路客户服务中心推出的官方购票应用软件,与火车票务官方网站共享用户、订单和票额等信息,并使用统一的购票业务规则,软件具有车票预订、在线支付、改签、退票、订单查询、常用联系人管理、个人资料修改、密码修改等功能。于2013年12月8日正式上线试运行。而“铁路12306”虽然满足了我们基本的购票需要,但是对一些特殊群体并没有照顾到。为了改变这种情况,为此尝试使用python代码来实现抢票系统。
使用工具和库
开发环境是python3.6.5
开发工具是sublime Text
使用到的重要库:
http请求(requests库)
模拟登录流程
1.进入12306登录网站
首先导入requests包,利用requests中的Session()保持cookie。
import requests
session = requests.Session()
url = "https://kyfw.12306.cn/otn/login/init"
response = session.get(url)
2.获取验证码
在开发者工具中的Network可以很容易找到其中的验证码。从而我们可以得到验证码的 Request URL: https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.5516997730666673。
(这个验证码是实时刷新的)
接着上面的的代码,我们通过发送get请求得到验证码图片并保存。
#下载验证码
captcha_url = "https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.3874691076542045"
cap_response = session.get(captcha_url)
#保存验证码 写文件
f = open('captcha.jpg','wb')
f.write(cap_response.content) #二进制数据
f.close()
3.校验验证码
首先尝试选择验证码并点击。我们可以在Netwrok中看到一个新的响应。
从名字也可以很容易理解这是一个校验验证码的响应。我们把Headers拉到最下面可以看到通过发送POST请求来校验验证码是否正确。
这其中的answer就是我们选择验证码之后的位置。
下面是代码实现。
#校验验证码
check_url = "https://kyfw.12306.cn/passport/captcha/captcha-check"
'''
Form Data
answer: 168,45
login_site: E
rand: sjrand
'''
point = {
'1': '35,43',
'2': '108,43',
'3': '185,43',
'4': '254,43',
'5': '34,117',
'6': '108,117',
'7': '180,117',
'8': '258,117',
}
#可以确定八个图片的位置
def get_point(nums):
nums = nums.split(',')
# print(nums)
temp = []
for item in nums: # ['3', '6']
temp.append(point[item])
return ','.join(temp)
data = {
'answer': get_point(input('请输入验证码坐标:')), #'254,106'
'login_site': 'E',
'rand': 'sjrand'
}
check_response = session.post(check_url,data=data)
# print(check_response.text)
check_res = check_response.json()
# 判断校验结果
if not check_res['result_code'] == '4':
exit('验证码校验失败,请重新登录')
最后我们通过验证”result_code”是否等于4来判断验证码是否选择正确。
4.登录,校验用户名和密码
假如我们尝试选择正确的验证码登录,会发现12306是先验证验证码是否正确再去校验用户名或者密码。
通过跟验证码同样的方式,我们首先选择正确的验证码,输入用户名和密码后点击登录会发现得到了新的响应。
这里也很容易理解,浏览器得到账号和密码之后发送了一个表单给Request URL: https://kyfw.12306.cn/passport/web/login。
用与验证码类似的方法来写代码。
# 登录 ,校验用户名和密码
import config
login_url = 'https://kyfw.12306.cn/passport/web/login'
login_data = {
"username": config.username,
"password": config.password,
"appid": "otn"
}
login_response = session.post(login_url,data=login_data)
这里我新写了一个代码来调用用户名和密码。
到这里我们差不多完成了验证码问题和登录问题。
当时我也简单的认为这里已经登录全部完成。但是之后测试时发现得到的网页中没有显示个人的名字。通过再次检查发现登录完成后还发送了两个请求得到权限来完整的完成登录。
4.获取权限
经过反复测试之后发现需要两个请求来获取权限。
首先我们发送请求到uamk中,FortData为:
#获取权限
uamtk_url = 'https://kyfw.12306.cn/passport/web/auth/uamtk'
uamtk_data = {
'appid': 'otn'
}
response = session.post(uamtk_url, data=uamtk_data)
print(response.text)
uantk_response = response.json()['newapptk']
我们尝试输出得到的返回值,发现其中的”newapptk”就是第二个请求发送的表单内容
所以我们定义一个变量来存储这个值。再将它发送给”uamauthclient”中。
# 获取权限
auth_url = 'https://kyfw.12306.cn/otn/uamauthclient'
auth_data = {
'tk': uantk_response
}
response = session.post(auth_url, data=auth_data)
if response.json()['result_code'] == 0:
print(response.text)
完成这两步之后我们输出返回值。可以看到这里输出了我们用户的姓名。
5.跳转页面
# 跳转登陆页面
login_redirect = 'https://kyfw.12306.cn/otn/login/userLogin'
response = session.get(login_redirect)
check_url = 'https://kyfw.12306.cn/otn/login/checkUser'
data = {
'_json_att': ''
}
response = session.post(check_url,data=data)
# print(response.text)
假如我们尝试输出可以得到用户的姓名来验证是否已经获取权限并登录成功。
到此为止我们已经完整的完成了登录过程。之后便是购票等步骤。
下面附上代码运行的结果。
测试完成后我们将整个代码进行封装。
import requests
import random
import config
class TicketRob:
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
})
self.index_url = 'https://kyfw.12306.cn/otn/login/init'
self.captcha_url = 'https://kyfw.12306.cn/passport/captcha/captcha-image'
self.login_url = 'https://kyfw.12306.cn/passport/web/login'
self.check_captcha_url = 'https://kyfw.12306.cn/passport/captcha/captcha-check'
self.uamtk_url = 'https://kyfw.12306.cn/passport/web/auth/uamtk'
self.auth_url = 'https://kyfw.12306.cn/otn/uamauthclient'
self.point = {
'1': '35,43',
'2': '108,43',
'3': '185,43',
'4': '254,43',
'5': '34,117',
'6': '108,117',
'7': '180,117',
'8': '258,117',
}
self.dict = {}
def get_point(self, nums):
nums = nums.split(',')
# print(nums)
temp = []
for item in nums: # ['3', '6']
temp.append(self.point[item])
return ','.join(temp)
# 检查用户名密码
def main(self, username, password):
data = {
'username': username,
'password': password,
'appid': 'otn'
}
self.session.get(self.index_url) # 1
self.get_captcha() # 2
check_res = self.check_captcha() # 3
if check_res:
response = self.session.post(self.login_url, data=data) # 4
if response.json()['result_code'] == 0:
tk = self.get_tk() # 5
auth_res = self.get_auth(tk) # 6
# 获取验证码
def get_captcha(self):
data = {
'login_site': 'E',
'module': 'login',
'rand': 'sjrand',
str(random.random()): ''
}
response = self.session.get(self.captcha_url, params=data)
with open('captcha.jpg', 'wb') as f:
f.write(response.content)
# 校验验证码
def check_captcha(self):
data = {
'answer': self.get_point(input('请输入正确的图片序号>>>:')),
'login_site': 'E',
'rand': 'sjrand'
}
response = self.session.post(self.check_captcha_url, data=data)
if response.json()['result_code'] == '4':
return True
else:
print('验证码选择错误,请重新选择')
return False
# 获取权限token
def get_tk(self):
uamtk_data = {
'appid': 'otn'
}
response = self.session.post(self.uamtk_url, data=uamtk_data)
return response.json()['newapptk']
# 获取权限
def get_auth(self, tk):
auth_data = {
'tk': tk
}
response = self.session.post(self.auth_url, data=auth_data)
if response.json()['result_code'] == 0:
print(response.text)
return True
return False
if __name__ == '__main__':
ticket = TicketRob()
ticket.main(config.username, config.password)