某医院挂号记
近来有某医院的产科挂号需求,平台规则:
- 放号时间:每天8:00 AM
- 挂号时间:8:00-23:00
- 男性不能挂产科
- 产科必须使用人脸识别验证通过后,自动提交挂号(人脸数据有效期1分钟以内,每次验证人脸需要13-15秒左右)
- 只能通过微信网页端操作挂号
- [更新]每10s只能提交1次验证
关注退号情况
每30s检查一次,有余号则推送到Bark,以下为python实现
1 import requests 2 import time 3 s = requests.session() 4 ###带连续*号为地址隐藏 5 def send_err(text): 6 url = f'https://api.day.app/******/挂号错误提醒/{text}?sound=update&isArchive=1&level=timeSensitive' 7 requests.get(url) 8 def send_hook(text): 9 url = f'https://api.day.app/******/挂号提醒/{text}?isArchive=1&sound=update&level=timeSensitive&url=wechat://' 10 requests.get(url) 11 url = f'https://api.day.app/******/挂号提醒/{text}?isArchive=1&sound=update&level=timeSensitive&url=wechat://' 12 requests.get(url) 13 def get_phpsessid(times = 3): 14 #获取云中的phpsessid 15 try: 16 return requests.get('https://******?id=7ri5os2y&projectid=21020216034545855318',timeout = 2).json()['data']['SYS_string1'] 17 except: 18 print(f'phpsessid获取失败,{times}s后重试') 19 send_err('phosessid获取失败') 20 time.sleep(times) 21 times += 1 22 return get_phpsessid(times) 23 def get_doc_list(deptId): 24 phpsessid = get_phpsessid() 25 s.headers.update({ 26 'User-Agent':'Mozilla/5.0 (iPhone; CPU iPhone OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.29(0x18001d38) NetType/4G Language/zh_CN', 27 'cookie':f'PHPSESSID='+phpsessid, 28 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 29 }) 30 #获取排班列表 31 url = 'https://******/index.php?g=WapApi&m=Register&a=getRegistDate' 32 date_c = 'g=WapApi&m=Register&a=getRegistDate' 33 try: 34 r = s.post(url,data=date_c,timeout=2).json() 35 except: 36 print(f'获取排班日期失败,5s后重试') 37 time.sleep(5) 38 return get_doc_list(deptId) 39 date_list = [i['date'] for i in r['data'][::-1] if i['stop']==0]#排除掉stop=1的日期项,周末没有建档号 40 41 #获取医生列表 42 for day in date_list: 43 url = 'https://******/index.php?g=WapApi&m=Register&a=getDoctorList' 44 c = f'deptId={deptId}&date={day}&SessionType=&LabelId=0&districtCode=2' 45 r = s.post(url=url,data=c).json() 46 if r['state'] == 1: 47 for doc in r['data']: 48 if f"{doc['SeqNoStrLast']}" != '0': 49 docName = doc['docName']#医生姓名 50 period = doc['period']#上下午 51 number = doc['SeqNoStrLast']#余号 52 text = f'{day} {period}-{docName}-余{number}' 53 # print(text) 54 send_hook(text) 55 else: 56 text = r['errorMsg'] 57 send_err(f'挂号系统报错{text},10s后重试') 58 time.sleep(10) 59 return get_doc_list(deptId) 60 time.sleep(1) 61 62 ############参数 start############# 63 #deptId:产科-1336,产后复查-1933 64 deptId = 1336 65 ############参数 end############# 66 while 1: 67 get_doc_list(deptId) 68 print('.',end='') 69 time.sleep(30)
准备工作:
为方便分析网络请求,可在web端登录此网站,使用Edge浏览器+修改请求头的插件(Gooreplacer)
在插件中设置请求头修改参数,只需修改User-Agent、Cookie两个参数。若设置正确,则可正常进入web端
经测试,web端不能挂号成功(挂号成功后没有付款信息,在挂号列表也不会有显示)
【妇产科>产科】的号比较紧缺,不好测试业务逻辑(主要是人脸);可以选择【妇产科>产后复查门诊】等,号源比较充足且能验证人脸的类目测试
方案1:绕过人脸识别(失败)
通过抓包,在确认挂号阶段,有一个html文件"**/tpl/Wap/default/WxView/registerAndAppoint/selectCard.html"
(经验证,此文件与登录者无关,代码完全一致)
描述人脸或图片验证的选择方式:
红框部分的意思为:请求http://***isFace,若返回的**asdfafs为“binoigaibno”,则启用人脸,否则弹出验证码,验证:
通过抓包得到isFace的返回数据可见,只需修改返回的binoigaibno则可直接启用验证码,可以直接Mock此请求
下面是测试,可以Mock成功,原本应该验证人脸的已经改成图片,但是在提交阶段验证失败,无解。
同理,修改Html文件,使其100%走人脸,仍然出现上述情况
方案2:卡点提交(重放请求)
根据挂号流程:在7:45会开启人脸识别通道(按键变绿),通过后,自动提交挂号申请。但每次人脸识别的时间不一致,提交挂号的时间点不可控制。
首先想到是把人脸请求包在本地截断,等到8:00再手动放出去(可以用机器重放,时间更精准)
再进一步人脸识别时间兴许可以提前,可以在倒计时上下点功夫
再再进一步,可以多终端登录,提升成功率(2023.02.27更新:策略更新,不支持连续提交)
需要注意,经测试,人脸识别成功的数据包有效期只有45s左右,加之每次识别需要15s左右的时间,所以只能准备2-4个请求包。
· 改善点:Moke时间请求
通过网络分析可以发现每进入挂号页面会有一次检查时间的请求,URL包含"checkTime",可以将此请求Mock,使倒计时提前结束,提前进入挂号流程:
Ps:在Edge浏览器中可以使用requestly插件实现Mock
· 改善点:取消人脸等待时间
经测试,每次验证成功后到提交挂号数据有一定延迟,检查Html代码可以定位到,可以将此处置为1
· 改善点:手动提交挂号
经测试,每次挂号后均是自动提交挂号,这里可以借助weui.dialog提醒模块,将流程改为弹出窗口提醒
将提交方法塞进确定按钮里,即可实现点确认再提交挂号
· 改善点:机器提交
将多个挂号请求包保存到本地,机器设置定时发送,或许可以增加成功率
· 改善点:矫正时间
由于本地时间可能与服务器时间有误差,故取服务器时间,到点后自动提交
本文来自博客园,作者:airgeek,转载请注明原文链接:https://www.cnblogs.com/airgeek/p/17040239.html