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 固定值
- 可以使用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)
写的很烂(见谅),有问题大家可以一起讨论。