使用Python脚本自动订餐

公司员工餐选用的美餐平台,但是经常由于太忙忘记点餐,导致饿着肚子加班。反正菜品的可选项比较少,想着要是能自动点一个就好了。于是有了以下脚本。

接口分析

参数分析

自动点餐无非就是模拟人的行为,自动请求一些关键接口。于是分析了一下美餐网的web端接口,发现关键接口如下:

  1. 获取菜单接口:https://meican.com/preorder/api/v2.1/restaurants/show?tabUniqueId=???&targetTime=???&restaurantUniqueId=???&client_id=???&client_secret=???
  2. 下单接口:https://meican.com/preorder/api/v2.1/orders/add?client_id=???&client_secret=???

有了接口只是第一步,第二步开始分析、尝试接口的哪些参数是静态的,哪些是动态的。

首先通过多个请求,可以得知,上述两个关键接口中的client_idclient_secret是静态的固定不变的,猜测可能是web端的身份。

然后第一个接口中的restaurantUniqueId可以发现多次请求并没发生变化,并且通过命名基本可以得出,应该是类似企业食堂在美餐那边的一个唯一id。

第一个接口中的targetTime,通过数据样例分析,以及对比其他非关键接口数据,可以得知,应该是企业在美餐网设置的点餐截止时间。例如我们公司配置的是早餐6:00截止点餐,那么这个targetTime就是 yyyy-MM-dd +06:00。

最后第一个接口还有一个参数tabUniqueId ,最开始我以为是完全静态的,后面通过踩坑得知,这个应该是代表每一餐的唯一id,早、中、晚均不一样。

响应分析

第一个接口拿到的关键响应如下(忽略了一些):

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
"dishList": [
{
"dishSectionId": xxxx,
"id": xxxx,
"isSection": true,
"name": "晚餐",
"originalPriceInCent": 0,
"priceInCent": 0,
"priceString": ""
},
{
"dishSectionId": xxxx,
"id": y1,
"isSection": false,
"name": "周五 水饺&花生米拌黄瓜",
"originalPriceInCent": 1100,
"priceInCent": 1100,
"priceString": "11"
},
{
"dishSectionId": xxxx,
"id": y2,
"isSection": false,
"name": "周五 A套餐 小鸡炖蘑菇&干炸小黄鱼&肉沫豆腐&土豆片炒肉",
"originalPriceInCent": 1100,
"priceInCent": 1100,
"priceString": "11"
}
]

这个需要结合第二个接口参数来分析,哪些是有用的信息,第二个接口的请求参数如下:

1
{"corpAddressRemark":"","corpAddressUniqueId":"xxxx","order":orderP,"remarks":remarkP,"tabUniqueId":uu['tab'],"targetTime":targetTime,"userAddressUniqueId":"xxxx"}

其中targetTime应该与第一个接口的一致。remarks没懂到底有没有用,tabUniqueId跟上个接口也是一致。最后order是跟第一个接口响应有关的,如下:

1
[{"count":1,"dishId":y1}]

其中dishId就是第一个接口dishList中的id。好了关键信息有了,接下来就可以开始编写脚本了。

脚本编写

脚本就不废话了,由于Python不是主力语言,平时写的少,而且由于是摸鱼时间🐟来写的,所以写的比较粗糙和不规范。🤦‍♂️

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
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time
import sys
import requests
import json
import datetime


tomorrow = (datetime.datetime.now()+datetime.timedelta(days=1)).strftime("%Y-%m-%d")
print("tomorrow:", tomorrow)
headers={}
headers1={}
headers2={}
headers3={}

user1={'headers':headers,'phone':'xxxxx','tab':''}
user2={'headers':headers1,'phone':'xxxxx','tab':''}
user3={'headers':headers2,'phone':'xxxxx','tab':''}
user4={'headers':headers3,'phone':'xxxxx','tab':''}

users=[user1,user2,user3,user4]

add_url="https://meican.com/preorder/api/v2.1/orders/add?client_id=xxxx&client_secret=xxxx"
count=0
for i in range(0, len(users)):
try:
count=count+1
uu = users[i]
url = "https://meican.com/preorder/api/v2.1/restaurants/show?tabUniqueId="+uu['tab']+"&targetTime=" + tomorrow + "+06:00&restaurantUniqueId=xxxx&client_id=xxxx&client_secret=xxxx"
r = requests.get(url, headers=uu['headers'])
data = json.loads(r.content)
if 'dishList' in data:
bData = data['dishList'][1]['name']
aAata = data['dishList'][2]['name']
if count==1:
# 发送企业微信通知
print("发通知")
botData=json.dumps({"msgtype":"text","text":{"content":"明日早餐:\r\n"+bData+"\r\n"+aAata,"mentioned_list":["@all"]}})
botR = requests.post(url='https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxx', data=botData, headers={"Content-Type":"application/json;charset=utf-8"})
print(botR.content)
print("menu: ", aAata, bData)
bDataId=str(data['dishList'][1]['id'])
targetTime = tomorrow + ' 06:00';
orderP = "[{\"count\":1,\"dishId\":"+bDataId+"}]"
remarkP = "[{\"dishId\":\""+bDataId+"\",\"remark\":\"\"}]"
postData = {"corpAddressRemark":"","corpAddressUniqueId":"xxxxx","order":orderP,"remarks":remarkP,"tabUniqueId":uu['tab'],"targetTime":targetTime,"userAddressUniqueId":"xxxxx"}
print('当前用户:',uu['phone'])
ar = requests.post(url=add_url, data=postData, headers=uu['headers'])
print(ar.content)
data2 = json.loads(ar.content)
if 'status' in data2:
if 'SUCCESSFUL'==data2['status']:
botData2 = json.dumps({"msgtype": "text", "text": {"content": "已为尊贵的会员点餐成功", "mentioned_mobile_list": [uu['phone']]}})
print("成功通知:", botData2)
# 发送企业微信通知
botR2 = requests.post(url='https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxx',data=botData2, headers={"Content-Type": "application/json;charset=utf-8"})
print(botR2.content)
print("点餐成功!")
else:
print("点餐失败!")
else:
print("点餐失败!")
else:
print('无可用信息:', data)
except Exception:
print('err')
print("Good bye!")

定时任务

最后由于本人用的Mac,所以直接使用了launchctl

定义任务

首先进入~/Library/LaunchAgents,在目录下创建一个文件com.meican.plist:

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.meican.plist</string>
<key>ProgramArguments</key>
<array>
<string>/Users/zhaojingzhou/workspace/sources/meican.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>00</integer>
<key>Hour</key>
<integer>16</integer>
<!-- <key>Day</key>
<integer>*</integer> -->
<key>Weekday</key>
<array>
<integer>1</integer>
<integer>2</integer>
<integer>3</integer>
<integer>4</integer>
<integer>7</integer>
</array>
</dict>
<key>StandardOutPath</key>
<string>/Users/zhaojingzhou/workspace/sources/stdout</string>
<key>StandardErrorPath</key>
<string>/Users/zhaojingzhou/workspace/sources/error</string>
</dict>
</plist>

ProgramArguments

任务执行的脚本,我这里用shell 将上面写的Python脚本包了一下:

1
/usr/local/bin/python3.9 /Users/zhaojingzhou/workspace/sources/meican.py

脚本一定要给执行权限啊

1
chmod 775 xxx.sh

StartCalendarInterval

我这里定义的是周1,2,3,4,7下午16:00执行。需要注意的是0,7都代表周日。

OutPath

StandardOutPath,StandardErrorPath 代表标准输出,和错误输出,用来排查脚本错误。

加载任务

所有的的都准备好了之后执行:

1
launchctl load -w com.meican.plist

如果修改了任务定义需要unload之后在load,unload:

1
launchctl unload com.meican.plist

如果想立即执行一次:

1
launchctl start com.meican.plist

最后

目前跑了一段时间下来,感觉还行,再也不用饿肚子啦。。。

希望美餐别调整接口。。。

码字不易,且看且珍惜

作者

太阳当空赵先生

发布于

2021-12-24

更新于

2022-02-22

许可协议

评论