本文介绍基于Python 语言,自动、定时监测某体检预约网站 中指定日期 的体检余额 ,并在有体检余额 时自动给自己发送邮件提醒的方法。
来到春招末期,很多单位进入了体检 流程。其中,银行(尤其是四大行)喜欢"海检 ",即差额体检 ------收到体检不代表录取 的那种;而正巧我春招主要报名的就是银行、国企等,所以单是这周就有4
家单位发了体检通知。
其中,有的单位需要自费体检 。在这周4
家单位中,交通银行就需自费(我报的还是总行呢)------关于银行的体检,大家可以参考文章建信金融科技软开工程师24届Offer面经(https://blog.csdn.net/zhebushibiaoshifu/article/details/135352867)。**贫穷的我** 自然想到了直接用我们研究所发的免费体检 来充当单位体检 了------所里每年上半年会提供1
次免费体检的机会,往年我都是第一时间就去体检;但今年就是想着可以将其充当入职体检 ,所以迟迟没有去。因此,这次看到交行的自费体检,就想着预约所里给的这个免费体检------等的就是这一天,能省则省。但是,当我打开所里体检的预约网页后,发现我1
个堂堂男学生,竟然最近几天已经没有体检名额了,如下图所示。
但是,交行给出的体检报告提交截止时间 也比较近,所以必须要预约到上图中最近几天 的体检;因此,选择用Python 监控这几天的体检名额,且若发现最近几天有名额,就给我自己发邮件。当然,更好、更完整的操作是,只要发现最近几天有名额,就直接由程序自动实现预约 ------但一方面,这个需求若想实现,还得进一步研究后续提交预约时的HTTP 请求信息,我太菜了,这个对我而言可能比较麻烦;另一方面,在准备开始写脚本时正好收到了中国星网的体检,他们是无需自费且提供体检报告的那种体检,所以可以直接把星网的体检报告发给交行,相当于暂时又用不到所里的体检了。所以我后来也就没再继续探究上述这个更进一步的需求的实现方法------当然,后面如果有机会,可以再进一步研究一下。
知道了需求,即可开始尝试实现。首先,虽然所里体检的预约流程主要是在手机上进行的,但它也还是一个普通的网页 ,而非小程序 ;较之小程序 ,网页 实现起来或许会更容易一些------当然,我也没做过小程序的爬虫,也并不知道小程序的话到底是会更复杂还是更容易;但网页的话,至少流程、思路方面还是比较清晰的。
我们先查看上图中所示网页的HTML ,也就是能显示哪一天是否可预约体检 的那个网页的HTML ,如下图所示。可以看到,这个网页的HTML (下图)并不含有每一天的预约余额信息(上图),也就是说这些预约余额信息应该是JavaScript动态加载的。
接下来,我们按下F12
键进入浏览器的开发者工具 ,并选择"Network ";随后,重新刷新当前网页,并观察出现的每一个元素;如下图所示。
通过观察,找到了具有每天体检余额信息 的那1
个元素,如下图所示。
随后,查看这个元素的信息,如下图所示。可以看到,这是1
个POST 请求,具体的请求Headers也是比较常规的。
这里提一句,我一开始看到这个POST 请求的Headers 具有伪头部 (也就是上图中右下角紫色框内,排在前4
个的、Key 前带有冒号:
的头部),说明其是HTTP/2 的请求;所以当时还以为不能用Python 的requests
库了,然后hyper
库又不维护了,所以已经准备用HTTPX
库来实现需求了。但是后来发现,requests
库还是可以正常发送请求并获取回复,所以最后还是选择用更为方便的requests
库了。这里我也不太清楚requests
库不支持HTTP/2这句话是如何界定的。
接下来,我们首先撰写Python访问网页并获取体检名额余额的代码;这一部分代码具体如下。
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
个人可以注册多个域名,不同域名的邮箱之间就可以互发邮件)------其中一个作为发件地址 ,另一个作为收件地址。
我们如果此时执行上述代码,即可看到指定日期内体检名额的剩余情况。其中,如下图所示,某次执行后,发现06
月03
日这一天有体检的名额了(原本写代码时06
月03
日还是没有名额的,这说明有1
位同学预约了这一天但是现在取消了)。
那么,邮箱中就会收到如下图所示的提醒邮件。如果大家开启了类似微信的邮件实时提醒 ,或手机端邮箱APP 的应用消息通知,那么相当于就是实时接收体检余额的提醒了。
为了方便,我们也将体检预约的网页链接放在了邮件中,如下图所示;点击后即可进入预约。
进入预约网页内,发现果然06
月03
日这一天,当前是可以预约的。如果有需要的话,此时就可以手动预约了。
当然,上述我们是单次执行 程序。如果希望可以在后台定时 对体检余额加以检测(例如我想每隔30
分钟就自动查询1
次),则可以通过如下代码来实现。
python
# -*- 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
分钟运行1
次run_script()
函数,而这个函数就是用来执行本文前述的那一段监测余量 、发送邮件 的代码------也就是上述代码中的check.py
文件;这里因为我将2
个Python代码文件放在同一路径下了,所以就用了相对路径。
随后,通过手动调用1
次run_script()
函数,使得程序开始时先执行1
次查询,然后再每隔30
分钟查询1
次。代码最后,进入1
个无限循环,并在循环中调用schedule.run_pending()
函数,确保按预定计划运行run_script()
函数;同时,在每1
次循环中,脚本会休眠1
秒钟,避免占用太多CPU资源。
完成上述代码后,执行这一文件,即可看到已经按照要求,每隔30
分钟查询1
次体检余量了。
如果查询过程中,发现指定日期出现了体检名额,那么和本文前述内容一样,程序会自动给我们发邮件,提醒我们可以预约了。这里大家可以结合自己的使用场景与个人需求,修改每次查询的间隔时间;但是建议不要间隔的时间太短,否则可能会被网站的服务器屏蔽。
当然,还是那句话,本文这里的代码只能自动监控余量 并加以邮件提醒 ,而没有实现发现余量后自动预约。如果大家感兴趣,可以进一步实现自动预约的需求。
至此,大功告成。
欢迎关注:疯狂学习GIS