用Python订羽毛球场地

羽毛球算是现在比较热门的一项运动,周末经常和同学朋友一起玩,奈何场地难订,而且就算运气好订到了,时间、位置都不是很理想。所以,我今天要撸一个自动订羽毛球场地的代码,方便之后订场地!!!喜欢打羽毛球的来啊,约起来,一起打,虽然我很菜2333….
1. 前言
首先来看看我们平时如何订场地。因为住在北京-丰台,常去的体育馆是丽泽体育馆,他们订场地是在公众号订,如图:

(1)首先关注公众号;
(2)点击羽毛球,选择想订场地的日期;
(3)现在时间段,然后选择时间段,提交订单。
(4)场地是提前7天预订,然后每天晚上00点开始接受预订。
既然要用Python来自动下单订场地,那就得模拟发起提交订单的过程。那就需要先抓包找到这个订场的服务器地址,以及发送的请求数据。
2. 抓包分析
我们将提交订单那个页面的地址复制出来,(https://wx.papa.com.cn/?version=1634094485&v=pro2&flag=413872.100540&token=#/stadium/100540/fieldboard?date=2021-10-16&sport_tag_id=1&sport_tag_name=%E7%BE%BD%E6%AF%9B%E7%90%83),在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点开始订,所以睡着了估计就没场地了,后续增加个自动字支付就更好了(估计比较难)。
文章内容不得用于商业用途,仅做学习交流。使用本文章内容出现的任何法律问题与本作者无关。
在丰台想打羽毛的小哥哥小姐姐一起来呀!!