Python自动实时查询预约网站的剩余名额并在有余额时发邮件提示

  本文介绍基于Python语言,自动、定时监测某体检预约网站指定日期体检余额,并在有体检余额时自动给自己发送邮件提醒的方法。

  来到春招末期,很多单位进入了体检流程。其中,银行(尤其是四大行)喜欢“海检”,即差额体检——收到体检不代表录取的那种;而正巧我春招主要报名的就是银行、国企等,所以单是这周就有4家单位发了体检通知。

  其中,有的单位需要自费体检。在这周4家单位中,交通银行就需自费(我报的还是总行呢)——关于银行的体检,大家可以参考文章建信金融科技软开工程师24届Offer面经(https://blog.csdn.net/zhebushibiaoshifu/article/details/135352867)。贫穷的我自然想到了直接用我们研究所发的免费体检来充当单位体检了——所里每年上半年会提供1次免费体检的机会,往年我都是第一时间就去体检;但今年就是想着可以将其充当入职体检,所以迟迟没有去。因此,这次看到交行的自费体检,就想着预约所里给的这个免费体检——等的就是这一天,能省则省。但是,当我打开所里体检的预约网页后,发现我1个堂堂男学生,竟然最近几天已经没有体检名额了,如下图所示。

  但是,交行给出的体检报告提交截止时间也比较近,所以必须要预约到上图中最近几天的体检;因此,选择用Python监控这几天的体检名额,且若发现最近几天有名额,就给我自己发邮件。当然,更好、更完整的操作是,只要发现最近几天有名额,就直接由程序自动实现预约——但一方面,这个需求若想实现,还得进一步研究后续提交预约时的HTTP请求信息,我太菜了,这个对我而言可能比较麻烦;另一方面,在准备开始写脚本时正好收到了中国星网的体检,他们是无需自费且提供体检报告的那种体检,所以可以直接把星网的体检报告发给交行,相当于暂时又用不到所里的体检了。所以我后来也就没再继续探究上述这个更进一步的需求的实现方法——当然,后面如果有机会,可以再进一步研究一下。

  知道了需求,即可开始尝试实现。首先,虽然所里体检的预约流程主要是在手机上进行的,但它也还是一个普通的网页,而非小程序;较之小程序网页实现起来或许会更容易一些——当然,我也没做过小程序的爬虫,也并不知道小程序的话到底是会更复杂还是更容易;但网页的话,至少流程、思路方面还是比较清晰的。

  我们先查看上图中所示网页的HTML,也就是能显示哪一天是否可预约体检的那个网页的HTML,如下图所示。可以看到,这个网页的HTML(下图)并不含有每一天的预约余额信息(上图),也就是说这些预约余额信息应该是JavaScript动态加载的。

  接下来,我们按下F12键进入浏览器的开发者工具,并选择“Network”;随后,重新刷新当前网页,并观察出现的每一个元素;如下图所示。

  通过观察,找到了具有每天体检余额信息的那1个元素,如下图所示。

  随后,查看这个元素的信息,如下图所示。可以看到,这是1POST请求,具体的请求Headers也是比较常规的。

  这里提一句,我一开始看到这个POST请求的Headers具有伪头部(也就是上图中右下角紫色框内,排在前4个的、Key前带有冒号:的头部),说明其是HTTP/2的请求;所以当时还以为不能用Pythonrequests库了,然后hyper库又不维护了,所以已经准备用HTTPX库来实现需求了。但是后来发现,requests库还是可以正常发送请求并获取回复,所以最后还是选择用更为方便的requests库了。这里我也不太清楚requests库不支持HTTP/2这句话是如何界定的。

  接下来,我们首先撰写Python访问网页并获取体检名额余额的代码;这一部分代码具体如下。

# -*- coding: utf-8 -*-
"""
Created on Thu May 31 22:08:26 2024

@author: fkxxgis
"""

import smtplib
import requests
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

def send_email(subject, body, email_to):
    smtp_server = "smtp.qq.com"
    smtp_port = 465
    email_from = "你的发件邮箱"
    password = "你的邮箱的授权码(不是邮箱密码)"
    
    msg = MIMEMultipart()
    msg["From"] = email_from
    msg["To"] = email_to
    msg["Subject"] = subject
    msg.attach(MIMEText(body, "plain"))
    
    try:
        server = smtplib.SMTP_SSL(smtp_server, smtp_port)
        server.login(email_from, password)
        server.sendmail(email_from, email_to, msg.as_string())
        server.quit()
        print("Email sent successfully!")
    except Exception as e:
        print("Email sent failed!")

url = "POST请求的链接"
url_in = "直达预约界面的链接,设置这个是为了方便从邮件中直达预约网页"
date_need = ["2024-06-03", "2024-06-04", "2024-06-05"]

headers = {"accept": "application/json, text/plain, */*",
           "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
           "content-type": "application/json;charset=UTF-8",
           "cookie": "这里cookie太长了,我就删除了,大家理解即可",
           "origin": "https://hosp.etong-online.com",
           "priority": "u=1, i",
           "referer": "https://hosp.etong-online.com/",
           "sec-ch-ua": "\"Google Chrome\";v=\"125\", \"Chromium\";v=\"125\", \"Not.A/Brand\";v=\"24\"",
           "sec-ch-ua-mobile": "?0",
           "sec-ch-ua-platform": "\"Windows\"",
           "sec-fetch-dest": "empty",
           "sec-fetch-mode": "cors",
           "sec-fetch-site": "same-origin",
           "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
}

data = {"batchCode": "3797",
        "unitCode": "0521",
        "institutionCode": "04010802",
        "token": "xDMxsuTNeHhY8MbYqKEa9A=="
}

response = requests.post(url, headers = headers, json = data)

if response.status_code != 200:
    print ("Access error!")
else:
    response_json = response.json()
    for date in date_need:
        for item in response_json['data']:
            if item['scheduleDate'] == date:
                if item['stockQuantity'] != 0:
                    print(f"Date {date} has stock quantity: {item['stockQuantity']}")
                    
                    subject = f"在{date}这天体检有名额了!"
                    body = f"目前{date}的体检有{item['stockQuantity']}个名额,可以看看要不要预约;若需预约请点击{url_in} 。"
                    email_to = "你的收件邮箱"
                    send_email(subject, body, email_to)
                else:
                    print(f"Date {date} has no stock quantity.")
                break

  其中,代码的大致流程也比较清晰。首先定义send_email()函数,用于给自己发送电子邮件;该函数需要提供邮件主题、正文和收件人邮箱地址。随后的部分,就是发送那个可以返回体检名额余额POST请求,其中的headers部分大家就复制前述浏览器开发者工具中,这个请求的Request Headers就好,data部分则复制Request Payload就好。

  随后,遍历指定日期,检查每个日期是否有剩余预约名额;如果某日期有剩余的预约名额,则打印1条消息并通过电子邮件发送通知——邮件中包括具有体检余额的日期名额数量预约链接等。这里需要注意,如果这个提醒邮件是发给我们个人,就需要自己有2个邮箱(QQ邮箱1个人可以注册多个域名,不同域名的邮箱之间就可以互发邮件)——其中一个作为发件地址,另一个作为收件地址

  我们如果此时执行上述代码,即可看到指定日期内体检名额的剩余情况。其中,如下图所示,某次执行后,发现0603日这一天有体检的名额了(原本写代码时0603日还是没有名额的,这说明有1位同学预约了这一天但是现在取消了)。

  那么,邮箱中就会收到如下图所示的提醒邮件。如果大家开启了类似微信的邮件实时提醒,或手机端邮箱APP应用消息通知,那么相当于就是实时接收体检余额的提醒了。

  为了方便,我们也将体检预约的网页链接放在了邮件中,如下图所示;点击后即可进入预约。

  进入预约网页内,发现果然0603日这一天,当前是可以预约的。如果有需要的话,此时就可以手动预约了。

  当然,上述我们是单次执行程序。如果希望可以在后台定时对体检余额加以检测(例如我想每隔30分钟就自动查询1次),则可以通过如下代码来实现。

# -*- coding: utf-8 -*-
"""
Created on Sat Jun  1 16:04:04 2024

@author: fkxxgis
"""

import time
import datetime
import schedule
import subprocess

count_num = 0

def run_script():
    global count_num
    count_num += 1
    current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"At {current_time}, the script has been run {count_num} time(s).")
    
    result = subprocess.run(["python", "check.py"], capture_output = True, text = True)
    
    print(result.stdout)
    if result.stderr:
        print(result.stderr)

schedule.every(30).minutes.do(run_script)
run_script()

while True:
    schedule.run_pending()
    time.sleep(1)

  上述代码通过schedule库,使得我们可以每30分钟运行1run_script()函数,而这个函数就是用来执行本文前述的那一段监测余量发送邮件的代码——也就是上述代码中的check.py文件;这里因为我将2Python代码文件放在同一路径下了,所以就用了相对路径。

  随后,通过手动调用1run_script()函数,使得程序开始时先执行1次查询,然后再每隔30分钟查询1次。代码最后,进入1个无限循环,并在循环中调用schedule.run_pending()函数,确保按预定计划运行run_script()函数;同时,在每1次循环中,脚本会休眠1秒钟,避免占用太多CPU资源。

  完成上述代码后,执行这一文件,即可看到已经按照要求,每隔30分钟查询1次体检余量了。

  如果查询过程中,发现指定日期出现了体检名额,那么和本文前述内容一样,程序会自动给我们发邮件,提醒我们可以预约了。这里大家可以结合自己的使用场景与个人需求,修改每次查询的间隔时间;但是建议不要间隔的时间太短,否则可能会被网站的服务器屏蔽。

  当然,还是那句话,本文这里的代码只能自动监控余量并加以邮件提醒,而没有实现发现余量后自动预约。如果大家感兴趣,可以进一步实现自动预约的需求。

  至此,大功告成。

欢迎关注:疯狂学习GIS

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/669863.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot基础及上传组件封装

简介 本文主要以文件上传为demo,介绍了一些 springboot web 开发的入门的技术栈。 对应刚接触 springboot 的可以参考下。 主要包括文件md5比对、生成图片缩略图、数据库迁移、文件记录持久化、请求全局异常处理等功能。 准备工作 在 idea 中创建项目&#xff…

十大排序 —— 快速排序

十大排序 —— 快速排序 快速排序一些坑快速排序的性能优点:缺点:性能优化: 我们今天来看看十大排序中很出名的一个算法——快速排序: 快速排序 快速排序(Quick Sort)是一种经典的、高效的排序算法&#…

centos8stream 编译安装 php-rabbit-mq模块

官方GitHub:https://github.com/php-amqp/php-amqp 环境依赖安装 dnf install cmake make -y 1.安装rabbitmq-c cd /usr/local/src/ wget https://github.com/alanxz/rabbitmq-c/archive/refs/tags/v0.14.0.tar.gz tar xvf v0.14.0.tar.gz cd rabbitmq-c-0.14.0/…

Linux下多线程的相关概念

🤖个人主页:晚风相伴-CSDN博客 💖如果觉得内容对你有帮助的话,还请给博主一键三连(点赞💜、收藏🧡、关注💚)吧 🙏如果内容有误或者有写的不好的地方的话&…

php反序列化入门

一,php面向对象。 1.面向对象: 以“对象”伪中心的编程思想,把要解决的问题分解成对象,简单理解为套用模版,注重结果。 2.面向过程: 以“整体事件”为中心的编程思想,把解决问题的步骤分析出…

实时监控电脑屏幕的软件是什么?三款超受欢迎的电脑监控软件

实时监控电脑屏幕的软件在现代企业管理中扮演着至关重要的角色,它们不仅帮助管理者实时监控员工的工作状态,提高工作效率,还通过数据分析和报告功能,为企业提供了优化管理流程和决策支持的依据。以下将介绍几款市面上广泛使用的实…

Redis过期策略数据淘汰策略

过期策略 一、设置过期时间 redis有四种命令可以用于设置键的生存时间和过期时间: EXPIRE : 将键的生存时间设为 ttl 秒 PEXPIRE :将键的生存时间设为 ttl 毫秒 EXPIREAT :将键的过期时间设为 timestamp 所指定的秒数时间戳 PEXPIREAT : 将键的过期时间设为 times…

GNU Radio创建qt time plot python OOT块

文章目录 前言一、创建自定义的 OOT 块1、安装相应依赖2、创建 OOT 块3、修改相关4、编译及安装 OOT 块 二、测试1、grc 图2、运行结果 三、资源自取 前言 官方提供的绘制时域波形的 block 名字叫做 QT GUI Time Sink,其底层实现是用 C 写的,但是我发现…

virtualbox中ubuntu22.04网络配置

第一:添加两个网卡,网卡1是NAT方式,网卡2是仅主机模式(两个顺序不能颠倒) 第二步:启动ifconfig查看网络

『 Linux 』文件系统

文章目录 磁盘构造磁盘抽象化 磁盘的寻址方式磁盘控制器磁盘数据传输文件系统Inode数据块(Data Blocks)超级块(SuperBlock)块组描述符(Group Descriptor) 磁盘构造 磁盘内部构造由磁头臂,磁头,主轴,盘片,盘面,磁道,柱面,扇区构成; 磁头臂:控制磁头的移动,可以精确地…

Exce 两列一组对齐呈现,缺失补 0

Excel 里有 多 组数据,每组 2 列,每组长度不同。第 1 列是编号,列之间的编号有重复。 ABCDEFGH1Mass10Mass11Mass12Mass132802200581309088146532802225938133306824779282975598142002482273148413154988335698822331305832720485110460842…

go解析yaml

go解析yaml文件关键就是结构体的创建 初学go tag字段要和yaml文件中的key对应起来,每个层级都要创建对应的结构体,有点烦 package configimport ("gopkg.in/yaml.v3""os" )type Config struct {MysqlConfig MysqlConfig yaml:&q…

Spring Boot 开发 -- 过滤器与拦截器详解

引言 在Web开发中,经常需要对请求进行预处理或在响应后进行后处理,Spring Boot提供了过滤器和拦截器两种机制来实现这一需求。虽然它们都可以用来处理HTTP请求和响应,但在使用场景、执行顺序和配置方式上存在明显的差异。本文将详细讲解Spri…

【UML用户指南】-01-UML基本元素的介绍(一)

1、UML的词汇表 (1)事物; (2)关系; (3)图。 事物是对模型中首要成分的抽象;关系把事物结合在一起;图聚集了相关的事物。 注:事物也称为元素 2…

东芝机械人电池低报警解除与机器人多旋转数据清零

今天启动一台设备,触摸屏一直显示机器人报警(翻译过后为电池电量低),更换电池后关机重启后也不能消除,所以打开示教器,下面就来说说怎么解决此项问题(可以参考官方发的手册,已手册为…

携程梁建章:持续投资创新与AI,开启旅游行业未来增长

5月30至31日,携程集团在上海和张家界举办Envision 2024全球合作伙伴大会,邀请超50个国家和地区的1600余名外籍旅游业嘉宾与会,共同探讨中国跨境旅游市场发展机遇,讲好中国故事。 携程国际业务增速迅猛,创新与AI解锁未…

IntelliJ IDEA / Android Studio 方法显示Git提交人

显示方法: 设置 > 编辑器 > 嵌入提示 > Code Vision > 代码作者(勾选) IntelliJ IDEA Android Studio

css-表头筛选的特定样式

背景 饿了么的表头筛选样式比较简单,如图1,产品觉得不够醒目(觉得用户可能不知道这是筛选,我表示不理解) 要求改进筛选的样式,达到图2的效果,主要是状态列,既希望这列的宽度固定&a…

git应用最佳实践

插: AI时代,程序员或多或少要了解些人工智能,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家(前言 – 人工智能教程 ) 坚持不懈,越努力越幸运,大家…

Linux内网中安装jdk1.8详细教程

本章教程,主要介绍如何在内网环境中配置JDK1.8环境变量 一、下载Linux版压缩包 下载地址:https://www.oracle.com/java/technologies/downloads/#java8 下载完成之后,通过XFTP等工具,将安装包上传到内网服务器 二、安装配置步骤 1、解压压缩包 tar -zxvf /usr/local/jdk-…