嵊泗客运抢票

Posted by YiKe on April 26, 2023

背景

这个五一是疫情后得第一个五一,出行人数真的恐怖,早早定了嵊泗得酒店(不退款),提前7天开始抢船票,但是早起来抢都铩羽而归,只能写个脚本抢了。最后,结果还是好的 ,抢到了合适得班次。

代码

代码里有些隐私信息替换成了”**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import traceback

import requests
import time
import hmac
import hashlib
import base64
import urllib.parse


def update_dict(origin, target):
    for k in origin:
        if k in target:
            origin[k] = target[k]


order_data_tmp = {"accountTypeId": "0", "userId": "****", "buyTicketType": 1, "contactNum": "17521080261",
                  "lineNum": 4973, "lineName": "洋山加枸嵊", "lineNo": 23005, "shipName": "碧海9轮", "startPortNo": 28,
                  "startPortName": "嵊泗(沈家湾)", "endPortNo": 17, "endPortName": "嵊泗(枸杞)", "sailDate": "2023-04-28",
                  "sailTime": "13:00", "lineDirect": 1, "totalFee": 220, "totalPayFee": 220, "sx": 0,
                  "orderItemRequests": [
                      {"passName": "**", "credentialType": 1, "passId": 3593485, "seatClassName": "上舱", "seatClass": 31,
                       "ticketFee": 110, "realFee": 110, "freeChildCount": 0, "passType": 1},
                      {"passName": "**", "credentialType": 1, "passId": 3593483, "seatClassName": "上舱", "seatClass": 31,
                       "ticketFee": 110, "realFee": 110, "freeChildCount": 0, "passType": 1}], "busStartTime": "",
                  "clxm": "高速客船",
                  "clxh": 3, "hxlxh": 1, "hxlxm": "旅游增开班", "bus": 0, "bus2": 0, "dwh": 23}

timestamp = str(round(time.time() * 1000))
secret = '*****'
secret_enc = secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
headers = {'Content-Type': 'application/json'}

dingtalk_hook = "https://oapi.dingtalk.com/robot/send?access_token=*******&timestamp={}&sign={}".format(
    timestamp, sign)

# 南浦(1046)到枸杞(1017)  白名单 4994,1775
query1 = {"startPortNo": 1046, "endPortNo": 1017, "startDate": "2023-04-29"}
# 南浦到嵊山 4973
query2 = {"startPortNo": 1046, "endPortNo": 1016, "startDate": "2023-04-29"}
# 沈家湾(1028)到枸杞  白名单 1775
query3 = {"startPortNo": 1028, "endPortNo": 1017, "startDate": "2023-04-29"}
# 沈家湾到嵊山
query4 = {"startPortNo": 1028, "endPortNo": 1016, "startDate": "2023-04-29"}

# 枸杞到沈家湾
query5 = {"startPortNo": 1017, "endPortNo": 1028, "startDate": "2023-04-30", "accountTypeId": "0"}
# 枸杞到南浦  白名单 1777
query6 = {"startPortNo":1017,"endPortNo":1046,"startDate":"2023-04-30","accountTypeId":"0"}
# 嵊山到南浦
query7={"startPortNo":1016,"endPortNo":1046,"startDate":"2023-04-30","accountTypeId":"0"}


white_list =[4994, 1775, 4973, 1775, 1777]

forward_querys = [query1, query2, query3, query6]

backward_querys = [query6]

import time
import datetime

switch = True
debug = False

while True:

    try:
        if switch:
            querys = forward_querys
        else:
            querys = backward_querys
        switch = not switch
        # 请求所有query
        data = []
        for query in querys:
            url = 'https://www.ssky123.com/api/v2/line/ship/enq'
            response = requests.post(url, headers=headers, json=query)
            if response.status_code == 200:

                res = response.json()
                if len(res['data']) > 0:
                    data.extend(res['data'])

        # filter 有余票得
        lefts = []
        try:
            for d in data:
                # 白名单过滤
                if d['lineNum'] not in white_list:
                    continue
                seatClasses = d['seatClasses']
                for cls in seatClasses:
                    if cls["pubCurrentCount"] >= 2:
                        tickets = {}
                        tickets['startPortName'] = d['startPortName']
                        tickets['endPortName'] = d['endPortName']
                        tickets['className'] = cls['className']
                        tickets['pubCurrentCount'] = cls['pubCurrentCount']
                        tickets['sailDate'] = d['sailDate']
                        tickets['origin_data'] = d
                        tickets['seatClassesInfo'] = cls
                        lefts.append(tickets)
        except Exception as e:
            traceback.print_exc()


        if len(lefts) > 0:
            # 发消息
            content = ''
            for l in lefts:
                content = content + '\n' + "{}: {}到{}, {}, 余票:{}, 开船时间:{}".format(l['sailDate'], l['startPortName'],
                                                                         l["endPortName"], l['className'],
                                                                         str(l['pubCurrentCount']), l['origin_data']['sailTime'])
            ding_msg = {"msgtype": "text", "text": {"content": content}}
            if debug:
                print(content)
            else:
                response = requests.post(dingtalk_hook, headers=headers, json=ding_msg)

            # 下订单
            target = lefts[0]['origin_data']
            order_data = order_data_tmp.copy()
            update_dict(order_data, target)
            for orderItem in order_data['orderItemRequests']:
                update_dict(orderItem, lefts[0]['seatClassesInfo'])
                orderItem['ticketFee'] = int(lefts[0]['seatClassesInfo']['localPrice'])
                orderItem['realFee'] = int(lefts[0]['seatClassesInfo']['localPrice'])
                orderItem['seatClassName'] = lefts[0]['seatClassesInfo']['className']
                orderItem['seatClass'] = lefts[0]['seatClassesInfo']['classNum']

            order_data['totalFee'] = int(lefts[0]['seatClassesInfo']['localPrice']) * 2
            order_data['totalPayFee'] = int(lefts[0]['seatClassesInfo']['localPrice']) * 2
            order_data['sailDate'] = order_data['sailDate'].replace('/', '-')
            order_url = "https://www.ssky123.com/api/v2/holding/save"
            header = headers.copy()
            header['Cookie'] = '*****'
            header['authentication'] = '***'
            header['token'] = '****'
            if not debug:
                response = requests.post(order_url, headers=header, json=order_data)
                if response.status_code == 200:
                    ding_msg = {"msgtype": "text", "text": {"content": "已经完成订票,请尽快付款"}}
                else:
                    content = "自动购票失败。 错误信息:" + response.text
                    ding_msg = {"msgtype": "text", "text": {"content": content}}
                response = requests.post(dingtalk_hook, headers=headers, json=ding_msg)

        if not debug:
            time.sleep(15)
    except Exception as e:

        print("异常退出,等待")
        traceback.print_exc()
        time.sleep(60)