Python 自动化监控网站预约名额实现方案
介绍使用 Python 结合 Selenium 自动化技术监控特定网站预约名额的方法。通过分析页面 DOM 结构,定位关键属性值,利用无头浏览器模拟用户操作进行轮询。当检测到可用名额时,通过 SMTP 协议发送邮件通知用户。文章详细阐述了环境搭建、浏览器配置、元素定位、数据提取及邮件发送等核心步骤,并提供了完整的代码示例和反爬优化建议,适用于各类需要实时监测网页状态变化的场景。

介绍使用 Python 结合 Selenium 自动化技术监控特定网站预约名额的方法。通过分析页面 DOM 结构,定位关键属性值,利用无头浏览器模拟用户操作进行轮询。当检测到可用名额时,通过 SMTP 协议发送邮件通知用户。文章详细阐述了环境搭建、浏览器配置、元素定位、数据提取及邮件发送等核心步骤,并提供了完整的代码示例和反爬优化建议,适用于各类需要实时监测网页状态变化的场景。

在抢票、预约签证或购买限量商品等场景中,用户往往需要实时监控特定页面的状态变化。当页面出现可用名额时,系统需立即通知用户。传统的刷新查看方式效率低下且容易错过时机,因此利用 Python 编写自动化脚本进行轮询监控成为了一种高效的解决方案。
本文将以一个具体的案例为例,演示如何使用 Selenium 自动化技术结合邮件通知功能,实现对目标网站预约状态的实时监控。
首先需要对目标网页的 DOM 结构进行分析。通过浏览器开发者工具(F12)查看元素属性,确定判断'有名额'的关键特征。
在本案例中,关键逻辑在于检测表格单元格(td)的 title 属性。当该属性值为 "Available" 时,表示该时间段有可预约的位置;若为 "Not Available" 或 "Slots Full",则表示不可用。

smtplib 配合 email 模块,实现检测到名额后自动发送邮件提醒。lxml 库用于快速提取 HTML 中的 XPath 信息。在开始编写代码前,请确保已安装以下依赖库:
pip install selenium lxml webdriver-manager
同时,需要下载与本地 Chrome 版本匹配的 chromedriver,或者使用 webdriver-manager 自动管理驱动。
为了减少浏览器指纹特征,我们需要对 Chrome 选项进行特殊配置。包括禁用自动化扩展、添加伪装参数以及开启无头模式。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
def init_driver():
options = Options()
# 隐藏"Chrome 正在受到自动软件的控制"
options.add_experimental_option('useAutomationExtension', False)
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_argument('--disable-blink-features=AutomationControlled')
# 配置无头浏览器,不显示 GUI 界面
options.add_argument('--headless')
# 设置 User-Agent,模拟真实用户
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...')
driver = webdriver.Chrome(options=options)
driver.maximize_window()
return driver
脚本启动后,首先需要访问目标 URL。由于页面可能存在多层跳转或 iframe 嵌套,需要使用显式等待(WebDriverWait)来确保元素加载完成。
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC
import time
def start_requests(driver):
url = 'https://brazil.blscn.cn/chinese/book_appointment.php'
driver.get(url)
time.sleep(3) # 初始加载缓冲
# 等待并点击确认按钮
try:
element = WebDriverWait(driver, 20).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "#IDBodyPanel .secondry-btn"))
)
if element.is_displayed():
element.click()
except Exception:
print("未找到确认按钮")
return
# 处理 iframe 跳转
try:
iframe = driver.find_element(By.CSS_SELECTOR, '.wrap iframe')
driver.switch_to.frame(iframe)
except Exception:
pass
这是监控的核心部分。脚本需要遍历下拉框选项,选择城市,然后检查日期表格中的 title 属性。
from lxml import etree
def check_availability(driver, email_address):
# 等待位置 ID 加载
element = WebDriverWait(driver, 30).until(
EC.presence_of_element_located((By.ID, 'valCenterLocationId'))
)
select_box = driver.find_element(By.ID, 'valCenterLocationId')
select_option = Select(select_box)
options_list = select_option.options
for index, option in enumerate(options_list[1:], start=1): # 跳过第一个默认选项
try:
# 重新定位以防页面刷新
select_box = driver.find_element(By.ID, 'valCenterLocationId')
Select(select_box).select_by_index(index)
time.sleep(1)
# 获取当前选中的城市名称
selected_value = driver.find_element(By.CSS_SELECTOR, f'#valCenterLocationId > option:nth-child({index+1})').text
# 定位第二个下拉框并选择
select_box2 = driver.find_element(By.ID, 'valAppointmentForMembers')
Select(select_box2).select_by_index(1)
time.sleep(1)
# 点击日期弹出框
driver.find_element(By.ID, 'valAppointmentDate').click()
time.sleep(1)
# 获取页面源码
html = driver.page_source
tree = etree.HTML(html)
# 提取所有 td 标签
td_list = tree.xpath('//table[@class="table-condensed"]/tbody/tr/td')
available_slots = []
for td in td_list:
title = td.xpath('./@title')
if title and title[0] == 'Available':
available_slots.append(selected_value)
if available_slots:
send_email_notification(email_address, available_slots)
print(f'发现名额:{available_slots}')
return True
else:
print(f'{selected_value} 暂无名额')
time.sleep(3)
except Exception as e:
print(f'处理 {selected_value} 时出错:{e}')
continue
return False
为了实现即时通知,需要配置 SMTP 服务。这里以 QQ 邮箱为例,需要在邮箱设置中开启 SMTP 服务并获取授权码。
import smtplib
from email.mime.text import MIMEText
from email.header import Header
class SendEmail:
def __init__(self, to_addr, content, auth_code):
self.to_addr = to_addr
self.content = content
self.auth_code = auth_code
self.from_addr = '[email protected]'
self.password = auth_code
self.smtp_server = 'smtp.qq.com'
self.port = 587
def sed_email(self):
msg = MIMEText(self.content, 'plain', 'utf-8')
msg['From'] = Header('Monitor System', 'utf-8')
msg['To'] = Header(self.to_addr, 'utf-8')
msg['Subject'] = Header('预约名额监控报警', 'utf-8')
try:
server = smtplib.SMTP_SSL(self.smtp_server, self.port)
server.login(self.from_addr, self.password)
server.sendmail(self.from_addr, [self.to_addr], msg.as_string())
server.quit()
print('邮件发送成功')
except Exception as e:
print(f'邮件发送失败:{e}')
def send_email_notification(addr, slots):
text_content = f"监测到预约位置:{slots}"
me = SendEmail(addr, text_content, '你的邮箱授权码')
me.sed_email()
将上述模块整合,构建主循环。程序将持续运行,直到检测到名额为止。
if __name__ == "__main__":
driver = init_driver()
target_email = '[email protected]'
count = 0
print('开始监听预约位置,请稍等!!!')
while True:
try:
if check_availability(driver, target_email):
break
else:
count += 1
print(f'第 {count} 次尝试,无名额')
time.sleep(60) # 增加间隔时间,避免被封禁
except Exception as e:
print(f'发生错误:{e}')
time.sleep(60)
driver.quit()
print('程序结束')
如果目标网站开启了严格的反爬机制,频繁的请求可能导致 IP 被封禁。建议采取以下措施:
sleep 时间,引入随机变量。若页面弹出验证码,纯代码将无法继续。此时需要引入打码平台接口,或暂停脚本等待人工处理。
无头模式虽然节省内存,但在复杂页面渲染下可能不稳定。若条件允许,可保留 GUI 模式以便调试,生产环境再切换回 Headless。
通过 Selenium 自动化结合邮件通知,我们可以构建一个轻量级的监控工具。本方案重点解决了动态页面数据抓取和实时通知的问题。在实际应用中,还需根据具体业务场景调整选择器策略和反爬策略,以确保监控的稳定性和持久性。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online