使用Python脚本自动订餐

1,177 阅读4分钟

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

接口分析

参数分析

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

  1. 获取菜单接口:meican.com/preorder/ap…???
  2. 下单接口:meican.com/preorder/ap…???

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

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

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

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

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

响应分析

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

"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"
        }
    ]

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

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

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

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

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

脚本编写

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

#!/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:

<?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脚本包了一下:

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

脚本一定要给执行权限啊

chmod 775 xxx.sh

StartCalendarInterval

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

OutPath

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

加载任务

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

 launchctl load -w com.meican.plist

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

launchctl unload com.meican.plist

如果想立即执行一次:

launchctl start com.meican.plist

最后

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

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

码字不易,且看且珍惜