用Python订羽毛球场地

羽毛球算是现在比较热门的一项运动,周末经常和同学朋友一起玩,奈何场地难订,而且就算运气好订到了,时间、位置都不是很理想。所以,我今天要撸一个自动订羽毛球场地的代码,方便之后订场地!!!喜欢打羽毛球的来啊,约起来,一起打,虽然我很菜2333….

1. 前言

首先来看看我们平时如何订场地。因为住在北京-丰台,常去的体育馆是丽泽体育馆,他们订场地是在公众号订,如图:

(1)首先关注公众号;

(2)点击羽毛球,选择想订场地的日期

(3)现在时间段,然后选择时间段提交订单

(4)场地是提前7天预订,然后每天晚上00点开始接受预订。

既然要用Python来自动下单订场地,那就得模拟发起提交订单的过程。那就需要先抓包找到这个订场的服务器地址,以及发送的请求数据。

2. 抓包分析

我们将提交订单那个页面的地址复制出来,(wx.papa.com.cn/?),在chrome浏览器打开,然后请在微信客户端打开链接…..

是的,你没看错,在浏览器是访问不了这个微信浏览器才能访问的网址,貌似需要授权。网上找了一堆资料说改浏览器User-Agents,添加cookies的,试了都不行。那如何解决请在微信客户端打开链接呢,最后用了这个方法:

(1)打开Charles或者Fiddler抓包软件;

(2)在微信PC版中打开刚才的网址(如果是直接转发的可以直接单击打开),这里就是为了使用微信浏览器来打开这个网址来抓包:

打开之后:

然后随便选择几个时间段,提交订单。再到Charles中去找一下抓包结果:

从图中我们就可以看到提交订单时使用的是skus(场地编号+时间段),date_str(时间),以及access_token_wx(核心,用来识别微信用户的)等。那接下来我们只需要用Python发送skus、时间、以及access_token_wx给服务器就可以完成订场地的动作了。

3. Python实现自动订场地

3.1 获取场地列表skus

在订场地之前,我们需要知道场地的skus,然后才能发送给服务器,可以使用下面的代码获取:

# 获取场地列表

import requests

url = 'https://wx-api.papa.com.cn/v2'
headers = {
    'Host': 'wx-api.papa.com.cn',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'Origin': 'https://wx.papa.com.cn',
    'Accept': 'application/json, text/plain, */*',
    'User-Agent': 'MicroMessenger/6.8.0', # 不要也可以
    'Referer': 'https://wx.papa.com.cn/?version=1634094485&v=pro2&flag=413872.100540&token=&flag=413872.100540&v=pro2',
    'Accept-Language': 'zh-cn' 
}

# with open('羽毛球.txt', 'r') as f:
#     data = f.read()

data = {'client_type': 'browser',
'sport_tag_id': '1', # 类型 {'1': '羽毛球', '2': '篮球', '3': '乒乓球', '8': '网球', '14': '舞蹈'}
'date_str': '2021-10-19', # 时间
'r': 'stadia.skuList', 
'access_token_wx': '87*************6d2' # 识别id,改为你自己的
}


# 需要verify=False,不然会报401错误
r = requests.post(url=url, headers=headers, data=data, verify=False)

r


# json处理
r.json()

import pandas as pd
pd.set_option('display.max_rows', 800)
d = r.json()

d['sport_tag_list']
# {'1': '羽毛球', '2': '篮球', '3': '乒乓球', '8': '网球', '14': '舞蹈'}


df = pd.DataFrame()

df_skuList = pd.DataFrame(d['skuList'])

for i in df_skuList.columns:
    for j in range(0, len(df_skuList[i])):
        tmp = pd.DataFrame([df_skuList[i][j]])
        
        df = pd.concat([df, tmp])
df.reset_index(drop=True).head(10).append(df.reset_index(drop=True).tail(10))

这样我们就获取到了所有场地在所有时间段的sku号码。从这个sku号可以看出,如1005400000100001140是由10054000001000+场地号01+时间id 140组成。

那接下来就需要考虑订哪些场地以及几点的问题,那就是场地优先时间优先的问题。

3.2 场地分析和时间段选择

凭借我那惊人的差记忆和同学的回忆以及搜搜的资料,大致画了下体育馆羽毛球场地的分布图如下:

从场地分布图来看,最好的是9号场地,其次是8、10,然后7、11,以及4号,最后是6、5号。因为6、5号那里有个墙,打球影响视线,7和11也贴墙,1、2、3貌似经常是教学场地;

  • 场地优先次序:
    • 9
    • 8、10
    • 7、11
    • 4
    • 6
    • 5

场地排序好之后就是日期时间段:日期个人觉得周六最好,其次周末,然后时间段下午2点到4点打完刚好可以去吃饭,所以时间段排序如下:

  • 时间优先次序
    • 周六、周日:
      • 14:00~16:00
      • 15:00~17:00
      • 16:00~18:00
      • 13:00~15:00
      • 12:00~14:00

场地和时间段选好之后,就可以去刚才做好的sku表里面去把对应的时间段的skus取出来存到字典里,后面直接循环调用就行,N_09_14_16表示9号场地,14点到16点对应的sku。

# 场地编号与时间对应sku
sku_dict = {
    'N_09_14_16': '1005400000100009280,1005400000100009290,1005400000100009300,1005400000100009310',
    'N_08_14_16': '1005400000100008280,1005400000100008290,1005400000100008300,1005400000100008310',
    'N_10_14_16': '1005400000100010280,1005400000100010290,1005400000100010300,1005400000100010310',
    'N_07_14_16': '1005400000100007280,1005400000100007290,1005400000100007300,1005400000100007310',
    'N_11_14_16': '1005400000100011280,1005400000100011290,1005400000100011300,1005400000100011310',
    'N_06_14_16': '1005400000100006280,1005400000100006290,1005400000100006300,1005400000100006310',
    'N_04_14_16': '1005400000100004280,1005400000100004290,1005400000100004300,1005400000100004310',
    'N_05_14_16': '1005400000100005280,1005400000100005290,1005400000100005300,1005400000100005310',

    'N_09_15_17': '1005400000100009300,1005400000100009310,1005400000100009320,1005400000100009330',
    'N_08_15_17': '1005400000100008300,1005400000100008310,1005400000100008320,1005400000100008330',
    'N_10_15_17': '1005400000100010300,1005400000100010310,1005400000100010320,1005400000100010330',
    'N_07_15_17': '1005400000100007300,1005400000100007310,1005400000100007320,1005400000100007330',
    'N_11_15_17': '1005400000100011300,1005400000100011310,1005400000100011320,1005400000100011330',
    'N_06_15_17': '1005400000100006300,1005400000100006310,1005400000100006320,1005400000100006330',
    'N_04_15_17': '1005400000100004300,1005400000100004310,1005400000100004320,1005400000100004330',
    'N_05_15_17': '1005400000100005300,1005400000100005310,1005400000100005320,1005400000100005330',

    'N_09_14_18': '1005400000100009320,1005400000100009330,1005400000100009340,1005400000100009350',
    'N_08_14_18': '1005400000100008320,1005400000100008330,1005400000100008340,1005400000100008350',
    'N_10_14_18': '1005400000100010320,1005400000100010330,1005400000100010340,1005400000100010350',
    'N_07_14_18': '1005400000100007320,1005400000100007330,1005400000100007340,1005400000100007350',
    'N_11_14_18': '1005400000100011320,1005400000100011330,1005400000100011340,1005400000100011350',
    'N_06_14_18': '1005400000100006320,1005400000100006330,1005400000100006340,1005400000100006350',
    'N_04_14_18': '1005400000100004320,1005400000100004330,1005400000100004340,1005400000100004350',
    'N_05_14_18': '1005400000100005320,1005400000100005330,1005400000100005340,1005400000100005350',

    'N_09_13_15': '1005400000100009260,1005400000100009270,1005400000100009280,1005400000100009290',
    'N_08_13_15': '1005400000100008260,1005400000100008270,1005400000100008280,1005400000100008290',
    'N_10_13_15': '1005400000100010260,1005400000100010270,1005400000100010280,1005400000100010290',
    'N_07_13_15': '1005400000100007260,1005400000100007270,1005400000100007280,1005400000100007290',
    'N_11_13_15': '1005400000100011260,1005400000100011270,1005400000100011280,1005400000100011290',
    'N_06_13_15': '1005400000100006260,1005400000100006270,1005400000100006280,1005400000100006290',
    'N_04_13_15': '1005400000100004260,1005400000100004270,1005400000100004280,1005400000100004290',
    'N_05_13_15': '1005400000100005260,1005400000100005270,1005400000100005280,1005400000100005290',

    'N_09_12_14': '1005400000100009240,1005400000100009250,1005400000100009260,1005400000100009270',
    'N_08_12_14': '1005400000100008240,1005400000100008250,1005400000100008260,1005400000100008270',
    'N_10_12_14': '1005400000100010240,1005400000100010250,1005400000100010260,1005400000100010270',
    'N_07_12_14': '1005400000100007240,1005400000100007250,1005400000100007260,1005400000100007270',
    'N_11_12_14': '1005400000100011240,1005400000100011250,1005400000100011260,1005400000100011270',
    'N_06_12_14': '1005400000100006240,1005400000100006250,1005400000100006260,1005400000100006270',
    'N_04_12_14': '1005400000100004240,1005400000100004250,1005400000100004260,1005400000100004270',
    'N_05_12_14': '1005400000100005240,1005400000100005250,1005400000100005260,1005400000100005270'
}

3.3 Python智能订场

万事俱备,只欠代码了。刚才Charles抓包的请求,改写为Python代码即可:

# 核心函数
def sku_order_submet(date_str, skus, access_token_wx='878**********6d2'):
    '''
    羽毛球订场地
    params:
      date_str: 日期,如2021-10-23
      skus: 场地号,如1005400000100001280,1005400000100001290,1005400000100001300,1005400000100001310
      access_token_wx: 唯一识别号
    
    return:
        
    
    '''

    import requests

    url = 'https://wx-api.papa.com.cn/v2'


    headers = {
        'Host': 'wx-api.papa.com.cn',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Origin': 'https://wx.papa.com.cn',
        'Accept': 'application/json, text/plain, */*',
        'User-Agent': 'MicroMessenger/6.8.0',
        'Referer': 'https://wx.papa.com.cn/?version=1634094485&v=pro2&flag=413872.100540&token=&flag=413872.100540&v=pro2',
        'Accept-Language': 'zh-cn' 
    }

    data = {
        'client_type': 'browser',
        # 'skus': '1005400000100001280,1005400000100001290,1005400000100001300,1005400000100001310', # 场地sku号
        # 'skus': '1005400000100001320,1005400000100001330', 
        'skus': skus,
        # 'date_str': '2021-10-25', # 时间
        'date_str': date_str,
        'stadium_id': '100540', # 不知道干啥的,好像都一样
        'sport_tag': '1', # 1是羽毛球
        'r': 'stadia.skuOrderSubmit',
        'access_token_wx': access_token_wx
    }
    
    # import json
    # print(json.dumps(data))
    # 需要verify=False,不然会报401错误
    order_req = requests.post(url=url, headers=headers, data=data, verify=False)

    # order_req
    # order_req.json()
    
    return order_req
    
    
import warnings
warnings.filterwarnings("ignore")

# 循环去查询订场地

date_str = '2021-10-19'

for number, skus in sku_dict.items():
    print('预定日期date_str: ', date_str)
    print('    场地号: ', number)
    
    # skus = '1005400000100001280,1005400000100001290,1005400000100001300,1005400000100001310'
    req = sku_order_submet(date_str=date_str, skus=skus, access_token_wx='878***********d6d2') # 改为你自己的
    
    
    print('    场地: ', skus.split(',')[0][-4:-3]+'号场地')
    print('    skus: ', skus)
    status_code = req.status_code
    print('    status_code: ', status_code)

    error = req.json()['error'] if 'error' in str(req.json()) else None
    print('    error: ', error)
    
    
    if status_code == 200:
        print('Good,订购成功,快去微信付款吧')
        print('\n')
        break
    print('\n')

订购成功之后,去微信里面打开链接查看订购结果,可以看到订购成功,然后付款就行(ps:选择微信支付有优惠),如果7分钟之内没有支付订单,订单会自动取消。

这样我们就把订场地代码完成,后续只需要做一个定时任务,然后晚上00点跑就行,然后在手机支付。

4. 结束语

本周场地之前已经订好了,代码效果周五晚上试试,看看效果如何!!!

因为订单会在7分钟内失效,而且场地是晚上00点开始订,所以睡着了估计就没场地了,后续增加个自动字支付就更好了(估计比较难)。

文章内容不得用于商业用途,仅做学习交流。使用本文章内容出现的任何法律问题与本作者无关。

在丰台想打羽毛的小哥哥小姐姐一起来呀!!