终于搞定了回家车票

快要春节了,老板发话:‘提前完成工作可以提前回家‘;于是老猫每天加班加点赶进度,估计提前1周回家;正当老猫沉浸在幸福之时,老板过来关心问我:‘老猫,车票买了吗,买不到晚几天走吧,那会好买‘;忽然有种被算计的感觉!!

人无远虑必有近忧,车票是个大问题,老猫要买的车票这两天放票;于是每天发动同事帮我抢票,可是连续两次都没抢到;老猫有点慌了,买不到车票怎么办?

那么多车票,为什么好几个人抢连续两天都抢不到;

老猫分析其中可能存在原因,分析过程如下:

购票流程分析:

1>老猫买的是比较紧张的车次,抢票人数远远大于出票数量;
2>每次抢票和网速,手速有一定关系;
3>虽然感觉每次手动购票速度很快,但是购票人数多,手速快的也很多,对比而言也就不快了。

购票中存在影响因素:

老猫又仔细分析了网页购票过程:

1>登录,验证码与密码登录;
2>刷票,选择始发站,终点站,日期,车次;
3>出票后点击预购;
4>等待排队,选择乘车人;

理论上大家刷票过程都是一样的,但是几个因素会影响我们购票结果:

1>第一步登录,这个没有问题,老猫和大家都会提前登录;
2>第二步:先看个示意图:

老猫提前选择好车次等信息,到出票时刻点击刷票;但是网速与浏览器渲染页面速度,可能会影响下一步操作;
3>刷票之后,如果出现购买车次,马上点击预购,这里会和自己手速有关,几百毫秒过去了,老猫可能已经排在几百人之后了;
4>点击预购,出现下面页面:

这里考验手速:点击购票人与订单提交,然后凭天由命吧。

结合上面分析:抢热点车次,真有点撞大运的感觉。如何解决问题呢?

1>使用第三方软件代购或者购买加油包;
2>拼车回家;

出去安全考虑,还是应该选择第一个方式。但是老猫有点好奇,为什么第三方软件或者加油包能够购买成功?老猫猜测可能原因:

老猫认为每个账号就是一个购票者,这些账号由脚本控制,到时间点开始抢票,脚本速度肯定要比人的速度快,所有购买成功几率要大于认为操作。以上部分构成纯属瞎猜,如有雷同,纯属巧合。
这样来看,老猫除了和几万个人竞争,还要和第三方软件竞争,所以热点车次的车票更难买到了。这样大家也就理解为什么第三方软件购买成功几率更大。
为了验证这个猜测,老猫打算使用Pyhon脚本自动登录12306,说干就干。

1:登录过程分析:

12306的验证码是比较恶心的,每次弄得不清楚,还整一些不认识的东西;老猫曾经连续10次选择错误,最后系统警告我刷码频繁,最后只能让人代买。
12306网页版登录过程分下面几个部分:

1>输入用户名与密码,如果不输入,登录提示错误;
2>图片验证;
2>账号与密码验证;

这里我们来看后两个步骤:

1.1 验证码分析

我们先借助浏览器分析登录行为;
a)输入用户名与密码,界面如下:

b)点击刷新,更新验证码:
为了防止失效,看到效果,我们刷新验证码;
请求地址:https://kyfw.12306.cn/passport/captcha/captcha-image64?
请求参数:

{
    ‘login_site‘: ‘E‘,
    ‘module‘: ‘login‘,
    ‘rand‘: ‘sjrand‘,
    ‘1546822674059‘:‘‘,#当前时间戳,
    ‘callback‘: ‘jQuery19109060005139400158_1546821765087‘,#使用固定值
    ‘_‘: ‘1546821765097‘#1546821765097,通过浏览器观察:每次请求值加1
}

c)选择验证码并登陆(尝试错误选择,观察结果):
过程如下 :

查看请求信息:

验证失败,应答信息为:

jQuery19109060005139400158_1546821765087({"result_message":"验证码校验失败","result_code":"5"});

d)图片验证分析:
图片验证请求地址:https://kyfw.12306.cn/passport/captcha/captcha-check? ;
请求参数:

{
    ‘callback‘: ‘jQuery19109060005139400158_1546821765087‘,#与上一步请求验证图片相同
    ‘answer‘: ‘111,52,109,96‘,#根据图片选择点击位置
    ‘rand‘: ‘sjrand‘,
    ‘login_site‘: ‘E‘,
    ‘_‘: ‘1546821765102‘#每次请求值加1,
}

e)如何选择点击位置?
有下面几种方式:

1>机器学习图片识别(设计内容较多,准确率不敢保证);
2>云打码(老猫自己没有折腾);
3>将图片下载下来,自己选择位置,手动填入位置;

老猫实现方式:

1>观察请求信息,坐标应该是正确图片大概位置,
2>图片信息:小图长宽为70,
第一排坐标:(35,35),(105,35),(175,35),(245,35);
第二排坐标:(35,105),(105,105),(175,105),(245,105);
3>根据图片提示,选择对应位置图片,然后返回坐标;例如选择0,1,返回值:
35,35,105,35

如果选择成功,应答消息中会有"验证码校验成功"信息;
我们根据上面分析来使用代码完成这一过程。

2:验证码处理:

老猫将其过程分析下面几步:

1>下载图片,然后打开图片查看;
2>输入正确图片位置,获取坐标;
3>提交验证;

实现需要知识点:

1>requests模块,cookie管理;
2>Python基本知识点与面向对象编程;

实现思路:

1>通过浏览器获取当前callback值;
2>请求并保存验证码;
3>手动打开图片,输入正确位置,获取坐标;
4>请求验证,如果验证失败重复2~4步骤;

验证码验证代码实现:

import re
import base64
import json
import requests
import time

class login12306:
    #初始化
    def __init__(self, callback, nums, headers):
        self.callback = callback
        self.nums = nums
        self.headers = headers
        self.s = requests.Session()
        self.fpath = ‘./code.jpg‘
    #发起请求
    def do_request(self, req_type=‘GET‘, url=‘‘,pdata=None, jdata=None):
        if req_type == ‘POST‘:
            req = req = requests.Request(req_type, url, data=pdata,                                          json=jdata,headers=self.headers)
        else:
            req = requests.Request(req_type, url, params=pdata,                                    headers=self.headers)
        pre = self.s.prepare_request(req)
        try:
            resp = self.s.send(pre, timeout=3)
            return resp
        except:
            return None
    #获取验证码坐标
    def gen_pointlist(self):
        #提示输入位置
        indexs = input(‘input 1~8 : \n‘)
        width = 35
        points = []
        select = []
        #生成图片对应位置
        for i in range(0, 8):
            points.append([(i) % 4 * width * 2 + width, width * (i // 4 * 2) + width])
        #根据位置后去坐标点
        [select.extend(points[int(index)-1]) for index in indexs]
        tmp = [str(val) for val in select]
        return ‘,‘.join(tmp)
    #下载验证码
    def downimg(self):
        url = ‘https://kyfw.12306.cn/passport/captcha/captcha-image64?‘
        ts = str(int(time.time() * 1000))
        pdata = {
            ‘login_site‘: ‘E‘,
            ‘module‘: ‘login‘,
            ‘rand‘: ‘sjrand‘,
            ts: ‘‘,
            ‘callback‘: self.callback,
            ‘_‘: str(self.nums)
        }
        req = self.do_request(‘GET‘, url, pdata=pdata)
        if not req:
            return
        m = re.search(r‘\((.*)\)‘, req.text)
        text = m.group()[1:-1]
        jdata = json.loads(text)
        img = base64.b64decode(jdata[‘image‘])
        with open(self.fpath, ‘wb‘) as f:
            f.write(img)
        return True
    #获取验证码信息
    def get_qcinfo(self):
        #下载验证码
        if self.downimg():
            #下载完成之后打开图片,并输入位置获取坐标
            indexs = self.gen_pointlist()
            return indexs
    #验证码校验
    def qc_verify(self):
        indexs = self.get_qcinfo()
        if not indexs:
            return False
        self.nums += 1

        #验证码输入检查
        url = ‘https://kyfw.12306.cn/passport/captcha/captcha-check?‘
        print(indexs)
        jdata = {
            ‘callback‘: self.callback,‘answer‘: indexs,‘rand‘: ‘sjrand‘,
            ‘login_site‘: ‘E‘,‘_‘: self.nums
        }
        req = self.do_request(‘GET‘, url, pdata=jdata)
        #输入成功返回True
        if (‘验证码校验成功‘) in req.text:
            print(‘验证码成功‘)
            self.indexs = indexs
            return True
        return False
    #用户名与密码登录
    def user_login(self):
        pass
    #登录接口
    def start_login(self):
        while True:
            if self.qc_verify():
                break
            time.sleep(2)
        self.user_login()

if __name__ == ‘__main__‘:
    callback = ‘jQuery19109060005139400158_1546821765087‘,
    nums = 1546821765097
    hds = {‘Accept-Language‘: ‘zh-CN,zh;q=0.9‘,
               ‘Connection‘: ‘keep-alive‘,
               ‘Host‘: ‘kyfw.12306.cn‘,
               ‘Cache-Control‘: ‘no-cache‘,
               ‘User-Agent‘: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36‘,
               }
    obj =login12306(callback, nums, hds)
    obj.start_login()

运行输出结果如下:

3:用户登录:

浏览器中,输入正确验证码,用户名与密码;点击登录,登录过程如下图:

我们完善user_login方法,代码如下:

    def user_login(self):
        url = ‘https://kyfw.12306.cn/passport/web/login‘
        jdata = {
            ‘username‘: ‘myusername‘,#更换自己用户名
            ‘password‘: ‘mypwd‘,#更换成自己密码
            ‘appid‘: ‘otn‘,
            ‘answer‘: self.indexs,
        }

        req = self.do_request(‘POST‘, url, pdata=jdata)
        items = [
            {‘url‘:‘https://kyfw.12306.cn/otn/login/userLogin‘, ‘f‘:‘GET‘},
            {‘url‘:‘https://kyfw.12306.cn/otn/passport?redirect=/otn/login/userLogin‘,‘f‘:‘GET‘},
        ]
        for item in items:
            url = item.get(‘url‘)
            f = item.get(‘f‘)
            req = self.do_request(f, url)

        url = ‘https://kyfw.12306.cn/passport/web/auth/uamtk‘
        info = {‘appid‘: ‘otn‘}
        req = self.do_request(‘POST‘, url, pdata=info)

        url = ‘https://kyfw.12306.cn/otn/uamauthclient‘
        jdata = req.json()
        info = {‘tk‘: jdata[‘newapptk‘]}
        req = self.do_request(‘POST‘, url, pdata=info)

        req_items = [
            {‘url‘:‘https://kyfw.12306.cn/otn/login/userLogin‘,‘f‘:‘GET‘},
            {‘url‘:‘https://kyfw.12306.cn/otn/login/conf‘,‘f‘:‘POST‘},
            {‘url‘:‘https://kyfw.12306.cn/otn/index/initMy12306Api‘,‘f‘:‘post‘},

        ]
        for item in req_items:
            url = item.get(‘url‘)
            f = item.get(‘f‘)
            req = self.do_request(f, url)
        print(req.text)
        return req

再次运行代码结果如下:

登录成功了,我们可以提取应答消息中的信息,判断是否登录成功。

对于用户名密码请求地址,我们可以不用关心具体作用,只需要记录请求地址与参数,有的数据可能需要在应答中提取。基于这个基础,我们可以使用Python完成预定车次选择,查找账户中联系人,购票这一系列操作。
经过分析,可以看到使用脚本操作,省去了页面渲染与人为点击这两个操作,节省了时间,这样订单提交速度就比其他人快,购买成功几率明显增加。

但是老猫不打算使用,因为使用的人越多,这种现象越猖獗;

但是,老猫怎么回老家呢?老猫一天心不在焉,没有一点工作状态。老板看在心里也十分为我着急。

最终结果:

原文地址:http://blog.51cto.com/huangyg/2339960

时间: 2024-07-30 21:33:24

终于搞定了回家车票的相关文章

Access denied for user ''@'localhost' to database 'mysql'。网上找了一些方法,终于搞定了。

转载:http://blog.csdn.net/lilian129/article/details/9297881 这几天用空密码登录mysql后,然后修改mysql默认密码,使用mysql表出现过这个问题,提示:ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'mysql'.网上找了一些方法,终于搞定了. 我用的是xampp集成的mysql,之前空密码能登进去phpmyadmin,但怎么也进不去phpm

第二天,终于搞定 —— 2014.10.28

npm install 报错 bower ENOGIT git is not installed or not in the PATH 的问题终于搞定. 在angular-phonecat文件夹打开nodejs cmd.exe,运行npm install,就报错的问题,终于被我搞定了,期间查阅了N多资料,都是泪啊,再一次感受到了baidu和google的差距,在stackoverflow上终于找到了解决方法,亏的英语没差到那种完全看不懂的程度啊... 查阅资料是提示是把Git的默认cmd.exe

模拟摄像头,AV视频信号线解码,PAL制 NTSC,输入解码显示,终于搞定,记录下!

咱们常用的摄像头,监控等,大多数都是AV信号,国内制式都是PAL,采用同轴,传输,这样的好处在于,传输距离可以很长,且线路简单.视频阵列中也大多使用av摄像头. 常用的机顶盒,电视机,机顶盒最终输出的图像信号到电视机,绝大部分都是CVBS,混合信号,其中视频信号为AV,PAL制式. 所以pal制式信号,是相当流行,也相当适用.之前搞定了CMOS摄像头驱动,大多数并口信号线,ttl电平,因为信号线多,不易形成整列,如二马搞的汽车全景摄像头应该都是av信号,大家也可以看看自己的汽车,倒车摄像头是不是

静态dll的问题终于搞定了

导入plugin,构建qapplicationhttps://forum.qt.io/topic/60940/qt-static-dll-x64-using-qapplication-issues/2我构建了qapplication,但是没调用exec,想不通为啥能工作 主程序里exec了,dll里面有exec 你是怎么调用dll的?也许在LoadLibrary/Plugin的时候,帮你对接了?我是隐式调用的 你导入什么plugin了qwindowsintegration

利用python+seleniumUI自动化登录获取cookie后再去测试接口,今天终于搞定了

#coding=utf-8from selenium import webdriverfrom selenium.common.exceptions import NoSuchElementExceptionimport unittest,timeimport requests #登录模块函数def login(): u'''gdtmpd登录''' driver=webdriver.Chrome() driver.get('登录地址url') nowhandle=driver.current_w

终于搞定PyTorch+YoloV3+TensorRT,在aws上利用G4实例和DLAMI进行迁移训练

原文地址:https://www.cnblogs.com/cloudrivers/p/12656276.html

一步一步搞定Python3.6编码问题

Python中的编码问题很蛋疼,我们遇到这类问题有时候会使用下面几个步骤处理该问题: 1:这么低级问题,别问同事了太丢人,赶紧百度: 2:卧槽,出了一堆广告,骂个娘,赶紧找相关解决问题方式: 3:尼玛,终于搞定,原来这么简单,赶紧搞定其它工作,晚上回头在查原因: 4:傻B单位每天加班,到家都11点了,赶紧洗洗睡了:等会,先来局王者农药...... 5:编码问题早就忘了: 6:过了几天又出现编码错误,,从第1步开始重新来过. 今天我们就花一局王者农药时间搞定Python3.6的编码问题. 搞清楚编

Centos安装FastDFS+Nginx(一天时间搞定)

最近在研究和使用Fastdfs,别人搭的环境,终究是别人的,绝知此事要躬行~躬行啊~      下面的脚本主要参考了官方的INSTALL文件,这个是比较权威的,部分地方和实际情况不一致.比如配置文件的名字,Fastdfs的安装位置. 一.下载    https://github.com/happyfish100/fastdfs fastdfs-5.05.zip https://github.com/happyfish100/libfastcommon libfastcommon-1.0.7.zi

vpn+squid搞定内网才能访问的svn

业精于勤荒于嬉,愿程序猿们鼓起干劲,坚持学下去! 目录 前言 一.squid安装和使用 二.本机的svn代理设置 前言 今天由于要修改公司项目的配置文件,于是不得不秒登vpn,登上svn跳板机,把要修改的文件update下来.修改完成后,在跳板机上传,最后再经过几道程序,终于更新到线上了. 对于不能在个人电脑上update公司svn的代码,着实有些不方便.当然公司是为了代码安全考虑,多加一点防范,我们是可以理解的. 于是,又开始琢磨怎么把svn的代码搞到本机.其实,这问题蛮简单的,只要对于网络知