Python实例 一 12306抢票(二) 下单

【Python抢票神器】火车票枪票软件到底靠谱吗?实测—终极攻略。 好啦!这款Python实现12306查票以及自动购票的小程序写到这里就正式over了!大家还想看爬什么的可以跟我讲撒!评论区留言即可!👇。 阅读详情

第二篇 刷票与下单

原文地址:http://www.tnblog.net/cz/article/details/241

解决"url":"/leftTicket/init","status":false,"httpstatus":200,"messages":["系统忙,请稍后重试"] 问题

1.记住登陆

    上一篇写了登陆:http://www.tnblog.net/cz/article/details/162

    为了方便调试 不让每次登陆都去扫码一遍,做一个缓存登陆的。

    登陆就是会话保持,一般依赖于cookie,token之类的信息保存到cookie中,请求的时候带着cookie去服务器,服务器就知道是同一个人。而请求使用的request库的session会自动保存每次服务器返回的cookie信息,下次请求带上该cookie ,所以能在运行时一直保持你登陆过的状态,但是重新运行程序就不行了,所以我们可以把上次的cookie信息存到文件中,下次运行的时候把cookie读取放到session中去 就哦了!

登录前session的cookie信息

登陆成功的

 

 

把cookie信息存入文件,与读取出来:

# 保存cookie
def saveCookie():
    _cookies = session.cookies.get_dict()
    # 取到session的cookie信息 取出来是键值对把他转化成字符串类型保存下来
    cookieStr = json.dumps(_cookies)
    with open('./cookies.txt', 'w') as f:
    f.write(cookieStr)
    print('记录cookie成功')

# 取出cookie
def getCookie():
    try:
        with open('./cookies.txt', 'r') as f:
        _cookie = json.load(f)
        # session的cookie是一个RequestsCookieJar类型的,把键值对转换为给他
        session.cookies = requests.utils.cookiejar_from_dict(_cookie)
    except FileNotFoundError:
        print('还未登陆过..')

 

保存成功之后 这样:

 

 

测试一下:

先登录

停止程序之后,直接读取cookie测试成功!!

 

 

 

好了开始进入刷票下单:

流程:

https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2019-02-14&leftTicketDTO.from_station=CQW&leftTicketDTO.to_station=TVW&purpose_codes=ADULT # 查询
https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest # 提交下单请求
https://kyfw.12306.cn/otn/confirmPassenger/initDc # 获取一堆参数 后面的请求需要用到
https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs # 获取购票人信息 (乘车人信息)
https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo # 检查订单信息 (是否能购票,是否有未支付的订单)
https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount # 获取余票,与排队人数
https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue #选座,确定提交
https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1547780170175&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=a8dc2ed1a814b3067cfc32550c17e0ed # 排队
https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue # 确定下单成功回执

 

一、查询

地址:https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2019-02-14&leftTicketDTO.from_station=CQW&leftTicketDTO.to_station=TVW&purpose_codes=ADULT 
参数:{
      leftTicketDTO.train_date:2019-02-14  #购票日期
      leftTicketDTO.from_station:CQW #起点站
      leftTicketDTO.to_station:TVW #终点站
      purpose_codes:ADULT #票类型,ADULT普通票 
    }
返回:{"data":{"flag":"1","map":{"CUW":"重庆北","CXW":"重庆西","TVW":"潼南"},"result":["|列车运行图调整,暂停发售|77000D51030J|D5103|CXW|ICW|CXW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W3|01|03|0|1|||||||||||||||||0|0|null","|列车运行图调整,暂停发售|77000D51170A|D5117|CUW|NIW|CUW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W2|01|03|0|1|||||||||||||||||0|0|null","IQEEzf7ymvboXyZSxb%2Bvno%2FvYNEZbmuqDINQp%2Bmnh3k4f0fECt06bHNQPKk4Hlpp8k%2FEY%2FHY1hMb%0Aua7x0jZi61wrCfaoT%2FPBfZMLd4iBVRZurosYHAz0E191H6A5Imnmwng39zx10jq6Bj5eBTTWbH4U%0Ag5a1RfO85IpYBCUAfwSajgMxulbU63F6m2rH%2Bn3QV2djAIrKzIDYwjfdFAuLAfzMa5Sb0cV3nEG7%0AnW43VHcCPF9qB%2BvHUSuHaQcbaHdYztBgPpKcWEXeSXZ0Hekt8%2BIORs8NXs%2FMiGi%2FLkbi5NdZYb%2BJ%0Ax36wYA%3D%3D|预订|710000K1420H|K143|NNZ|CDW|CXW|TVW|09:18|10:36|01:18|Y|t47JENLelAwrUH8jO7aObclgoEFHNBYIMu9USdXqyieI0PymUpkzmENrhno%3D|20190213|3|Z1|11|12|0|0||||2|||有||有|16|||||10401030|1413|0|0|null","MUyH%2FHTEfnPsZwsT%2B2JzqnO21xQ5sGp0nnfSH52Dxg82ZvrybT7nChBhBxb3TsvMjtJyUJx89uNQ%0AMCrH9ekzbik0zw54JI4FG7uavZBLFJwJFKM6jXIhI6akPir9cafU%2BO07Fq2oHvjihljCWeydEtMq%0AHltj9Cl6HqKGR9xxZS1UsSAWqyjFBxPFLPFkPjn1LwFH%2FnvW1li%2FMEDeRugFkjsdIzBNPnrhDB3I%0A%2Bi022Xdvfv8XW3YA0vrhyYKwZU%2FGAR52EgfaJAMKGomYC%2F6HFjCas%2BCl8big1Vkw%2FuIAObayl7Is%0AtwecSA%3D%3D|预订|710000K8720H|K873|ZJZ|ICW|CXW|TVW|09:24|10:52|01:28|Y|MtttfIDxq6HqNo9XM%2BstI4zCQgaSjr%2Fce9ZED1rem4EoS1%2FuzFDmxLqVGMs%3D|20190213|3|Z1|18|19|0|0||||无|||有||有|有|||||10401030|1413|0|0|null","|列车运行图调整,暂停发售|77000D514570|D5145|CUW|TVW|CUW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W2|01|03|0|1|||||||||||||||||1|0|null","|预订|4e0000D63208|D633|WHN|CDW|CUW|TVW|13:59|14:51|00:52|N|1y9bvC04rZ6kAvLar8uM3MsUNOn3MncbI4gVoC5mFGj1Ltnz|20190214|3|N3|13|15|0|0|||||||无||||无|无|||O0M0O0|OMO|0|0|null","|列车运行图调整,暂停发售|77000D513908|D5139|CUW|NUW|CUW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||W2|01|03|0|1|||||||||||||||||0|0|null","|列车停运|71000D178202|D1783|NFZ|ICW|CXW|TVW|24:00|24:00|99:59|IS_TIME_NOT_BUY||20190214||Z1|17|18|0|1|||||||||||||||||0|0|null","|预订|6c000D180604|D1807|IZQ|ICW|CXW|TVW|16:01|16:48|00:47|N|0IsJRXfUvj4fKLm47STmvIB%2BzwWrdhrzCQqwSJd8lsG62utJ|20190214|3|Q9|13|14|0|0|||||||无||||无|||无|O0F0O0|OFO|0|0|null","43XpgRuIos5DmDfOdyu0HdaV7DDZSadtZ7uNBtK%2FIySnx0giFWZSvBddQ%2FPVAoKo%2FINIK7TD5jyE%0AGZv3ODius1%2BTL3IK6NIBVTdxysFwgmI3cKUME4TrOB2Pf1mw%2BFjSirjYyuvekN315V%2BbNGdVn62L%0Ai9%2FJxuXCZs8g0Akf1jhgE7VN6VNnxl8DGxwwz55RJ%2FGes5qp%2FvD2qa%2F%2FDf81Q4tSkXKCBlF5d2Oh%0ANjG49IAciQHLaX7gXTzzxY7HxJbmS7TBMNfxQCdtFa5%2Fm8NEAZem%2FLZqLU4%2BpDyZWyqoUO4%3D|预订|5l000D237340|D2373|NKH|ICW|CUW|TVW|16:56|17:47|00:51|Y|pp0%2Ft2%2BQaSwUfIh63Y%2FXtf%2FVMBxdG0SLx%2FK1MiAVidpk0ak%2B|20190214|3|H2|15|17|0|0|||||||无||||2|1|||O0M0O0|OMO|0|0|null","NwCfMpWhQYGrv9zKy%2FE2bJHhqOoVHXTgo5hTboBBIe%2FKzro2zxrnAiA4x1lswwh5upe5V31vZstu%0AK0eMiMwDCjgTeU4CwQtsJdOE%2FFN3yY6AN4D%2FS%2Fh1r1pC3QWwUfbXueeXWVrsXQuv7gxyz8wQjXDH%0Ah0%2Bfm2VUKochP2Te910gF9I5oWcUk4Gkih44Ey7u2hQmjW8QN%2BtYdmvCn5LZ7QW3GOZHWSzePrZJ%0ALr3Kyh2JqCZokd9MguNCNb99pGH5AV%2B97%2F3ek7cN1F872WL0HTRZsnS3wkhHwLowo4nWqmE%3D|预订|77000D514708|D5147|CUW|TVW|CUW|TVW|17:13|17:57|00:44|Y|CLy0iYHVYp2Mctmf3%2FRbBmuCCUGyeyU8JF%2Bjolnv%2FfplIrD2|20190214|3|W2|01|03|0|0|||||||有||||有|19|||O0M0O0|OMO|1|0|null","%2FCq%2FwdowS1S9VIqsBpnF2KRBh9cwXXdvrkE7Ni7Q2Yo%2F5hpvDoHAw%2FwYScidhgJuXAOY7%2B6E1Nln%0AfusLNDgv3zWGzFWW73htly%2Fo05MnO1hNMer5dhSDFMx3M2e2m4DYiSJ6wCdleKPb0%2FQc%2B46QGJbo%0A458zqWY%2F1CVFCWhCrd3w3RGHDQ2MqqzcaYkxvxSdcdpd3B1Xoy%2BBZjTNH6gEccvbvW%2BTmHZcP7S2%0ADLZg9NaARQD1cxGCc3VMnEE5kOxTDDuAL66PfFy%2B1hzHMz0kjLURhHrGie7rFzC%2Fzo64HxM%3D|预订|5l0000D35273|D353|AOH|ICW|CUW|TVW|18:11|18:56|00:45|Y|PEDp1LSroEHChj4TmdO54rOKNNaLWQ%2F3PWTrFnOf0Gmhulg9|20190214|3|H2|18|19|0|0|||||||无||||2|2|||O0M0O0|OMO|0|0|null","eZJHEjCjbrawzJcm8DtAxZjjT7uZZe%2Bw23ZW5o3WiK6sWUjGu4bAnn1AqRTGb0xoH4tUKbrjMQAz%0AyXb74lN6G3kDNrYCi66g%2BNKfci2BXK6Hd2fMEFi0PoAcBtOCXEFbXfA7x%2BSYaFxNh4P2nLtkQ46i%0AMY%2BTkporMHoy2I8Dq7MFekl%2FuPbLUlfEJlZ69NxPaYETZ3TPfW8aHKstvQ72CGjriLvdkpeK7sYj%0A9rRG5RB%2BzatWKpJYTJwC1pfiPXY7djOegP7Ifd2%2Frg6ZWfabP5faBnQxiyHsHWLX7Al8jHc%3D|预订......"]},"httpstatus":200,"messages":"","status":true}

 需要注意:

    1.查询地址的queryZ会随机变化,有可能是queryA之类的,先随便传一个,如果不对的话 他会返回正的给你,再带上正确的地址去查询就可以了

 
  1. {'c_name': 'CLeftTicketUrl', 'c_url': 'leftTicket/queryZ', 'status': False}

    2.起点站终点站的数据是个简码,可以去下载他的信息下来自己整理 地址:https://www.12306.cn/index/script/core/common/station_name_v10015.js  

是用@隔开的 这样↓自己解析成一个json文件 方便使用(自己写写)

  =>  

    3.把查询的信息配置到一个方法中,方便读取更改

TicketDTO = {}  # 封装请求参数
def initTicketDTO():
    with open('./city.json', encoding='utf-8') as f:
        CITY_DATA = json.load(f)
    TicketDTO['train_date'] = '2019-02-14' #日期
    TicketDTO['from_station_name'] = '重庆' #起点站
    TicketDTO['to_station_name'] = '潼南' #终点站
    TicketDTO['class'] = ['D5147'] #想购买车次  支持多个
    TicketDTO['holder'] = ['XX'] #谁买票 目前只支持单人
    
    TicketDTO['from_station'] = CITY_DATA[TicketDTO['from_station_name']] #起点站转换的简码
    TicketDTO['to_station'] = CITY_DATA[TicketDTO['to_station_name']] #终点站简码
    
    TicketDTO['passengerInfo'] = {} #购票人信息

 

查询车票代码:

select_ticket_URL = 'leftTicket/queryA'  # 查票的地址是在queryA、queryZ啥的随机变化的
def select_ticket():
    initTicketDTO()
    global select_ticket_URL  # 也可以从查询的初始化界面的leftTicket/init中获取,这就不写了 还是用这种方式
    url = 'https://kyfw.12306.cn/otn/'+select_ticket_URL +\
        '?leftTicketDTO.train_date='+TicketDTO['train_date'] + \
        '&leftTicketDTO.from_station=' + TicketDTO['from_station'] +\
        '&leftTicketDTO.to_station='+TicketDTO['to_station'] +\
        '&purpose_codes=ADULT'  # ADULT:单程普通票
    data = get(url)
    json_result = json.loads(data)
    if(not json_result['status']):
        select_ticket_URL = json_result['c_url']
        select_ticket()
        return
    city_info = json_result['data']['map']  # 城市信息
    for item in json_result['data']['result']:
        classes = item.split('|')  # 被竖线隔开的
        canBuy = classes[11]  # 是否可购买
        secretStr = urllib.request.unquote(classes[0])  # 下单信息
        IsEnable = (classes[0] == "" and False or True)  # 有无预定信息
        train_no = classes[2]  # 班次
        TrainClass = classes[3]  # 班次
        FirstSeat = classes[31]  # 一等座
        SecondSeat = Convert(classes[30])   # 二等座
        print(canBuy+"=> 班次:"+TrainClass+" 历程:" + classes[13]+" "+city_info[classes[6]]+"=>"+city_info[classes[7]] + " "+classes[8]+"-" + classes[9]+"["+classes[10] + "h] 一等座:"+str(FirstSeat)+" 二等座:"+str(SecondSeat))
    if(TrainClass in TicketDTO['class']):
        print(train_no+":二等座余票:"+str(SecondSeat))
        if(IsEnable and canBuy == 'Y'):  # 有提交信息 并且可以购买
            print('可购买,正在提交')
            if(SecondSeat > 0):
                while True:
                json_initDc = submitOrderRequest(secretStr)
                if(type(json_initDc) is dict):
                checkOrderInfo(json_initDc)
                time.sleep(2)
            else:
                print('不可购买')
def Convert(val):
    if(val == '无' or val == ''):
        return 0
    elif(val == '有'): #有一般是大于20
        return 20
    return int(val)

 

返回的车次信息是通过'|'分割的 分割开来就可以看到了 大致有用的信息的元素位置代码中已经标注了

 

 

 

二、提交下单请求

地址:https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest 
参数:{
       secretStr:NwCfMpWhQYGrv9zKy/E2bJHhqOoVHXTgo5hTboBBIe/Kzro2zxrnAiA4x1lswwh5upe5V31vZwXQuv7gxy............  # 这个参数是第一步查询的第一个参数,那趟车可买票的时候才会有这个参数
       train_date:2019-02-14  # 发车日期
       back_train_date:2019-01-20 # 返程日期(不是往返票不用管他)一般默认是当天
       tour_flag:dc # 单程
       purpose_codes:ADULT # 普通票
       query_from_station_name:重庆 #起点站
       query_to_station_name:潼南 # 终点站
       undefined:  # 为空的参数
    }
返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":"N","messages":[],"validateMessages":{}}

代码:

# 检测是否有未完成的订单,没有的话获取购票人
def submitOrderRequest(secretStr):
    reqdata = {
        "secretStr": secretStr,
        "train_date": TicketDTO['train_date'],
        "back_train_date": time.strftime('%Y-%m-%d', time.localtime(time.time())),
        "tour_flag": 'dc',
        "purpose_codes": 'ADULT',
        "query_from_station_name": TicketDTO['from_station_name'],
        "query_to_station_name": TicketDTO['to_station_name'],
        "undefined": ''
    }
    url = 'https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest'
    data = post(url, reqdata)
    json_result = json.loads(data)
    if(json_result['status']):
        if(json_result['data'] == 'N'):  # 无未完成的订单 去获取参数然后获取购票人
            json_initDc = getinitDc()
            if(getPassenge(json_initDc['REPEAT_SUBMIT_TOKEN'])):
                return json_initDc
    else:
        print(json_result['messages'])
        return False

如果你有票未支付的状态下,会返回有未完成的订单什么什么的叫你去处理,其他没啥

 

 

三、获取一些参数后面用到

https://kyfw.12306.cn/otn/confirmPassenger/initDc #获取一堆参数 后面的请求需要用到
参数:{
     _json_att:'' # 空参数
   }
返回的是一个页面,需要用正则去取数据

代码:

def getinitDc():
    html_data = post('https://kyfw.12306.cn/otn/confirmPassenger/initDc',
                {"_json_att: ": ""})
    REPEAT_SUBMIT_TOKEN = re.findall(re.compile(
        "var globalRepeatSubmitToken = '(.*?)';", re.S), html_data)
    key_check_isChange = re.findall(re.compile(
        "'key_check_isChange':'(.*?)',", re.S), html_data)
    leftTicketStr = re.findall(re.compile(
        "'leftTicketStr':'(.*?)',", re.S), html_data)
    tour_flag = re.findall(re.compile(
        ",'tour_flag':'(.*?)',", re.S), html_data)
    purpose_codes = re.findall(re.compile(
        ",'purpose_codes':'(.*?)',", re.S), html_data)
    train_location = re.findall(re.compile(
        ",'train_location':'(.*?)'", re.S), html_data)
    train_no = re.findall(re.compile(",'train_no':'(.*?)',", re.S), html_data) 
    station_train_code = re.findall(re.compile(
        ",'station_train_code':'(.*?)',", re.S), html_data) 
    from_station_telecode = re.findall(re.compile(
        ",'from_station_telecode':'(.*?)',", re.S), html_data)
    to_station = re.findall(re.compile(
        ",'to_station':'(.*?)',", re.S), html_data)
    json_initDc = {
        'REPEAT_SUBMIT_TOKEN': REPEAT_SUBMIT_TOKEN[0],
        'key_check_isChange': key_check_isChange[0],
        'leftTicketStr': leftTicketStr[0],
        'tour_flag': tour_flag[0],
        'purpose_codes': purpose_codes[0],
        'train_location': train_location[0],
        'train_no': train_no[0],
        'station_train_code': station_train_code[0],
        'from_station_telecode': from_station_telecode[0],
        'to_station': to_station[0]
    }
    return json_initDc

 

 

 

四、获取购票人信息

https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs # 获取购票人信息 (乘车人信息)
参数:{
        _json_att:'', #为空
        REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c # 第三步中获取的一个参数
      }
返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"notify_for_gat":"","isExist":true,"exMsg":"","two_isOpenClick":["93","95","97","99"],"other_isOpenClick":["91","93","98","99","95","97"],"normal_passengers":[{"code":"2","passenger_name":"XX","sex_code":"M","sex_name":"男","born_date":"199....","country_code":"CN","passenger_id_type_code":"1","passenger_id_type_name":"中国居民身份证","passenger_id_no":"500223....","passenger_type":"1","passenger_flag":"0","passenger_type_name":"成人","mobile_no":"13...","phone_no":"","email":"...@qq.com","address":"","postalcode":"","first_letter":"CZ","recordCount":"7","total_times":"99","index_id":"0","gat_born_date":"","gat_valid_date_start":"","gat_valid_date_end":"","gat_version":""},{"code":"1","passenger_name":"XX","sex_code":"M","sex_name":"男","born_date":"2016-02-14 00:00:00","country_code":"CN","passenger_id_type_code":"1","passenger_id_type_name":"中国居民身份证","passenger_id_no":"500230....","passenger_type":"1","passenger_flag":"0","passenger_type_name":"成人","mobile_no":"","phone_no":"","email":"","address":"","postalcode":"","first_letter":"AJ","recordCount":"7","total_times":"99","index_id":"1","gat_born_date":"","gat_valid_date_start":"","gat_valid_date_end":"","gat_version":""},{"code":"4","passenger_name":"XXX","sex_code":"F","sex_name":"女","born_date":"1900-01-01 00:00:00","country_code":"CN","passenger_id_type_code":"1","passenger_id_type_name":"中国居民身份证","passenger_id_no":"51160........","passenger_type":"1","passenger_flag":"0","passenger_type_name":"成人","mobile_no":"15.....","phone_no":"","email":"","address":"","postalcode":"","first_letter":"DYM","recordCount":"7","total_times":"99","index_id":"3","gat_born_date":"","gat_valid_date_start":"","gat_valid_date_end":"","gat_version":""}]},"messages":[],"validateMessages":{}}

代码

def getPassenge(REPEAT_SUBMIT_TOKEN):
    if(any(TicketDTO['passengerInfo'])): #如果获取过了就直接返回
        return True
    else:
        url = 'https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs'
        reqdata = {
            "_json_att: ": "",
            "REPEAT_SUBMIT_TOKEN": REPEAT_SUBMIT_TOKEN[0]
        }
        data = post(url, reqdata)
        json_result = json.loads(data)
        for item in json_result['data']['normal_passengers']:
            if item['passenger_name'] in TicketDTO['holder']:
                TicketDTO['passengerInfo'] = {'passengerTicketStr': 'O,'+item['passenger_flag']+','+item['passenger_type']+','+item['passenger_name']+','+item['passenger_id_type_code']+','+item['passenger_id_no']+','+item['mobile_no']+',N',
                    'oldPassengerStr': item['passenger_name']+','+item['passenger_id_type_code']+','+item['passenger_id_no'] + ',1_'} # 拼接下面要用到的参数
        if(any(TicketDTO['passengerInfo'])):
            print("购票人数据获取成功")
            return True

 

 

 

五、检查订单信息

https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo # 检查订单信息 (是否能购票,是否有未支付的订单)
参数:{
    cancel_flag:2  # 固定参数
    bed_level_order_num:000000000000000000000000000000  # 固定参数
    passengerTicketStr:O,0,1,XX,1,500223...,132...,N   #购票人方法中拼接好的 购票人信息
    oldPassengerStr:XX,1,5002.....,1_   #购票人中拼接好的
    tour_flag:dc  #单程。。基本固定的,不过第三步中获取过
    randCode:''  #空参数
    whatsSelect:1  # 固定
    _json_att:''  #空参数
    REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c #第三步中获取的
}
返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"ifShowPassCode":"N","canChooseBeds":"N","canChooseSeats":"Y","choose_Seats":"OM","isCanChooseMid":"N","ifShowPassCodeTime":"1","submitStatus":true,"smokeStr":""},"messages":[],"validateMessages":{}}

代码:

def checkOrderInfo(json_initDc):
    reqdata = {
        'cancel_flag': '2', #固定
        'bed_level_order_num': '000000000000000000000000000000',
        'passengerTicketStr': TicketDTO['passengerInfo']['passengerTicketStr'],
        'oldPassengerStr': TicketDTO['passengerInfo']['oldPassengerStr'],
        'tour_flag': json_initDc['tour_flag'],  # 基本固定
        'randCode': '',
        'whatsSelect': '1',  # 固定
        '_json_att': '',  # 为空
        'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
    }
    data = post(
                'https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo', reqdata)
    json_result = json.loads(data)
    print(json_result)
    if(json_result['status']):
        print('检查订单成功')
    else:
        print('检查订单失败'+json_result['messages'])
        return

返回的可选座位类型之类的东西

 

 

6.获取余票与排队人数

https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount # 获取余票,与排队人数
参数{
        train_date:Thu+Feb+14+2019+00:00:00+GMT+0800+(中国标准时间) #购票时间格式化出来的
        train_no:77000D514708 # 车次 第三步获取过
        stationTrainCode:D5147 #班次 第三步获取过
        seatType:O # O代表二等座,这儿有个固定字典的类型
        fromStationTelecode:CUW #起点站简码 第三步获取过
        toStationTelecode:TVW #终点站简码 第三步获取过
        leftTicket:CLy0iYHVYp2Mctmf3%2FRbBmuCCUGyeyU8JF%2Bjolnv%2FfplIrD2 #第三步获取的
        purpose_codes:00  #第三步获取的
        train_location:W2 #第三步获取的
        _json_att:''   #为空
        REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c #第三步获取的
}
返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"count":"0","ticket":"463,85","op_2":"false","countT":"0","op_1":"false"},"messages":[],"validateMessages":{}}

代码:

# 获取余票
def getQueueCount():
    reqdata = {
        'train_date': time.strftime("%a+%b+%d+%Y+00:00:00+GMT+0800", time.strptime(TicketDTO['train_date'], "%Y-%m-%d"))+'+(中国标准时间)',
        'train_no': json_initDc['train_no'],  # 班次号
        'stationTrainCode': json_initDc['station_train_code'],  # 车次
        'seatType': 'O',# json_result['data']['choose_Seats'],  #  就是你选座的类型 二等座 O
        'fromStationTelecode': json_initDc['from_station_telecode'],  # 起点终点
        'toStationTelecode': json_initDc['to_station'],
        'leftTicket': json_initDc['leftTicketStr'],
        'purpose_codes': json_initDc['purpose_codes'],  # ?? 姑且认为是固定  没有取不到的参数!
        'train_location': json_initDc['train_location'],  # ??
        '_json_att': '',
        'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
    }
    reqdata_Str= bytes.decode(urllib.parse.urlencode(reqdata).encode('utf-8')).replace('%2B','+').replace('GMT+0800','GMT%2B0800').replace('%28','(').replace('%29',')')
    _headers= {
        "Accept":"application/json, text/javascript, */*; q=0.01",
        "Accept-Encoding":"gzip, deflate, br",
        "Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
        "Host":"kyfw.12306.cn",
        "Referer":"https://kyfw.12306.cn/otn/confirmPassenger/initDc",
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0",
        "X-Requested-With":"XMLHttpRequest"
    }
    data = post(
        'https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount',reqdata_Str,headers=_headers)
    json_result = json.loads(data)
    if(json_result['status']):
        print(json_result)
        print('提交下单信息成功,余票信息:'+json_result['data']['ticket']+'  排队人数:'+json_result['data']['count']) #T估计是票总数
        if(json_result['data']['op_2']=='true'):
            print('排队人数超过余票') # 应该需要重新发起请求之类的
    else:
        print('提交下单信息失败,'+json_result['messages'])
        return

需注意:

    1.train_date 参数格式化的问题,在这卡了1天.......,转码不对 提交的请求总会返回"url":"/leftTicket/init","status":false,"httpstatus":200,"messages":["系统忙,请稍后重试"]

返回的信息里ticket代表票的余量 会返回2中票的余量  op_2为true时代表排队人数超过余票

 

 

 

六、选座,提交

https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue 
参数:{
        passengerTicketStr:O,0,1,XX,1,500....,132.....,N   # 拼接的购票人信息
        oldPassengerStr:XX,1,5002....,1_     # 拼接的购票人信息
        randCode:'' #空数据
        purpose_codes:00   # 第三步获取
        key_check_isChange:5EAB44C37C430901F5D2AA18462DD31BDADFE7DE17048A36906509CD   # 第三步获取
        leftTicketStr:Imh2yxCys%2Fo%2FhDX%2B3ZZOYgnkoNLrsZz6OFEYF5oGBrFRIB2p  # 第三步获取
        train_location:W2  # 第三步获取
        choose_seats:1D #选的座位,没有的话他会帮你随机选。。
        seatDetailType:000   # 固定
        whatsSelect:1  # 固定
        roomType:00   # 固定
        dwAll:N   # 固定
        _json_att:''  #空
        REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c    # 第三步获取
}
返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"submitStatus":true},"messages":[],"validateMessages":{}}

代码:

def confirmSingleForQueue():
    reqdata = {
        'passengerTicketStr': TicketDTO['passengerInfo']['passengerTicketStr'],
        'oldPassengerStr': TicketDTO['passengerInfo']['oldPassengerStr'],
        'randCode': '',
        'purpose_codes': json_initDc['purpose_codes'],
        'key_check_isChange': json_initDc['key_check_isChange'],
        'leftTicketStr': json_initDc['leftTicketStr'],
        'train_location': json_initDc['train_location'],
        'choose_seats': '1D',  # ??  选择的座位
        'seatDetailType': '000', # 固定
        'whatsSelect': '1',# 固定
        'roomType': '00',# 固定
        'dwAll': 'N',# 固定
        '_json_att':'' ,# 固定
        'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
    }
    data = post(
        'https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue', reqdata)
    print(data)
    json_result=json.loads(data)
    if(json_result['status']):
        if(json_result['data']['submitStatus']):
            print('选座成功!')
    else:
        print('选座失败。。 '+json_result['messages'])
        return

 

 

七、提交订单 排队,获取订单id

https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1547780170175&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=a8dc2ed1a814b3067cfc32550c17e0ed # 排队
参数:{
        random1547780170175 #秒级时间戳
        tourFlag:dc # 固定的 普通票
        _json_att:'' 
        REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c  #第三步获取
}
返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"queryOrderWaitTimeStatus":true,"count":0,"waitTime":-1,"requestId":6491860556856379439,"waitCount":0,"tourFlag":"dc","orderId":"E702546392"},"messages":[],"validateMessages":{}}

代码:

# 下单等待确定
def queryOrderWaitTime():
    orderId=''
    OrderWait_url = 'https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random='+str(int(time.time()*1000))+'&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN='+json_initDc['REPEAT_SUBMIT_TOKEN']
    html_data = get(OrderWait_url)
    json_result = json.loads(html_data) #这边返回的订单ID 需要作为参数
    print(json_result)
    if(json_result['status'] and json_result['data']['queryOrderWaitTimeStatus']):
        print('等待提交订单信息成功')
        orderId=json_result['data']['orderId']
        while(orderId==None):
            time.sleep(json_result['data']['waitTime'])
            html_data = get(OrderWait_url) #需要等一会才能返回订单的id
            json_result = json.loads(html_data)
            print(json_result)
            if(json_result['status'] and json_result['data']['queryOrderWaitTimeStatus']):orderId=json_result['data']['orderId']
    else:
        print('等待提交订单信息失败,'+json_result['messages'])
        return

需注意:返回的信息中有个等待时间waitTime代表需要等待这么久 才能获取到orderid 否则是none,orderid下面需要用到

 

 

八、确定回执信息

https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue # 确定下单成功
参数{
        orderSequence_no:E702546390  #上一步返回的订单id
        _json_att:'' #空
        REPEAT_SUBMIT_TOKEN:1e4811cbf86e722b50c7cb4293d1969c  #第三步的
}
返回:{"validateMessagesShowId":"_validatorMessage","status":true,"httpstatus":200,"data":{"submitStatus":true},"messages":[],"validateMessages":{}}

代码:

# 回执信息
def resultOrderForDcQueue():
    reqdata = {
        'orderSequence_no':orderId,
        '_json_att':'',
        'REPEAT_SUBMIT_TOKEN': json_initDc['REPEAT_SUBMIT_TOKEN']
    }
    data = post(
    'https://kyfw.12306.cn/otn/confirmPassenger/resultOrderForDcQueue', reqdata)
    print(data)
    json_result = json.loads(data)
    if(json_result['status']):
        if(json_result['data']['submitStatus']):
            print('下单成功!!!!!!!!!去付款吧')
            # Email提醒
    else:
        print('下单失败。。。。。。。 '+json_result['messages'])

五-八我是写在一个方法里的,这里为了展示,分出来

 

注意:

"submitStatus":true  !!!!说明下单成功了,去12306里支付就ok!!

 

 

源码地址:https://github.com/YuChenDayCode/Ticket

python3+selenium实现12306抢票脚本 文章目录一、 环境配置windows环境mac、linux环境二、 实现思路及代码1. 查询票数2. 登录关于自动登录3. 提交订单4. 邮件提醒5. 短信提醒三、总结与分析一些问题一些想法完整源码下载 一、 环境配置 python3 selenium模块,并配置使用环境 google浏览器或者其他浏览器 安装selenium模块 pip install selenium 到淘宝镜像下chr... 阅读详情

相关推荐

Python脚本实现12306自动抢票:从零到一的完整开发指南

本教程从零开始讲解Python实现12306自动抢票的完整开发流程,包括系统架构分析、API接口逆向、验证码识别技术、余票监控系统设计以及订单提交功能实现。教程提供了企业级开发技术的实战案例,包含详细代码及解释,并强调了自动抢票的法律风险与伦理问题。

全栈 2727

Python爬虫时遇到连接超时解决方案

在进行Python爬虫任务时,经常会遇到连接超时(TimeoutError)错误。连接超时意味着爬虫无法在规定的时间内建立与目标服务器的连接,导致请求失败。为了帮助您解决这个常见的问题,本文将提供一些解决办法,并提供相关的代码示例,希望能为您的爬虫任务提供实战价值。

weixin_44617651的博客 3446

python 实现12306自动抢票

      春节临近越来越多的人需要买火车票了,然而网上那些或分享抢票链接或加钱抢票的软件并不能确定帮自己买到票,既然如此为什么不能自己实现抢票呢?所以在借鉴网上大神的成果基础上,自己也实现了python实时抢票的功能,并且打包成exe可执行文件,小白也能抢票啦!          首先是下载12306-master GitHub地址:         在windows直接用可能会遇到 [Er...

weixin_38507462的博客 2540

python爬取12306

config.json文件内容如下:    { "iphone":"1582222222", "username":"xxx", "password":"xxxx", "bookinfos":{ "fromstation":"NJH","tostation":"S

淡入淡出 1821

Python抢票工具

春节抢票正在如火如荼的进行,过年回家那肯定需要抢票,每年的抢票大战,都是一场硬战,没有一个好工具,怎么能上战场死锁呢。今天小编推荐一个,送到了GitHub趋势榜第一:项目名很干脆,就是“”,标星超过1.2W,来自名叫的程序员。这很可能是全GitHub最德高望重的购票小助手了,功能一直在更新,且现已支持以上版本。有些后起之秀,也是在它的基础上开发出来,然后广受欢迎:标星5k的“”便是其中之一,它支持分布式抢票。如果你还没抢到回程的票,试一下这些Python工具吧。12306项目的用户评价。

z_ssyy的博客 4541

爬虫实战篇——12306抢票爬虫

点击上方“编程精选”,选择“置顶公众号”技术文章第一时间送达!(1)、前言(此代码经过我的实测具有较强的实用型)每逢佳节,大家对于回家抢票这件事是不是特别头疼呢?今天我在网上发现了这个代码,通过一天的学习,与大家分析下,大家可以直接拿来进行12306抢票,也可以进行优化,进行诸如演唱会的抢票,等等。该代码主要使用了模拟浏览器方式而并没有采用分析请求。(2)、技术路线selenium + chrom...

4559

python抢购爬虫_一个关于爬虫的抢购软件优化问题

整体的抢购逻辑流程如下:1.使用 selenium 模拟登陆拿到账号的 cookie 信息并保存2.获取目标抢购角色的网页链接以及价格(发送生单请求时需要这两个参数)3.完成生单之后进行支付,即发送支付请求4.在进行支付请求时,需要拿到订单号,然而这里涉及到两个重定向,于是使用 allow_redirects=false,获取到正常的 cookie 进行下一步操作5.在支付页面,需要发送支付请求,...

weixin_39600400的博客 1139

python实现12306抢票(内含讲义、资料、代码等)

python实现的12306抢票程序,内含讲义、资料、代码等,代码注释比较全,有需要可自行阅读。

第五章 python基础之网络和并发编程

C/S 架构(Client/Server Architecture)和 B/S 架构(Browser/Server Architecture)是两种不同的软件架构模型,用于描述客户端和服务器之间的交互方式。1. **C/S 架构(Client/Server Architecture):**- 在 C/S 架构中,应用程序被划分为两个主要组件:客户端(Client)和服务器(Server)。- 客户端负责用户界面和用户输入处理,通常运行在用户的计算机上。

linjun的博客 1620

西安交通大学SQ轴承数据集(文末附数据)

西安交通大学SQ轴承数据集

qq_43271683的博客 1万+

rsyslog交叉编译(armv7、armv8、aarch64、arm32平台通用)

该文档描述了如何交叉编译rsyslog到arm64嵌入式平台。

sinat_33909696的博客 1560

python实现自动登录12306抢票 -- selenium

提示:以下是本篇文章正文内容,下面案例可供参考num = int(input('请输入您想要的车次:'))# 点击预订按钮 -- 选择要预定的是第几躺车 -- 这个代码没有问题# //div//tbody[@id="queryLeftTable"]/tr[contains(@id,"ticket")]/@id -- 但是selenium的find_elements不可以用@id找到元素。login.json的内容展示。

xaing1314的博客 2万+

手把手教你用STM32CubeMX配置WS2812的PWM+DMA驱动(附完整代码)

本文详细介绍了如何使用STM32CubeMX配置PWM+DMA驱动WS2812全彩灯带,包括硬件选型、STM32F103C8T6的时钟配置、定时器与PWM设置、DMA传输优化等关键步骤,并提供了完整的代码实现和常见问题解决方案,帮助开发者高效控制WS2812灯带。

weixin_30859423的博客 440

python实现12306全自动抢票

这个脚本目前只能刷一趟车的,人数可以是多个,支持选取作为类型等。 实现思路是splinter.browser模拟浏览器登陆和操作,由于12306的验证码不好自动识别,所以,验证码需要用户进行手动识别,并进行登陆操作,之后的事情,就交由脚本来操作就可以了,下面是我测试时候的一些截图: 第一步:如下图,首先输入抢票基本信息   第二步:然后进入登录页,需要手动输入验证码,并点击登陆操作 ...

chen__an的博客 2万+

力软敏捷开发框架源码7.0.6旗舰版 JNPF开发平台3.4.5 企业版 旗舰版

JNPF作为作为一款深受用户喜爱的低代码产品,凭借雄厚的科研技术力量、丰富的软件开发项目经验、严谨的项目开发流程和高质量的售后服务,已成功帮助千家企业完成数字化项目,建立了覆盖不同行业客户和不同发展阶段的软件产品服务体系。

huoooos0的博客 1839

GraphRAG + Ollama 本地化部署实战:从零搭建知识图谱问答系统

本文详细介绍了如何在本地部署GraphRAG与Ollama,搭建一个完全自主可控的知识图谱问答系统。通过逐步指导环境配置、项目初始化、核心设置修改以及源码适配,成功将GraphRAG从依赖云端OpenAI API转向本地Ollama服务,并选用qwen2.5:7b和nomic-embed-text模型。该方案有效解决了数据隐私与持续API成本问题,特别适合中小企业构建内部知识库或智能客服系统。

x2y3z4a5b的博客 918

12306抢票脚本 python_使用Python进行12306抢票

阅读本文大概需要 “3.6min最近朋友圈各种加速包抢票,偶然看到可以使用python也可以进行抢票,索性测试下(其实笔者离家近,是不需要抢票的,骑自行车都能回)。不说废话,不搞那么多前戏了,直接贴上图看一下效果:这是一个开源项目,具体使用方法在github上有着较详细的介绍。下面只对这个开源项目按照自己的理解做个简单介绍,自身理解和实际有所出入的地方,可以提出来一起去修正 学习。贴心的...

weixin_39875760的博客 1万+

解析 RABAX during SAPGUI Logon 错误及其解决方案

登录用户关联了初始启动程序,该程序由于未处理的异常导致运行时错误。在 ST22 查看错误的程序名和错误行。修复初始程序代码中的逻辑问题。重新部署修复后的代码。示例代码:防止未处理的异常TRY." 模拟逻辑处理ENDTRY.此代码通过 TRY-CATCH 块捕获潜在错误,避免程序崩溃。是 SAP 系统中常见的运行时错误,其根源可能涉及多个方面,包括用户配置、系统状态、数据库连接和自定义开发等。通过结合 ST22、SM21 等诊断工具,以及完善的代码实践和系统监控,可以有效预防和解决此类问题。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1781

利用python实现12306抢票程序

1)、前言 (此代码经过我的实测具有较强的实用型)每逢佳节,大家对于回家抢票这件事是不是特别头疼呢?今天我在网上发现了这个代码,通过一天的学习,与大家分析下,大家可以直接拿来进行12306抢票,也可以进行优化,进行诸如演唱会的抢票,等等。该代码主要使用了模拟浏览器方式而并没有采用分析请求。 (2)、技术路线 selenium + chromedriver (3)、思路分析 1、模拟浏览器...

人生苦短, 还不用Python? 1万+

SwinTransformer 改进:SSPCAB模块(2024最新改进方法)

SSPCAB(Spatial Pyramid Pooling with Channel Attention Block)是2024年提出的一种新型神经网络架构改进方法,专注于在保持高精度的同时显著降低计算复杂度和参数量。该方法通过创新的空间金字塔池化与通道注意力机制的结合,实现了网络结构的紧凑化和运算效率的大幅提升。核心创新点1. 混合空间金字塔池化(Hybrid Spatial Pyramid Pooling, HSPP)多尺度动态核池化:根据输入特征图尺寸动态调整池化核大小和步长交叉级联结构。

Henry的博客 1308

RK3568平台开发系列:Android环境下CAN的使用

在RK3568平台上,我们可以利用Android环境中的API来实现CAN的使用。通过以上步骤,我们可以在Android应用中发送和接收CAN数据,并进行相应的处理。当接收到CAN数据时,将触发onMessageReceived回调方法,我们可以在这个方法中对接收到的数据进行处理。在上述代码中,我们指定了CAN的ID为0x123,数据为{0x01, 0x02, 0x03},并设置了标志位和周期。接收CAN数据需要注册一个监听器,并在监听器中处理接收到的数据。发送CAN数据需要指定CAN的ID和数据。

2301_79330513的博客 1912

GitHub 上的 12306 抢票神器,助力回家过年

又到周末了,不过本周末有些略微的特殊。 距离每年一次的全球最大规模的人类大迁徙活动已经只剩下一个多月了,各位在外工作一年的小伙伴大多数人又要和小编一样摩拳擦掌的对待史上最难抢的抢票活动。 然鹅,身为一名技术人员,怎么可能每次都靠自己的双手来抢车票,这是对小编职业的侮辱。 小编实在不想回忆去年过年没有抢到回家的直达车票,把高铁当地铁坐,还得算着每趟车的间隔时间最好在半个小时左右,共计换乘 3 躺高...

极客挖掘机 5092

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

12306抢票软件 python抢票脚本

自动查询车票,并筛选指定的车次进行购票。

qq_43616689的博客 7196

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Python 实现的 12306抢票脚本

12306抢票脚本 本脚本使用一个类来实现所有代码,大体上分为以下几个模块及其步骤: 初始化对象属性(在抢票前进行的属性初始化,包括初始化浏览器模拟对象,个人信息等)。 建立模拟浏览器,模拟浏览器进行cookie等存储。 验证模块: 获取验证图片到本地 将8个图片坐标位置改装成易于输入的1—8的位置编号,输入对应的位置号 发送请求进行后台校验 登录模块: 输入账号密码,请求服务器 获取...

简单点 2166

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Python12306抢票脚本

大家有兴趣可以了解一下bypass(只是兴趣哈)。 本脚本使用一个类来实现所有代码,大体上分为以下几个模块及其步骤: - 初始化对象属性(在抢票前进行的属性初始化,包括初始化浏览器模拟对象,个人信息等)。 - 建立模拟浏览器,模拟浏览器进行cookie等存储。 - 验证模块: - 获取验证图片到本地 - 将8个图片坐标位置改装成易于输入的1—8的位置编号,输入对应的位置号 ...

君_陌的博客 4787

python实现12306自动抢票,并发短信邮件通知

脚本实现及实践 最近在学Python,所以用Python写了这个12306抢票脚本,分享出来,与大家共同交流和学习,有不对的地方,请大家多多指正。话不多说,进入正题: 入门Python其实很容易,但是我们要去坚持学习,每一天坚持很困难,我相信很多人学了一个星期就放弃了,为什么呢?其实没有好的学习资料给你去学习,你们是很难坚持的,这是小编收集的Python入门学习资料关注,转发,私信小编“01”,即可免费领取!希望对你们有帮助,源码也可以领取! 这个脚本目前只能刷一趟车的,人数可以是多个,支持选取作为类型.

MC_XY的博客 5791
上一篇: Python实例 一 12306抢票(一)扫码登陆
DayDay_Code
博客等级 码龄14年 31粉丝 47原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值