python3 requests 实现12306购票登录模块
12306登录模块分析
第一次写文章,记录一下学习的内容。今天先记录登录模块的分析和实现。
在博客上看见一些大佬用的是splinter webdriver写的12306购票过程。
由于我之前学习了一阵子requests库,所以以下使用python3+requests实现12306购票。(当然也可以用python2 的urllib来实现)
- 安装requests库
pip3 install requests
- 基本思路
首先,我们可以使用谷歌浏览器的开发者模式,F12,来先分析一波登录12306的过程。(推荐大家一个好用的抓包工具Fiddler)
- 打开登录界面,https://kyfw.12306.cn/otn/login/init
- 先随意输入一个账号密码,再随意选验证码。(我们可以百度验证码类型,也可以自己尝试,可以知道12306的验证码是坐标型的验证码。)点击登录可以发现Network中多出一个xhr,点击查看(Headers Preview Response Cookies Timing都看看)可以发现Response返回的是:
{"result_message":"验证码校验失败","result_code":"5"}
- 再查看请求url https://kyfw.12306.cn/passport/captcha/captcha-check post数据为:
answer: 91,53,107,109 为验证码坐标
login_site: E 固定值
rand: sjrand 固定值
为验证码坐标
login_site: E 固定值
rand: sjrand 固定值
- 可以使用qq截图的坐标来输入,从红色箭头开始向正确验证码拉动。
- 输入正确的验证码后,查看Response为:
{"result_message":"验证码校验成功","result_code":"4"}
- 多处一个login的xhr,URL为 https://kyfw.12306.cn/passport/web/login Response为:
{"result_message":"密码输入错误。如果输错次数超过4次,用户将被锁定。","result_code":1}
- 说明验证码验证通过,现在是账号密码问题,那么我们输入自己的正确的账号密码就可以完成登录了呗。
代码:
import requests
from json import loads
from user import username,password #user.py 里面是自己正确的账号密码
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
disable_warnings(InsecureRequestWarning) #这三行作用是取消https警告
locate={
'1':'44,44,',
'2':'114,44,',
'3':'185,44,',
'4':'254,44,',
'5':'44,124,', #8个图的坐标代号
'6':'114,124,',
'7':'185,124,',
'8':'254,124,',
}
head={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36',
'Referer': 'https://kyfw.12306.cn/otn/login/init',
} #加header
session=requests.Session() #定义session,保持一个会话,验证码post和用户post保持同一个会话,之后的下单跨域保持登录
session.verify=False #取消验证SSL
def login():
resp1 = session.get(
'https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&',
headers=head)
with open('code.png','wb') as f:
f.write(resp1.content)
print('请输入验证码坐标代号:')
code=input()
write=code.split(',')
codes=''
for i in write:
codes+=locate[i]
data={
'answer': codes,
'login_site': 'E',
'rand': 'sjrand'
}
resp=session.post('https://kyfw.12306.cn/passport/captcha/captcha-check',headers=head,data=data)
html=loads(resp.content)
if html['result_code']=='4':
print('验证码校验成功!')
login_url='https://kyfw.12306.cn/passport/web/login'
user={
'username': username,
'password': password,
'appid': 'otn'
}
resp2=session.post(login_url,headers=head,data=user)
html=loads(resp2.content)
print(resp2.text)
if html['result_code'] == 0:
print('登陆成功!')
else:
print('登陆失败!')
else:
print('验证码校验失败,正在重新请求页面...')
login()
pass
login()
运行截图:
到这里如果只是验证登录的话已经完成,要实现下单的话还没完,因为虽然显示登录成功,但是使用Fiddler抓包可以看到,在完成上述的登录后还需要进行两次验证,才能保证session验证登录成功,保持登录,之后下单过程才不会出错。
两次验证URL分别为:
https://kyfw.12306.cn/passport/web/auth/uamtk post参数为 appid :otn
https://kyfw.12306.cn/otn/uamauthclient post参数为 第一次验证的Response的tk值
session保持登录代码:
import requests
from json import loads
from user import username,password
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
disable_warnings(InsecureRequestWarning)
locate={
'1':'44,44,',
'2':'114,44,',
'3':'185,44,',
'4':'254,44,',
'5':'44,124,',
'6':'114,124,',
'7':'185,124,',
'8':'254,124,',
}
head={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36'
}
now_session=requests.Session()
now_session.verify=False
def login():
print('-----------------验证码验证-----------------')
resp1 = now_session.get(
'https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.8430851651301317',
headers=head)
with open('code.png','wb') as f:
f.write(resp1.content)
print('请输入验证码坐标代号:')
code=input()
write=code.split(',')
codes=''
for i in write:
codes+=locate[i]
data={
'answer': codes,
'login_site': 'E',
'rand': 'sjrand'
}
resp=now_session.post('https://kyfw.12306.cn/passport/captcha/captcha-check',headers=head,data=data)
html=loads(resp.content)
if html['result_code']=='4':
print('验证码校验成功!')
print('-----------------登录中-----------------')
login_url='https://kyfw.12306.cn/passport/web/login'
user={
'username': username,
'password': password,
'appid': 'otn'
}
resp2=now_session.post(login_url,headers=head,data=user)
html=loads(resp2.content)
print(html)
if html['result_code']==0:
print('登陆成功!')
yzdata={
'appid':'otn'
}
tk_url='https://kyfw.12306.cn/passport/web/auth/uamtk'
resp3=now_session.post(tk_url,data=yzdata,headers=head)
print('-----------------第一次验证-----------------')
print(resp3.text)
login_message=resp3.json()['newapptk']
print('loginMessage=',login_message)
yz2data={
'tk':login_message
}
client_url='https://kyfw.12306.cn/otn/uamauthclient'
resp4=now_session.post(client_url,data=yz2data,headers=head)
print('-----------------第二次验证-----------------')
print(resp4.text)
else:
print('登陆失败!')
else:
print('验证码校验失败,正在重新请求页面...')
login()
pass
login()
运行效果:
备注:
python2 urllib 的session由下面定义:
cj = http.cookiejar.CookieJar()
pro = urllib.request.HTTPCookieProcessor(cj)
opener = urllib.request.build_opener(pro)
urllib.request.install_opener(opener)
写的很烂(见谅),有问题大家可以一起讨论。