[12306专题]第二篇12306的验证码

1.处理12306的验证码

  • 首先在12306登陆界面https://kyfw.12306.cn/otn/login/init,按F12,选中Network选项卡,输入用户名和密码.
事实上,12306在进行用户登陆的时候,有2个请求,第一个是通过https://kyfw.12306.cn/passport/captcha/captcha-check校验验证码是否正确,如果校验通过了,才会进行用户名和密码确认.

1.1获取12306的验证码
获取12306的验证码是通过GET请求https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.5724023240618408获取验证码图片,该请求地址中的时间戳数据&0.5724023240618408是可以去掉的.
  • 因12306有https证书的验证,在python会报错,而该验证在当前是可有可无,所以首先需要关闭这个验证,关闭的方法是导入ssl,用ssl._create_default_https_context=ssl._create_unverified_context实现默认不进行证书验证.
  • 访问该地址请求到的数据是一个二进制图片文件,每一次请求到的数据是不一样的.所以需要保存到code.png文件.该文件因为包含了校验的字符串和session数据,用以下一步解码.
import urllib2,sslssl._create_default_https_context=ssl._create_unverified_context
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')
codeimg=urllib2.urlopen(req).read()
with open('code.png','wb') as f:
    f.write(codeimg)

1.2 12306的验证码请求
  • 下载验证码后,首先是要照葫芦画瓢的原封不动的模拟登陆,按照预期的结果,应该返回一个验证码校验失败.
  • 在进行校验中,有三组数据answer,login_site,rand,其中只有answer是变化的.所以answer是真正的验证码数据
    • data是一个字典格式,需要用urllib.urlencode编码成字符串才能发送给服务器.
import urllib,urllib2,sslssl._create_default_https_context=ssl._create_unverified_context
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')
codeimg=urllib2.urlopen(req).read()
with open('code.png','wb') as f: #将请求打到的验证码保存为code.png
    f.write(codeimg)
​
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-check')
data={
    'answer':'108,41',
    'login_site':'E',
    'rand':'sjrand',
    }
data=urllib.urlencode(data)
h=urllib2.urlopen(req,data).read()
print h
  • ​ 但,请求到的数据"result_code":"8"和用12306网站直接操作"result_code":"5"的结果不一样.这是为何?

  • 这是因为,使用默认的urllib2.urlopen方法,每一次的请求,都是一次全新的请求操作.这意味着,请求验证码的是一台设备,输入验证码的是另一台设备,验证当然会通不过.为了解决这个问题,我们需要引入cookielib库,通过使用这个库下面的LWPCookieJar对象,创建我们的cookie包.
  • c=cookielib.LWPCookieJar(),实际上就像买一个包包,专门用来装钥匙,访问12306一次,网站就会发一根钥匙,下次继续请求改网站,就需要用钥匙开门
  • cookie=urllib2.HTTPCookieProcessor(c),把这个包包挂在身上,让urllib2.HTTPCookieProcessor这个小姐姐(名字叫cookie)背着它
  • opener=urllib2.build_opener(cookie),创建一个opener,这个opener就是刚刚背包的小姐姐,它拥有这个包包,可以去网站买东西之前看好的东西
  • urllib2.install_opener(opener) ,把这个小姐姐加入到urllib2这个组织,让她可以随意购物.
import urllib,urllib2,ssl,cookielibc=cookielib.LWPCookieJar()  #实例化一个LWPCookieJar对象
cookie=urllib2.HTTPCookieProcessor(c) #将LWPCookieJar对象c交给urllib2的HTTPCookieProcessor处理
opener=urllib2.build_opener(cookie) #把上面实例化的cookie对象交给build_opener来构造opener
urllib2.install_opener(opener) #安装opener到urllib2,让urllib2也能使用它ssl._create_default_https_context=ssl._create_unverified_context
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')
codeimg=opener.open(req).read()
with open('code.png','wb') as f:
    f.write(codeimg)
​
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-check')
data={
    'answer':'108,41',
    'login_site':'E',
    'rand':'sjrand',
    }
data=urllib.urlencode(data)
h=opener.open(req,data).read()
print h
  • 带上cookie之后的请求就是正确的,和我们预期的一样.

2.解构12306验证码

2.1坐标结构的验证码
事实上,12306的验证码,是有8个图片组成,每个图片进行点击的时候,会出现一组坐标值.这组坐标值正好是图片相对于左上角边框的位置的一组x,y坐标.为了说明情况,修改代码将验证码作为手输的方式
import urllib,urllib2,ssl,cookielibc=cookielib.LWPCookieJar()
cookie=urllib2.HTTPCookieProcessor(c)
opener=urllib2.build_opener(cookie)
urllib2.install_opener(opener)
​
ssl._create_default_https_context=ssl._create_unverified_context
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')
codeimg=opener.open(req).read()
with open('code.png','wb') as f:
    f.write(codeimg)
code=raw_input('>>') #采用code变量,接收输入的验证码
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-check')
data={
    'answer':code, #code是刚才输入的验证码
    'login_site':'E',
    'rand':'sjrand',
    }
data=urllib.urlencode(data)
h=opener.open(req,data).read()
print h
执行代码后,请求到下图,需要输入剪纸.这里我们使用QQ自带的截图工具,帮我们确认验证码的位置.根据图片的要求,有2张图符合要求,获取到的坐标对未117,55,117,125

将验证码输入,接口返回信息验证码校验成功,说明这个原理没毛病.

2.2模拟登陆
验证码校验成功后,就可以发送登陆请求,否则登陆请求会被拒绝
#-*-coding:utf-8-*-
import urllib,urllib2,ssl,cookielib,jsonc=cookielib.LWPCookieJar()
cookie=urllib2.HTTPCookieProcessor(c)
opener=urllib2.build_opener(cookie)
urllib2.install_opener(opener)
​
ssl._create_default_https_context=ssl._create_unverified_context
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')
codeimg=opener.open(req).read()
with open('code.png','wb') as f:
    f.write(codeimg)
code=raw_input('>>')
​
data={
    'answer':code,
    'login_site':'E',
    'rand':'sjrand',
    }
data=urllib.urlencode(data)
req=urllib2.Request('https://kyfw.12306.cn/passport/captcha/captcha-check',data=data)
h=opener.open(req).read()
h=json.loads(h)
print h['result_message']
if h['result_code']=='4': #这里4是验证码校验通过的意思,校验通过再执行登陆,否则不登陆
    data={
        'username':'这里替换成你的12306账号',
        'password':'这里替换成你的12306密码',
        'appid':'otn',
        }
    data=urllib.urlencode(data)
    req=urllib2.Request('https://kyfw.12306.cn/passport/web/login',data=data)
    h=opener.open(req).read()
    h=json.loads(h)
    print h["result_message"]

python3 版本

#python3
import urllib,urllib.request,ssl,http.cookiejar,jsonc=http.cookiejar.LWPCookieJar()
cookie=urllib.request.HTTPCookieProcessor(c)
opener=urllib.request.build_opener(cookie)
urllib.request.install_opener(opener)
​
ssl._create_default_https_context=ssl._create_unverified_context
req=urllib.request.Request('https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand')
codeimg=opener.open(req).read()
with open('code.png','wb') as f:
    f.write(codeimg)
code=input('>>')
​
data={
    'answer':code,
    'login_site':'E',
    'rand':'sjrand',
    }
data=urllib.parse.urlencode(data)
req=urllib.request.Request('https://kyfw.12306.cn/passport/captcha/captcha-check',data=data.encode('utf-8'))
h=opener.open(req).read()
h=json.loads(h)
print(h['result_message'])
if h['result_code']=='4': #这里4是验证码校验通过的意思,校验通过再执行登陆,否则不登陆
    data={
        'username':'这里替换成你的12306账号',
        'password':'这里替换成你的12306密码',
        'appid':'otn',
        }
    data=urllib.parse.urlencode(data)
    req=urllib.request.Request('https://kyfw.12306.cn/passport/web/login',data=data.encode('utf-8'))
    h=opener.open(req).read()
    h=json.loads(h)
    print(h["result_message"])
经过测试,输入正确的验证码和用户名密码后,成功的登陆了12306系统.

总结

本次重点分析了12306网站的验证码机制,对于12306验证码实现的方式做了分析,并对构造opener对象方法做了详细的举例说明,对于更好的理解python会话的理念有重要的意义.
赞(0) 打赏
未经允许不得转载:http://www.yueguangzu.net逗逗网 » [12306专题]第二篇12306的验证码
分享到: 更多 (0)

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    请教一下,登录请求 一直返回这个是啥原因额HTTP/1.0 302 Moved Temporarily : https://kyfw.12306.cn/passport/web/login

    cccc6年前 (2018-01-17)回复

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏