12036抢票软件实现(一)

一.先展示一下运行代码后的执行效果:

1.代码编译部分界面

2.登录12306官网,查看自己购票信息:

可以100%确定,运行代码后票抢到票,付款之后,它就是你的了。

补充说明一下:

1. 编译器里面展示的信息,其实我只是当时用于观察我运行的整个过程,你完全可以去掉那些打印的消息。如果你已经确

定了购买人的信息以及车票时间和地点,可以将个人账号密码信息连同购票信息一并写入文件夹中,到时候通过读取文件

内容,就可以自动获取需要的内容。

2.  需要记住几点:

             12306设定的每天只允许退票3次的操作;

             购票操作只允许在每日6:00至23:00之间;

             访问过于频繁会被12306禁IP

        所以调试的时间记得把握好。在进行下单的操作时,切莫频繁登录账号测试程序,退票太多当天就没法再测试下单步

骤了。至于第三条注意事项,本教程的抢票模式是不会出现此类情况,请放心。

3.  此程序是在票源充足的情况下执行抢票,没有用到代理池等爬虫技术,学习门槛很低。如果有童鞋想真正实现抢票(即

不断提交单,直至余票为零),需要考虑设置访问请求时间,防止过于频繁访问12306。

4.本教程目的在于实现用户登录、余票查询、提交订单三大步骤,所以很多地方都没有来得及优化,例如没有实现项目管

理、功能划分等等,有时间我会回过头进行优化,再次发布代码供大家学习。

 

二.前期准备

1.用到的开发工具:Pycharm 、火狐浏览器

(火狐浏览器是为了确保每次提交请求后,都能到抓包。360浏览器和谷歌浏览器在提交订单过程中,有些包抓取不到,不利于你

分析整个过程)

2.pycharm版本:Python2.7

(注:此处很多小伙伴肯定很烦恼为啥还在用pycharm2.7开发,其实只是我个人习惯而已,完全可以通过修改一些包的导入方式以

及输入输出函数的格式,就可以发布python3.6的版本)

3. 学习基础 :Python基础网络编程基础;(仅有python基础也可以弄明白整个实现过程)

 

开始项目,go!

1.用户登录

1.新建名为 user.py 文件,存放登录名和密码信息:

# -*- coding:utf-8 -*-
# Author: zjtMeng
UserId = 'abcd123456'
UserPwd = 'abcd123456'

2.新建 12306.py 文件,后面主要代码都是在该文件下编写。下面先设计登录函数 log() :

先附上登录部分的完整代码:

# -*- coding:utf-8 -*-
# Author: zjt
import urllib,urllib2
import ssl
import cookielib #python中内置可以操控cookie的模块
from user import UserId,UserPwd,dic_code
from json import loads

# python在安装时,默认的编码是ascii,当程序中出现非ascii编码时,python的处理常常会报这样的错UnicodeDecodeError
import sys
reload(sys)
sys.setdefaultencoding('utf8')


# 声明一个cookieJar对象来保存cookie
cookie = cookielib.CookieJar()
# 创建cookie处理器
hander = urllib2.HTTPCookieProcessor(cookie)
# 用该处理器初始化一个新的opener
opener = urllib2.build_opener(hander)
urllib2.install_opener(opener)
# 跳过验证证书步骤(此部分的'User-Agent'内容,需要填写自己的浏览器的信息,请勿直接照抄)
ssl._create_default_https_context = ssl._create_unverified_context
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
    'Referer': 'https: // kyfw.12306.cn / otn / login / init'
}

    # # 创建请求
    # res = opener.open('http://www.12306.cn')
    # for item in cookie:
    #     print 'name:' + item.name + '-value:' + item.value
    # print res

def login():
    #  请求并获取验证码图片地址
    req = urllib2.Request(
        'https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.9644364014773337'
    )
    # 添加头信息
    req.headers = headers
    imgCode = opener.open(req).read()
    with open('code.png','wb') as fn:
        fn.write(imgCode)

    # ------------------- 验证码登录 -------------------------
    # 验证验证码
    # code = raw_input("请输入验证码:")     #写入二维码坐标

    code = raw_input("请输入正确验证码对应位置的字母\n( a b c d\n  e f g h ):")     #写入二维码坐标

    user_input = ''
    for i in code:
        user_input += dic_code[i] + ','
    user_input = user_input[0:-1]

    data ={
        'answer': user_input,  #坐标二维码的结果
        'login_site':'E',
        'rand':'sjrand'
    }
    req = urllib2.Request(
        'https://kyfw.12306.cn/passport/captcha/captcha-check'
    )
    req.headers = headers
    data = urllib.urlencode(data)
    html = opener.open(req, data).read()
    # print '10001: ' + html


    # ------------------- 账号登录 -------------------------
    req = urllib2.Request(
        'https://kyfw.12306.cn/passport/web/login'
    )
    req.headers = headers
    data = {
        'username': UserId,
        'password': UserPwd,
            'appid':'otn'
    }
    data = urllib.urlencode(data)
    html = opener.open(req,data).read()
    # print '10002: ' + html

    req = urllib2.Request('https://kyfw.12306.cn/otn/login/userLogin')
    req.headers = headers
    data = {
        '_json_att':''
    }
    data = urllib.urlencode(data)
    html = opener.open(req,data)
    # print '10003: ' + html.geturl()

    req = urllib2.Request('https://kyfw.12306.cn/otn/passport?redirect=/otn/login/userLogin')
    req.headers = headers
    html = opener.open(req)
    # print '10004: ' + html.geturl()

    req = urllib2.Request('https://kyfw.12306.cn/passport/web/auth/uamtk')
    req.headers = headers
    data = {
        'appid':'otn'
    }
    data = urllib.urlencode(data)
    html = opener.open(req,data).read()
    # print '10005: ' + html

    result = loads(html)


    req = urllib2.Request('https://kyfw.12306.cn/otn/uamauthclient')
    req.headers = headers
    data = {
        'tk':result['newapptk']
    }
    data = urllib.urlencode(data)
    html = opener.open(req,data)
    # print '10006: ' + html.read()
    # print '10007: ' + html.geturl()

现在对上面的代码进行逐步解释:

登录的时候12306网站是先检验验证码是否正确,然后检查账号密码是否存在且正确

1.建立cookie对象

下方代码是基本执行cookie的操作,在网上的爬虫教程中基本都会用到这段。它的作用可以通过请求判断前后操作,是否是同一个人在操作。就好比登录账号后,点开个人中心需要是在已经获取用户权限的基础之上进行,否则页面就会跳转失败。

# 声明一个cookieJar对象来保存cookie
cookie = cookielib.CookieJar()
# 创建cookie处理器
hander = urllib2.HTTPCookieProcessor(cookie)
# 用该处理器初始化一个新的opener
opener = urllib2.build_opener(hander)
urllib2.install_opener(opener)
# 跳过验证证书步骤,默认证书是有效的
ssl._create_default_https_context = ssl._create_unverified_context
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
    'Referer': 'https: // kyfw.12306.cn / otn / login / init'
}

 

上面代码中的“User-Agent”从自己的浏览器中获取,具体操作如下:

2.获取验证码

以上图片是为了告诉你验证码图片怎么找到。为了方便看验证码,此处我采用的方式是先讲验证码图片保存下来,然后“输入验证码”。所以在 log() 函数中写入:

def login():
    #  请求并获取验证码图片地址
    req = urllib2.Request(
        'https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.9644364014773337'
    )
    # 添加头信息
    req.headers = headers
    
    #opener.open(req)表示执行get请求,然后通过read()方法获取响应结果,并用imgCode接收
    imgCode = opener.open(req).read()  

    with open('code.png','wb') as fn:
        fn.write(imgCode)

验证码图片就会保存在当前项目路径下,名称为code.png。12306的验证码采用的是坐标输入法,下图利用微信截图的尺度测量功能进行描述。8个验证码图片对应8个坐标的区间范围,只要你输入的坐标地址是在正确验证码区间范围内,且个数一致,则验证通过。

上面四张图中心的坐标地址分别为  '40,50','120,50','180,50','250,50';下面四张图中心的坐标地址分别为 '40,120','120,1200','180,120','250,120';我为了方便就在 user.py 中创建了一个字典存放这些地址:

# Author: zjtMeng
UserId = 'abcd123456'
UserPwd = 'abcd123456'

dic_code = {
            "a" : '40,50',
            "b": '120,50',
            'c': '180,50',
            'd' :'250,50',
            'e' :'40,120',
            'f' :'120,120',
            'g' :'180,120',
            'h' :'250,120'
}

这样到时候输入验证码的时候,输入‘e’和‘g’,就可以代表输入了'40,120,180,120',这时候12306.py中具体代码如下:

# ------------------- 验证码登录 -------------------------
    # 验证验证码
    # code = raw_input("请输入验证码:")     #写入二维码坐标

    code = raw_input("请输入正确验证码对应位置的字母\n( a b c d\n  e f g h ):")     #写入二维码坐标

    user_input = ''
    for i in code:
        user_input += dic_code[i] + ','
    #因为不确定需要输入多少个坐标地址,所以采用遍历的方式,
    #[0:-1]表示从第一位一直取到倒数第二位,目的是去掉倒数第一个的逗号
    user_input = user_input[0:-1] 

拿到验证码结果之后,需要新建一个request对象发送请求,而请求地址依旧可以从浏览器中获取到,方式如下。

下面开始新建一个request对象,并添加头信息和post请求内容data:

req = urllib2.Request(
        'https://kyfw.12306.cn/passport/captcha/captcha-check'
    )
    req.headers = headers

    #利用urllib.urlencode(data)将data字典转换成查询字符串,这样才可以通过post请求发送data数据
    data = urllib.urlencode(data)
    
    #变量html接收请求的响应结果,可以通过 print '10001: ' + html 打印结果,与浏览器进行对比,如果一致,则意味着通过
    html = opener.open(req, data).read()
    print '10001: ' + html  

如果执行到此处没有出问题,10001的结果应该是“{"result_message":"验证码校验成功","result_code":"4"}”

下面开始执行用户账户密码验证,先看看正常情况下浏览器上发送的是哪条请求:

所以我们依照这个操作,再次执行post请求,其中账号密码信息通过读取user.py文件中的内容获取

   from user import UserId,UserPwd,dic_code

    # ------------------- 账号登录 -------------------------
    req = urllib2.Request(
        'https://kyfw.12306.cn/passport/web/login'
    )
    req.headers = headers
    data = {
        'username': UserId,
        'password': UserPwd,
            'appid':'otn'
    }
    data = urllib.urlencode(data)
    html = opener.open(req,data).read()
    print '10002: ' + html

如果运行没有错误,结果应该和浏览器一致:

弄个动图展示一下运行效果:

按此操作,再发送剩余4个用于登录的请求,一旦执行完毕,cookie就会记录此刻你已经是登录状态,有权限可以进行下单操作。

剩下四个请求的代码如下:

    req = urllib2.Request('https://kyfw.12306.cn/otn/login/userLogin')
    req.headers = headers
    data = {
        '_json_att':''
    }
    data = urllib.urlencode(data)
    html = opener.open(req,data)
    print '10003: ' + html.geturl()

    req = urllib2.Request('https://kyfw.12306.cn/otn/passport?redirect=/otn/login/userLogin')
    req.headers = headers
    html = opener.open(req,data)
    print '10004: ' + html.geturl()

    req = urllib2.Request('https://kyfw.12306.cn/passport/web/auth/uamtk')
    req.headers = headers
    data = {
        'appid': 'otn'
    }
    data = urllib.urlencode(data)
    html = opener.open(req, data).read()
    print '10005: ' + html

    result = loads(html)

    req = urllib2.Request('https://kyfw.12306.cn/otn/uamauthclient')
    req.headers = headers
    data = {
        'tk': result['newapptk']
    }
    data = urllib.urlencode(data)
    html = opener.open(req, data)
    print '10006: ' + html.read()
    print '10007: ' + html.geturl()

正常情况执行的结果如下:

下一节继续介绍余票查询操作。余票查询可以在不执行login()情况下执行,即未登陆账号也可以查询余票情况。

猜你喜欢

转载自blog.csdn.net/zjt980452483/article/details/81167765