12306自动刷票下单-登录篇

眉间皱痕 提交于 2020-01-24 20:27:05

12306网站推出图片验证码以后,对于抢票软件就提出了更高的要求,本篇并不涉及自动识别验证码登录(主要是博主能力所限),提供一个途径-打码平台,这个几乎是可以破解所有验证码了,本篇主要是分享一下12306网站登录的流程的学习,勿吐槽,有问题请指正,博主也是刚开始接触爬虫,大家共勉共勉。

废话不多说了,直接干吧

 

首先打开12306登录页面https://kyfw.12306.cn/otn/login/init

 这里写图片描述

输入账号密码直接登录,会转入到下面的页面https://kyfw.12306.cn/otn/index/initMy12306

 这里写图片描述

红线划掉的就是用户名,那么我们最终就是要访问这个网页查找到我们的用户名,简单吧

 

好了,不闹了,看一下我们整个登录过程中的请求吧

 这里写图片描述

这里缺少了打开登录页面的请求,如果你没有发现,那我刚说了你就应该发现,这个登录页面是必须的,这里说一下我的理解,登录过程中我们是要发送验证码的,验证码作为独立的请求发送,那么服务器是要知道这个验证码是由张三发过来的还是由李四发过来的,所以当我们打开登录页面的时候,服务器会记录session或者cookie,我们之后的请求都通过携带cookie让服务器知道是我张三发送的请求,而不是李四。大概就是这么回事,可能说的不太对,我也就理解了这么点,见谅见谅。

这里我们使用requests库,不要太方便

import requests

# 一个提供UserAgent的库,不用自己再去搞那么多了,方便
from fake_useragent import UserAgent

# 禁用安全请求警告
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
disable_warnings(InsecureRequestWarning)

session = requests.session()
# 设置不验证SSL,你应该看到了HTTPS
session.verify = False

ua = UserAgent(verify_ssl=False)

# 请求头,最最基础的反爬伪装
headers = {
    "User-Agent": ua.random,
    "Host":"kyfw.12306.cn",
    "Referer":"https://kyfw.12306.cn/otn/passport?redirect=/otn/"
}

# 打开登录页面
url = "https://kyfw.12306.cn/otn/login/init"
session.get(url, headers=headers)

requests库的session会为我们保存cookie信息,只要我们继续使用session请求即可。

这里要先解释一下,我使用的是Chrome浏览器,但是很多请求里面确看不到response数据,真的很操蛋,我写的时候每一个请求都用代码打印出来,很痛苦,

因为想写篇博客,要给大家截图,所有装了个虚拟机Windows xp,安装了Fiddler才看到一些信息,我还是习惯用Chrome,我尽量按照Chrome的方式去说明。 这里写图片描述

在所有的请求里面,我们主要关注Type等于document和xhr的请求,按照从上到下的方式就是整个流程中请求的先后顺序。我们可以看到init后面uamtk和captcha_js.js?_=1510993251087, 这里写图片描述

很明显这个是我们打开登录页面时发送的请求,服务端告诉我们:你还没登录呢,废话我只是打开登录界面,当然没登录了。不过我们大概知道了,发送https://kyfw.12306.cn/passport/web/auth/uamtk这个请求,服务器会给我们反馈一些登录信息。后面那个请求很明显是js,我们暂时不用管。

下面是这个xhr请求:https://kyfw.12306.cn/passport/captcha/captcha-check 这里写图片描述

这个是发送验证码的请求,12306的验证码属于坐标型验证码,所有我们看到发送的是一段坐标,在这个请求上面我们看到https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&0.9919795512111436 这里写图片描述

哦,这个是请求验证码的,要发送验证码请求,自然要先获取验证码喽,多请求几次发现表单里除了最后一个随机数以外,其他的数据没有变化。接下来就是验证码的坐标了

 这里写图片描述 

首先是找原点坐标,这个跟正常登录发送的坐标对比一下,大概就能确定,然后是确定坐标的拼接,x1,y1还是y1,x1,这个也是和正常登录发送的坐标大概对比一下就能确定了,我这里只说大概原理,具体情况自己去尝试一下。下面上代码

验证码函数

def captcha():
    # 请求数据是不变的,随机数可以使用random.random()
    data = {
        "login_site": "E",
        "module": "login",
        "rand": "sjrand",
        "0.17231872703389062":""
    }

    # 获取验证码
    param = parse.urlencode(data)
    url = "https://kyfw.12306.cn/passport/captcha/captcha-image?{}".format(param)
    response = session.get(url, headers=headers)
    if response.status_code == 200:
        # 获取验证码并打开,然后...手动找一下坐标吧,我开始就说了不涉及自动识别验证码的
        file = BytesIO(response.content)
        img = Image.open(file)
        img.show()

    positions = input("请输入验证码: ")
    # 发送验证码
    data = {
        "answer": positions,
        "login_site": "E",
        "rand": "sjrand"
    }

    url = "https://kyfw.12306.cn/passport/captcha/captcha-check"
    response = session.post(url, headers=headers, data=data)
    if response.status_code == 200:
        result = json.loads(response.text)
        print(result.get("result_message"))
        # 请求成功以后返回的code是4,这个看请求信息就知道了
        return True if result.get("result_code") == "4" else False
    return False

验证码通过以后,就要发送账号登录了,继续看下面的请求https://kyfw.12306.cn/passport/web/login 这里写图片描述

账号密码登录成功,看到没?好了

 

 

还没完呢,为啥捏?还没看到initMy12306这个请求呢 继续往下撸吧,https://kyfw.12306.cn/passport/web/auth/uamtk,这个请求熟悉不?不熟悉的去翻前面 这里写图片描述

对比一下,前后不一样吧,验证通过,开心吧,哈哈! 咦!好像还多了点东西哦,newapptk什么鬼呢?不明白,继续看下面

敲黑板!!!是看后面的请求,你在看哪个下面,睡着了吗?https://kyfw.12306.cn/otn/uamauthclient 这里写图片描述

验证通过,不过重点我都圈起来了,还看不见吗?tk,你再看看上面newapptk,明白了吗?上面的请求服务器返回了newapptk数据,我们通过下面的请求把这个值赋给tk,再发送给服务器,然后服务器告诉我们验证通过,

apptk和前面的tk一样,有什么用呢?不知道,那就往下看,哎呀,到站了 https://kyfw.12306.cn/otn/index/initMy12306 这里写图片描述

已经成功了,有我们的账号名了,就是红点的地方,我当然不会给你看我的账号名了,到这里就真的完了,apptk没用到?没用就没用呗,终于结束了 

我还要贴一下代码,差点忘了

完整的代码

# -*- coding: utf-8 -*-
import json
from urllib import parse
from io import BytesIO
from config import *

import requests
from PIL import Image
from fake_useragent import UserAgent

# 禁用安全请求警告
from urllib3 import disable_warnings
from urllib3.exceptions import InsecureRequestWarning
disable_warnings(InsecureRequestWarning)

session = requests.session()
session.verify = False

ua = UserAgent(verify_ssl=False)
headers = {
    "User-Agent": ua.random,
    "Host":"kyfw.12306.cn",
    "Referer":"https://kyfw.12306.cn/otn/passport?redirect=/otn/"
}

def login():
    # 打开登录页面
    url = "https://kyfw.12306.cn/otn/login/init"
    session.get(url, headers=headers)
    # 发送验证码
    if not captcha():
        return False

    # 发送登录信息
    data = {
        "username":USER_NAME,
        "password":PASSWORD,
        "appid":"otn"
    }
    url = "https://kyfw.12306.cn/passport/web/login"
    response = session.post(url, headers=headers, data=data)
    if response.status_code == 200:
        result = json.loads(response.text)
        print(result.get("result_message"), result.get("result_code"))
        if result.get("result_code") != 0:
            return False

    data = {
        "appid":"otn"
    }
    url = "https://kyfw.12306.cn/passport/web/auth/uamtk"
    response = session.post(url, headers=headers, data=data)
    if response.status_code == 200:
        result = json.loads(response.text)
        print(result.get("result_message"))
        newapptk = result.get("newapptk")

    data = {
        "tk":newapptk
    }
    url = "https://kyfw.12306.cn/otn/uamauthclient"
    response = session.post(url, headers=headers, data=data)
    if response.status_code == 200:
        print(response.text)

    url = "https://kyfw.12306.cn/otn/index/initMy12306"
    response = session.get(url, headers=headers)
    if response.status_code == 200 and response.text.find("用户名") != -1:
        return True
    return False

def captcha():
    data = {
        "login_site": "E",
        "module": "login",
        "rand": "sjrand",
        "0.17231872703389062":""
    }

    # 获取验证码
    param = parse.urlencode(data)
    url = "https://kyfw.12306.cn/passport/captcha/captcha-image?{}".format(param)
    response = session.get(url, headers=headers)
    if response.status_code == 200:
        file = BytesIO(response.content)
        img = Image.open(file)
        img.show()

    positions = input("请输入验证码: ")
    # 发送验证码
    data = {
        "answer": positions,
        "login_site": "E",
        "rand": "sjrand"
    }

    url = "https://kyfw.12306.cn/passport/captcha/captcha-check"
    response = session.post(url, headers=headers, data=data)
    if response.status_code == 200:
        result = json.loads(response.text)
        print(result.get("result_message"))
        return True if result.get("result_code") == "4" else False
    return False

if __name__ == "__main__":
    if login():
        print("Success")
    else:
        print("Failed")

执行结果添一下,好累,我要去吃饭了

 这里写图片描述

我讨厌写文章,真的,这是我的第一篇爬虫文章了,写代码过程用了两个小时,写文档用了整整一个下午,是的,一个下午。哎,我果然还是不擅长写文章,之前想要写python微信公众号二次开发,结果写了好几次都是开头就删掉了,文才真的很烂,而且通篇逻辑混乱,我自己都看不下去。大家凑合看吧,希望多提意见,我一定虚心接受,慢慢改正。

 

 

=======================================长长的分割线 2018-2-1 23:20:13=======================================

针对大家一直在问的登录问题,我上周查看过,发现12306有一些改版,多出了两个请求,主要是增加了两个cookies,RAIL_DEVICEID和RAIL_EXPIRATION,

https://kyfw.12306.cn/otn/HttpZF/GetJS和https://kyfw.12306.cn/otn/HttpZF/logdevice?,通过一个js生成请求参数,然后获取上面两个cookie数据,之前看的时候发现GetJS这个请求返回的js脚本看不懂,分析不出来,就搁置了,这两天想再回过头来看一看,结果发现12306好像又改回去了,之前的又能正常登陆了,而且这个GetJS请求也没有js脚本返回了,logdevice请求也没了,估计是因为之前搞的不稳定,有时候正常登陆都有问题,所以暂时回退版本了吧。现在的这个教程应该是正常可用了。不过估计等12306调试稳定后,就又不行了,楼主还不会前端编程,所以js是薄弱点,如果12306再更新的话也搞不定这块。好在之前的那个GetJS请求脚本我保存了一份,上传上来希望熟悉js的同学能帮忙解密一下https://files.cnblogs.com/files/small-bud/test.js,大概就是后面的这些参数搞不定,可惜请求的参数信息我没有保存下来

===================================================================================================================== 

 


如果你觉得我的文章还可以,可以关注我的微信公众号:Python爬虫实战之路
也可以扫描下面二维码,添加我的微信号

 
公众号

 

 
微信号
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!