前言

票真的难抢,决定自己写一个登陆抢票的

经典老三样

打开charles
打开隐私模式的firefox登陆抢票网站
图片
我决定走扫码登陆,所以这里就直接点扫码,然后找到对应code的图片网址
图片-1670491909111
图片-1670491918509
随便写个测试一下

import requests
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0",
}

host = "kyfw.12306.cn"
data = {
    "appid":"otn"
}
position = "/passport/web/create-qr64"
res = requests.post("https://{}{}".format(host,position),headers=header,data=data,verify=False)

看charles抓包结果
图片-1670492089252

好像不需要什么其他请求和参数,然后试一下保存扫码+查询结果,查询qrcode是否被扫描qrcode接口是这个
图片-1670492342968

照着写吧

resultDict = json.loads(res.text)
uuid = resultDict["uuid"]
with open("./qrcode.png","wb") as f:
    f.write(base64.b64decode(resultDict["image"].encode()))
    f.close()
position = "/passport/web/checkqr"
data["uuid"] = uuid
ifBeenScan = False
while True:
    res = session.post("https://{}{}".format(host,position),headers=header,data=data,verify=False)
    result = json.loads(res.text)
    result_code = result["result_code"]
    if result_code == "2":
        print(result_message)
        session.cookies["uamtk"] = result["uamtk"]
        break
    else:
        time.sleep(10)
        print("请扫码")
postion = "/otn/login/userLogin"
session.post("https://{}{}".format(host,postion),headers=header,data=data,verify=False)

写完以后运行,成功扫码了,但是没有返回扫码成功
图片-1670493770932

尝试调低了timesleep时间也没有返回成功扫码

是不是cookie不全呢

没办法只能一个个找cookie了

对照找找,我缺的cookie很多,原请求如下

图片-1670494235922

我的请求如下
图片-1670494263131
翻了一下能找到的是这些
图片-1670494397707

直接请求试试

图片-1670494487858

图片-1670494498040
可以得到,解决了一个
这个地方明显是返回的rail_expiration和rail_deviceid,但是这里是个callback,必须得看看请求参数
图片-1670494769486

龟龟,你杀了我算了

图片-1670494606129
还有fo

转换一下思路
首先尝试一下能正确获取二维码状态的cookie
直接重发
图片-1670494968913
可以获取结果,然后试试删掉几个
删掉fo
图片-1670495018667
可以返回
删掉两个rail cookie
图片-1670495061924
还是可以,怪事,试试直接删完
图片-1670495095852
好吧看来行不通,先重新申请一个二维码试试cookie
图片-1670495250365
现在可以了,开始删cookie
删掉rail
图片-1670495291729
全删!
图片-1670495321664
我*,问题出在请求头上了
最后定位了一下,问题还是出在了rail没有在表单里提交
以下证明
图片-1670495533840
图片-1670495542113
那问题就变为了如何获取rail的值
刚才定位到

图片-1670495805762

这个请求
把url删到只剩这个
图片-1670495974524
成功返回了
现在来试试python代码实现

params = {
    "0aew":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0"
}
position = "/otn/HttpZF/logdevice"
res = session.get("https://{}{}".format(host,position),params=params,headers=header,verify=False)

我多说拿下了你是不是尔龙辣?
图片-1670496230160
返回的值是json string
图片-1670496268205
处理一下

RAIL_EXPIRATION = json.loads(res.text)["exp"]
RAIL_DEVICEID = json.loads(res.text)["dfp"]
session.cookies.set("RAIL_EXPIRATION",RAIL_EXPIRATION)
session.cookies.set("RAIL_DEVICEID",RAIL_DEVICEID)

报错了,不过小问题啦
图片-1670496488813
把这个callback处理一下就行
图片-1670496514044

result = json.loads(res.text).strip("callbackFunction(").strip(")")
RAIL_EXPIRATION = result["exp"]
RAIL_DEVICEID = result["dfp"]
session.cookies.set("RAIL_EXPIRATION",RAIL_EXPIRATION)
session.cookies.set("RAIL_DEVICEID",RAIL_DEVICEID)

自信运行
我带你们打
图片-1670496618639
乐,写错了

result = json.loads(res.text.strip("callbackFunction(").strip(")"))
RAIL_EXPIRATION = result["exp"]
RAIL_DEVICEID = result["dfp"]
session.cookies.set("RAIL_EXPIRATION",RAIL_EXPIRATION)
session.cookies.set("RAIL_DEVICEID",RAIL_DEVICEID)

自信运行

还是报错,那应该是这个反斜杠的锅,再去掉反斜杠

res.text.strip(r"callbackFunction('\\").strip(")").strip(res.text.strip(r"callbackFunction('\\").strip(r")")[-1])

操作有点傻了,但是能用就行

res.text.strip(r"callbackFunction('\\").strip(")")[:-1]

改了一下 ,应该可以了
看一下cookie
图片-1670497084219
有了
现在扫码
还是不行,忘记加表单请求了好像

加上去
图片-1670497182393
准备拿下
图片-1670497246521
好的翻车了
我尝试了交换以前的device id,也是可以的,那应该是请求头被识别出来了
判断了一下应该是同源请求
最后复制上请求头还不行,草
我觉得是表单顺序的问题,调了一下
还是不行

只能是deviceid生成不正确了
目前是直接把整个复制过来加进去,我目前把timestamp改成1000,扫码以后deviceID明显过期了
不清楚以后这些参数会不会过期
图片-1670498890932
图片-1670499023403
现在用cookie登陆
登陆只需要一个cookie
图片-1670499783044

图片-1670499710188

就是刚才得到的uamtk,post到接口就行

postion = "/passport/web/auth/uamtk-static"
header = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0"
}
session.post("https://{}{}".format(host,postion),headers=header,data=data,verify=False)

很难不成功

然后就是我最喜欢的买票接口啦,随便点一个下单看看接口
先查询

查询

图片-1670500052265

图片-1670500031381
这里具体思路就是html了,直接找xpath对应元素返回就行
我定位到了显示车票的ajax请求

图片-1670505619078
下断点到这里l的定义,看看他如何解析的,去反向推
但是这太耗费精力了,不如直接在返回的数据上下功夫
我分析了一下数据是这样的
如果是G开头的高铁,那么他会在第16条横线倒着来
比如
图片-1670509148919
第2条
Z285
他的数据在这里
图片-1670509195808
打开
图片-1670509212202
很明显从红色箭头所指开始数
第一个是软卧一等座,对应空
第二个是动卧,对应空
第三个是硬卧二等座,对应空
第四个是软座,对应空
第五个是硬座,对应9
第六个是无座,对应空
同理高铁也是倒着来
图片-1670509455840
这个20,有,有就很明显了
我们这里很穷,只监控硬座和二等座就可以了
我们要获取的数据是,列车的开头的起始时间,到站时间,持续时间,是否有座
那么思路就是,对返回的数据进行迭代,然后分割字符串获取数据
图片-1670509693451
记一下数据,索引分别为
3对应列车号,
8对应起始时间
9为结束时间
10为持续时间
-17为第一个框
-18为第二个框
类推


验证

图片-1670500590770

下单

图片-1670500299203
图片-1670500682973
到这里就不会了,乌乌
先休息一下

接着写