尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python医院抢号脚本解析:从爬虫到自动化预约的实战指南

Python医院抢号脚本解析:从爬虫到自动化预约的实战指南 简介面向需要解决医院挂号难问题的个人开发者或Python自动化初学者这份代码包演示了如何用Selenium模拟浏览器登录华西医院挂号系统并通过手机验证码自动接收、倒计时定时进入、电子健康卡选择、ddddocr图形验证码识别以及循环检测确认按钮等环节完成从登录到提交挂号的完整流程。资源共6个文件包含Python主脚本、config配置文件、依赖清单txt、说明文档md、演示页面html及编辑器配置压缩包仅8KB轻量便于快速调试。目前已有343人学习下载。通过阅读关键代码片段与实现思路读者不仅能直接理解脚本与医院网页的交互逻辑还可以掌握自动化登录、验证码OCR识别、定时任务触发等常见反爬对抗技巧为改造成其他预约平台提供可复用的技术框架。 最近后台收到不少私信问“Python医院抢号脚本到底能不能写”我直接说能写而且并不复杂。这个项目其实就是把咱们平时在预约挂号平台上手动操作的动作用Python脚本给自动跑起来——查询号源、盯着余号变化、辅助提交挂号信息。我最早写它是为了帮家里老人盯三甲医院的专家号那个放号速度手点根本拼不过。如果你也想给自家人做个“号源提醒助手”或者正在学习Python爬虫和浏览器自动化这篇可以当一份落地参考。先说清楚我这里的定位是“辅助工具”不是黄牛系统。我不鼓励任何人用脚本去批量抢号、倒卖号源更不赞成绕过验证码和医院的风控机制。代码写出来是帮你理解自动化预约的原理然后再用合规的方式简化自己或家人的挂号流程。医院系统现在都做得很严格任何脚本都扛不住无脑的高频请求最终还是要靠合理的时间策略和一点点运气。1. 项目概述与整体思路1.1 这个脚本到底解决什么问题一个典型的挂号场景是上午 8 点放号号源池里专家号就 20 个你 7:59 打开 App这个科室那个医生来回切换等页面加载完号已经没了。人工的问题不是手速慢而是眼睛没法一直盯在一个页面上。Python 脚本则可以用一个循环每隔几秒去查一次指定医生的排班接口一旦发现余号数量大于 0立刻写日志、发通知甚至帮你把挂号提交这一步做掉一部分。所以核心需求拆开就三个定时查询、状态判断、结果通知。再往下才是登录态维护、接口字段解析和异常处理。很多人一上来就想把“抢”字做得很猛实际上真正好用的脚本反而是“稳”——请求频率不高、不会给服务器造成压力、不会把自己的 IP 或账号搞出风控。能用脚本解决的是“盯梢”这个动作而不是创造号源。1.2 技术选型requests还是Selenium写这类脚本绕不开 requests 和 Selenium 的选择。我的习惯是先打开浏览器开发者工具把挂号网页完整跑一遍看看有没有可以直接调用的 JSON 接口。如果有就优先用 requests因为轻量、快而且方便在服务器上跑。如果整个网站都是前端 JS 动态渲染接口又做了复杂的加密参数那再考虑 Selenium 这类浏览器自动化。方案优点缺点适用场景requests Session轻量、执行快、资源占用低需要手动抓接口、构造参数有清晰 JSON 接口、能处理登录态Selenium / Playwright像真人操作浏览器能过一部分前端校验重、慢、容易被检测接口加密太复杂或需要模拟点击混合方案用 requests 查号用浏览器处理验证码两套代码维护半自动场景验证码人工介入从我实际测试来看requests 方案更适合做“定时查号通知”这件事Selenium 适合做“最后提交那一下”。顺便说一句现在很多平台的接口返回都带签名参数requests 要还原签名逻辑比较痛苦这时候用浏览器自动化反而省心。要是非得用 Selenium 全程盯梢也不是不行就是 CPU 和内存占用有点难看笔记本风扇会转得跟拖拉机一样。1.3 合规边界与使用约束这里必须说几句难听的。医院预约系统不是私人服务器它面向的是所有患者。脚本的合理使用边界是登录你自己的账号查询公开的号源信息帮助自己在规定时间内完成预约。脚本不应该做的事情包括注册大量账号刷号、用并发请求把服务打挂、破解验证码或签名算法、对同一号源进行多账号抢占。任何一个动作踩线轻则账号被冻结重则可能涉及更麻烦的纠纷。简单说如果你写的脚本拿来自用只是为了“盯号并提醒”这属于效率工具如果拿去接单、做黄牛那就是另一回事了。我的博客只分享前者。代码里的接口和字段都用了示例域名你学习的时候也需要用自己合法登录后的接口来测试不要对医院系统发起任何未授权的扫描和压力测试。记住一个原则脚本是帮你“等号”的不是帮你“插队”的。2. 核心细节解析医院预约系统的逻辑与接口2.1 医院预约挂号的基本流程不管哪个平台在线挂号的用户流程差异不大先登录然后进入“预约挂号”功能选择院区、科室、医生接着看到某一天的排班和剩余号源选定时间后填写或确认就诊人最后提交预约。提交成功会生成预约流水号有的医院还要求在线支付挂号费。从代码角度看这条流程对应着几个 HTTP 请求登录鉴权、获取科室列表、查询排班、提交预约。其中最关键的是“查询排班”这一步因为我们盯的就是它返回的remaining或类似字段。医院平台一般会在每天固定时间点更新排班比如早上 8 点释放新一天的号源脚本就在放号前几分钟启动小频率轮询这样成功率高也不容易触发风控。注意有的平台是放号时间随机或者分时段放号这种情况就需要自己观察几天的规律再写定时的逻辑。2.2 抓包定位关键接口要拿到接口不需要什么高级工具浏览器自带开发者工具就够了。我一般用 Chrome 的 F12切到 Network 面板刷新页面后手动点一遍挂号流程然后过滤 XHR/Fetch 请求逐个点开看 URL、请求头和响应体。有些平台会把接口路径藏在 JS 文件里这时候直接搜索关键词“schedule”或“appointment”会更快。常见的接口名字类似这样接口用途方法常见路径关键参数登录POST/api/loginusername, password查询科室GET/api/departmentshospitalId, parentId查询医生排班GET/api/scheduledepartmentId, doctorId, date提交预约POST/api/appointmentscheduleId, patientId, timeSlot抓包时重点看三个东西登录后接口返回的 token 存放在哪里排班接口的响应里有没有“余号”字段提交预约是否需要签名或验证码。把这些记录到自己的接口文档里脚本才有依据。不要凭感觉猜字段名我见过太多人抓包抓一半然后对着返回的 HTML 找 JSON浪费一晚时间。2.3 常见反爬机制与应对思路医院平台虽然不会像电商平台那样把反爬做得特别变态但基本防护还是有的。常见的有登录后 Token 时效、请求头 User-Agent 校验、短时间请求频率限制、滑块验证码以及接口返回内容加密。应对思路也很直接用 requests.Session 保持 cookie 和 token请求头里带上正常的 User-Agent 和 Referer轮询间隔放到 3 秒以上并加随机抖动一旦遇到验证码或需要二次确认就切到半自动让人来处理。绝不要自己写 OCR 去识别医院验证码这种操作不仅成功率低而且明显越界。另外很多平台的 App 接口和网页接口是两套体系App 接口的签名通常更复杂不建议新手一上来就逆向 App。3. 实操过程从环境搭建到核心代码实现3.1 环境准备Python安装与依赖先确认本机 Python 版本。在终端输入python --version如果是 3.8 以下建议去 python.org 下载 3.10 或 3.11安装时记得勾选“Add Python to PATH”。我这里测试用的是 Python 3.10.11操作系统 Windows 11理论上跨平台也没问题。依赖安装很简单pip 一条命令搞定pip install requests selenium beautifulsoup4 schedule其中 requests 用来做接口请求schedule 用来做定时任务selenium 按需安装。如果你只做“查号通知”不需要 selenium那就可以少装一个。后面我贴的代码主要是 requests schedule 的组合简单、轻量适合长期挂着。如果想把脚本打包成 exe 给家人用可以再加一行pip install pyinstaller用pyinstaller -F monitor.py导出单文件熟练后整个过程不到十分钟。3.2 登录与保持会话登录是整个脚本的地基。很多医院平台登录后返回一个 token后续接口都靠这个 token 鉴权。用 requests.Session 的好处是它自动帮你维护 cookie但 token 不一定在 cookie 里有时候在响应体或者 header 里需要手动带。下面的代码是一个登录示例接口域名请替换成你自己抓包拿到的真实地址import requests import logging logging.basicConfig(levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s) class HospitalClient: def __init__(self, username, password): self.session requests.Session() self.username username self.password password self.base_url https://example-hospital.com/api self.token None def login(self): url f{self.base_url}/login payload {username: self.username, password: self.password} headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://example-hospital.com/ } resp self.session.post(url, jsonpayload, headersheaders, timeout10) resp.raise_for_status() data resp.json() # 具体 token 字段名以实际接口为准 self.token data.get(data, {}).get(token) self.session.headers.update({Authorization: fBearer {self.token}}) logging.info(登录成功token 已保存)注意一点密码不要硬编码在脚本里。我通常用环境变量或者挂一个config.ini配置文件权限设成只读避免源码泄露时账号信息一起泄露。configparser是 Python 自带库读写都很方便没必要专门引入第三方配置模块。3.3 查询号源与余号监控登录之后最重要的一步就是查询指定医生的排班。查询接口一般要传医生 ID、科室 ID、日期等参数。我们关注返回里剩余号源的数量用循环定时去拉。import time import random def query_schedule(self, doctor_id, date): url f{self.base_url}/schedule params {doctorId: doctor_id, date: date} resp self.session.get(url, paramsparams, timeout10) resp.raise_for_status() return resp.json() def monitor_until_available(self, doctor_id, date, max_times20, interval(3, 5)): for i in range(max_times): try: data self.query_schedule(doctor_id, date) items data.get(data, []).get(list, []) available [item for item in items if item.get(remaining, 0) 0] if available: logging.warning(发现可预约号源: %s, available) # 这里可以接推送通知见 4.4 节 return available else: logging.info(第 %d 次查询暂无余号, i 1) except Exception as e: logging.error(查询异常: %s, e) time.sleep(random.uniform(*interval)) logging.info(达到最大查询次数停止监控) return []这里的 interval 是随机延时3 到 5 秒随机抖动。别小看这个抖动固定间隔在服务端检测里非常扎眼。循环次数根据放号时间动态调比如放号前 2 分钟启动查个 30 次就够了。我测试时习惯先把max_times设成 3验证代码没问题再改成 30避免调试阶段一直发请求。3.4 提交预约半自动与全自动的取舍如果查到位号源下一步就是提交预约。坦白说我自己的脚本默认不做全自动提交。原因有两个一是很多平台在提交时有滑块验证码或短信验证码自动化成本高二是万一自动提交导致误操作比如选错就诊人、抢错时间段处理起来很麻烦。所以更推荐半自动脚本一旦发现余号就立刻弹出浏览器并自动登录到确认页面然后由人点“确认挂号”。这样既把前面最耗时的“盯号”过程自动化了又保留了人工判断的最后一道闸。如果你确实要理解全自动提交的逻辑下面是一段基于 requests 的提交框架注意提交前一定要确认参数完整并且通过合法账号操作def submit_appointment(self, schedule_id, patient_id, time_slot): url f{self.base_url}/appointment payload { scheduleId: schedule_id, patientId: patient_id, timeSlot: time_slot, } resp self.session.post(url, jsonpayload, timeout10) result resp.json() if result.get(code) 0: logging.info(预约提交成功订单号: %s, result.get(data, {}).get(orderId)) else: logging.error(预约提交失败: %s, result) return result这里没有处理验证码。如果实际接口要求验证码请改成 Selenium 半自动在浏览器里手动输入验证码后再继续。Selenium 的操作核心是找到输入框 ID然后send_keys填入账号密码最后把控制权留给人工。你可以加一个input(请在浏览器中完成验证码然后按回车继续)这样脚本在关键步骤前会自动暂停。3.5 加入日志、随机延时与异常重试脚本要长时间运行稳定性比花哨的技巧更重要。我建议至少做三件事日志输出、随机延时、异常重试。日志我用 Python 自带的 logging输出到文件和控制台方便事后排查。随机延时上面已经写了主要是防止请求频率太固定。异常重试要控制次数不能无限重试否则账号很容易被风控。一个简单的定时启动流程可以这样import schedule def job(): client HospitalClient(your_username, your_password) client.login() client.monitor_until_available( doctor_iddoctor_001, date2025-06-01, max_times30, interval(3, 5) ) if __name__ __main__: job()如果想每天固定时间跑可以用schedule.every().day.at(07:55).do(job)但注意放号时间不一定每天一致最好自己确认。实际跑下来我习惯先在放号前 5 分钟启动一次查询看到有号就通知而不是全天 24 小时挂着。因为长时间高频轮询不仅浪费资源也容易让平台认为你在恶意访问。4. 常见问题与排查技巧实录4.1 登录态失效频繁掉线怎么办现象是脚本跑了一会儿后请求返回未登录或 401。常见原因有三个token 过期登录接口有设备限制服务器检测到异常活动强制下线。解决办法也比较明确如果 token 有效期短就在每次查询前检查登录状态失效就重新登录如果平台限制同一账号多处登录那就确保脚本只在一台机器运行不要同时开着 App。还可以把 token 缓存到本地文件避免每次重启都要登录但要注意文件权限别明文存放在公共目录。我用过一个简单方案把 token 存到~/.hospital_token文件权限设成600这样只有当前用户能读。4.2 请求被拦截302/403风控排查我遇到过最典型的场景是本地测试一切正常放到云服务器跑了两分钟开始疯狂返回 403。一查原来是服务器 IP 被风控了。医院平台会根据 IP 维度对高频访问做封禁云服务器 IP 段往往更容易被盯上。排查步骤建议按顺序来先看响应头里的 Set-Cookie 和响应体错误信息再对比正常浏览器请求和脚本请求的 User-Agent、Referer 差异然后把请求间隔从 2 秒拉到 8 秒加随机抖动观察是否恢复最后如果仍然 403就换网络环境或者考虑用浏览器方案。这里有一个很实用的小技巧在脚本里先用一个HEAD请求访问首页拿到 cookie 后再发业务请求成功率通常会高一些。4.3 验证码不要破解走人工很多攻略会教你怎么用第三方 OCR 识别验证码这个我强烈不建议碰。医院验证码通常不是单纯的图片有时是滑块、点选文字、短信动态码自动化成本极高而且从合规角度讲绕过验证码已经明显越界。实用的做法是半自动用 Selenium 打开浏览器执行到验证码之前的所有步骤然后把控制权交给人。你可以加一个等待输入的逻辑比如input(请在浏览器中完成验证码然后按回车继续)。这样既不破坏规则又节省了人力盯屏的时间。尤其是手机短信验证码除了人工输入没有任何合法且稳定的方式能绕过所以不要在这上面浪费时间。4.4 推送通知抢到号后第一时间提醒脚本查到号源后如果人不在电脑前光有日志不够最好推到手机。最简单的方式是用企业微信机器人、钉钉机器人或者 Server酱原理都是往一个 Webhook 地址 POST 一段 JSON。示例import requests def send_wechat_robot(webhook_url, message): resp requests.post( webhook_url, json{msgtype: text, text: {content: message}}, timeout5 ) if resp.status_code 200: logging.info(通知发送成功) else: logging.error(通知发送失败: %s, resp.text)在发现余号的地方调用send_wechat_robot(https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyxxx, 发现号源请尽快处理)即可。注意 Webhook 地址不要泄露到你的代码仓库里用环境变量或单独配置存。实际使用中推送消息最好带上医生、日期、时间段这些关键信息免得手机弹个通知你还得赶紧开电脑看。我在实际使用中最深的体会是这个项目真正的难点不在代码而在接口变化和环境差异。今天还能用的排班接口明天医院可能就换了路径甚至加了加密参数。所以脚本要保持简单模块之间解耦出了问题能快速定位。另外心态也很重要脚本只是提高了一点效率号源本身是有限的拼的还是谁更自律、谁更守规矩。如果你也想拿 Python 练手完全可以把这个需求当成一个很真实的爬虫和自动化项目来写但请一定遵守平台规则别把你的账号和 IP 搭进去。最后再分享一个小技巧写这类脚本时把核心的查询、提交、通知分别封装成独立函数接口变了只改数据解析那一段其余逻辑不用动维护起来省心很多。本文还有配套的精品资源点击获取
返回列表