返回列表 发布新帖

黄金价格消息推送

515 0
小K网牛逼 发表于 3 小时前 | 查看全部 阅读模式 <

马上注册,结交更多好友,享用更多功能,让你轻松玩转小K网。

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

×
一、背景需求
随着这两年黄金价格大涨,大家想实时关注金价但是有时又不能不方便实时查看手机或电脑,需要一个价格提醒的需求,虽然现有很多app提供价格提醒,但是可能我接触的少,发现很多软件都不是自己需要的,所以基于自己想法和需求想弄一个价格提醒系统。此程序适合做短线黄金交易。投资有风险,购买请谨慎!
二、技术实现
PS:作为一个白剽党而言想办法节省或者免费才是王道!哈
服务器:本地运行或云服务器域名:(可选)
开发语言:Python
主力开发:AI
消息推送:微信公众号(可申请微信公众平台的测试号-免费)
整个程序都是基于半吊子全栈开发“AI”训练而来,历经15天左右,因为是业余时间所以比较长哈,现在基本完成也测试了一个月左右,肯定有不尽人意的地方,但是还算满意(声明:本人对于python是大白,所以介意者可忽略本文及代码,自行实现)
三、开发思路整理
补充下一个重要说明,微信公众平台测试号有个坑,就是千万不要设置提醒过于频繁,每天最多100次左右吧,超过了他回缓存,递增给每天的配额,也就是说如果你一次性发1000条消息推送,那未来10天你都看不见新消息了,所以千万谨慎设置。
功能特点
  • 自动从新浪财经抓取实时黄金价格(每5分钟抓取一次)
  • 可配置的价格预警阈值
  • 通过微信公众号模板消息向用户发送价格提醒
  • 支持价格涨跌预警功能
  • 可配置推送间隔时间和推送次数
  • 支持定时推送(每小时的01分和31分)
  • 预警推送与定时推送互不影响(不太理想有bug)
  • 动态更新基准价格(不太理想有bug)
  • 增强的日志记录和监控
  • 简单的缓存机制提高性能
  • 支持缓存清除功能
  • 支持生成HTML文件用于Web预览
  • 支持生成windows桌面软件

https://gitee.com/hejsky/gold-price


黄金价格消息推送

黄金价格消息推送

黄金价格消息推送

黄金价格消息推送

代码access_token.py
  1. import requests
  2. import time
  3. from logger_config import get_logger
  4. from config import APP_ID, APP_SECRET
  5. # 获取日志记录器
  6. logger = get_logger(__name__)
  7. class AccessToken:
  8.     """
  9.     微信公众号 Access Token 管理类
  10.     """
  11.     def __init__(self):
  12.         self.access_token = None
  13.         self.token_expire_time = 0
  14.     def get_access_token(self):
  15.         """
  16.         获取access_token
  17.         :return: access_token或None
  18.         """
  19.         # 检查token是否仍然有效
  20.         if self.access_token and time.time() < self.token_expire_time:
  21.             logger.debug("使用缓存的Access Token")
  22.             return self.access_token
  23.         url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}'.format(
  24.             APP_ID, APP_SECRET)
  25.         headers = {
  26.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
  27.                           'Chrome/91.0.4472.124 Safari/537.36'
  28.         }
  29.         try:
  30.             response = requests.get(url, headers=headers, timeout=10)
  31.             response.raise_for_status()  # 检查HTTP状态码
  32.             result = response.json()
  33.             if 'access_token' in result:
  34.                 self.access_token = result['access_token']
  35.                 # 设置过期时间(提前5分钟刷新)
  36.                 expires_in = result.get('expires_in', 7200)
  37.                 self.token_expire_time = time.time() + expires_in - 300
  38.                 logger.info("成功获取新的Access Token")
  39.                 return self.access_token
  40.             else:
  41.                 logger.error(f"获取Access Token失败: {result}")
  42.                 self._reset_token()
  43.                 return None
  44.         except requests.exceptions.RequestException as e:
  45.             logger.error(f"HTTP请求异常: {e}")
  46.             self._reset_token()
  47.             return None
  48.         except ValueError as e:
  49.             logger.error(f"响应解析异常: {e}")
  50.             self._reset_token()
  51.             return None
  52.         except Exception as e:
  53.             logger.error(f"获取Access Token发生未知异常: {e}")
  54.             self._reset_token()
  55.             return None
  56.             
  57.     def _reset_token(self):
  58.         """
  59.         重置token信息
  60.         """
  61.         self.access_token = None
  62.         self.token_expire_time = 0
  63.     def refresh_access_token(self):
  64.         """
  65.         手动刷新access_token
  66.         """
  67.         # 清除当前token信息
  68.         self.access_token = None
  69.         self.token_expire_time = 0
  70.         self.get_access_token()
复制代码
数据抓取来源data_source.py
  1. # coding: utf-8
  2. """
  3. 黄金价格数据源模块
  4. 负责获取和处理黄金价格数据
  5. """
  6. import time
  7. import requests
  8. from logger_config import get_logger
  9. from playwright.sync_api import sync_playwright
  10. from config import PRICE_CACHE_EXPIRATION
  11. # 获取日志记录器
  12. logger = get_logger(__name__)
  13. # 备选数据源配置
  14. GOLD_PRICE_SOURCES = [
  15.     {
  16.         "name": "新浪财经-黄金频道",
  17.         "url": "https://finance.sina.com.cn/nmetal/",
  18.         "method": "playwright"
  19.     },
  20.     {
  21.         "name": "新浪财经-黄金期货",
  22.         "url": "https://finance.sina.com.cn/futures/quotes/XAU.shtml",
  23.         "method": "playwright"
  24.     },
  25.     {
  26.         "name": "新浪财经-API",
  27.         "url": "https://hq.sinajs.cn/list=njs_gold",
  28.         "method": "api"
  29.     }
  30. ]
  31. # 时间变量
  32. TIMEOUT = 10  # 通用超时时间(秒)
  33. RETRY_COUNT = 3  # 重试次数
  34. RETRY_INTERVAL = 2  # 重试间隔(秒)
  35. # 缓存机制
  36. class PriceCache:
  37.     """
  38.     价格缓存类,管理价格数据的缓存
  39.     用于减少重复的网络请求,提高性能
  40.     """
  41.     def __init__(self, expiration=PRICE_CACHE_EXPIRATION):
  42.         self._cache = {}  # 缓存字典
  43.         self._expiration = expiration  # 缓存过期时间(秒)
  44.          
  45.     def get(self, key):
  46.         """
  47.         获取缓存数据,如果过期则返回None
  48.         :param key: 缓存键
  49.         :return: 缓存值或None
  50.         """
  51.         if key in self._cache:
  52.             cached_data = self._cache[key]
  53.             if time.time() < cached_data['expires_at']:
  54.                 return cached_data['value']
  55.             else:
  56.                 # 缓存已过期,删除它
  57.                 del self._cache[key]
  58.         return None
  59.          
  60.     def set(self, key, value):
  61.         """
  62.         设置缓存数据
  63.         :param key: 缓存键
  64.         :param value: 缓存值
  65.         """
  66.         self._cache[key] = {
  67.             'value': value,
  68.             'expires_at': time.time() + self._expiration
  69.         }
  70.          
  71.     def clear(self):
  72.         """
  73.         清除所有缓存数据
  74.         """
  75.         cache_size = len(self._cache)
  76.         self._cache.clear()
  77.         logger.info(f"缓存已全部清除,共清除 {cache_size} 条记录")
  78.          
  79.     def clear_key(self, key):
  80.         """
  81.         清除指定键的缓存数据
  82.         :param key: 缓存键
  83.         """
  84.         if key in self._cache:
  85.             del self._cache[key]
  86.             logger.info(f"已清除缓存键: {key}")
  87.             return True
  88.         return False
  89.          
  90. # 创建价格缓存实例
  91. price_cache = PriceCache()  # 使用配置文件中的缓存过期时间
  92. def get_gold_price_from_sina_page_playwright(url):
  93.     """
  94.     使用 Playwright 从新浪财经页面获取黄金价格
  95.     :param url: 数据源URL
  96.     :return: 价格数据字典或None
  97.     """
  98.     browser = None
  99.     page = None
  100.     try:
  101.         with sync_playwright() as p:
  102.             # 启动浏览器 (增加超时时间)
  103.             browser = p.chromium.launch(headless=True, timeout=15000)
  104.             page = browser.new_page()
  105.             # 设置用户代理
  106.             page.set_extra_http_headers({
  107.                 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
  108.                               "Chrome/120.0.0.0 Safari/537.36"
  109.             })
  110.             # 访问页面 (增加超时时间)
  111.             page.goto(url, timeout=20000)
  112.             # 等待元素出现 (增加超时时间)
  113.             page.wait_for_selector("#realtimeGC", timeout=20000)
  114.             # 获取价格链接元素,通过CSS选择器精确定位
  115.             price_link_element = page.locator("#realtimeGC > .r_g_price_c_r > a")
  116.             # 获取人民币价格
  117.             price_text = price_link_element.locator(".r_g_price_now").text_content(timeout=10000)
  118.             # 获取价格变化
  119.             price_change_text = price_link_element.locator(".r_g_price_change").text_content(timeout=10000)
  120.             # 解析价格文本
  121.             if price_text:
  122.                 # 移除可能的空格和换行符
  123.                 price_text = price_text.strip()
  124.                 try:
  125.                     price = float(price_text)
  126.                     if price <= 0:
  127.                         logger.error("Playwright获取页面价格数据异常,获取到的价格为0或负数")
  128.                         return None
  129.                     # 返回包含所有价格相关信息的对象
  130.                     result = {
  131.                         "price": price,
  132.                         "change": price_change_text.strip() if price_change_text else "",
  133.                         "timestamp": int(time.time()),
  134.                         "readable_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
  135.                     }
  136.                     return result
  137.                 except ValueError as e:
  138.                     logger.error(f"价格文本转换为数字失败: {e}, 原始文本: {price_text}")
  139.                     return None
  140.             else:
  141.                 logger.error("无法解析页面价格数据(Playwright方式),未找到有效价格文本")
  142.                 return None
  143.     except Exception as e:
  144.         logger.error(f"使用Playwright抓取页面失败: {e}")
  145.         return None
  146.     finally:
  147.         # 确保浏览器和页面总是被关闭
  148.         # 注意:在 with sync_playwright() 上下文内,Playwright 会自动关闭资源
  149.         # 这里的关闭操作可能会因为事件循环已关闭而失败,所以添加 try-except
  150.         if page:
  151.             try:
  152.                 page.close()
  153.             except Exception as e:
  154.                 logger.debug(f"关闭页面失败(可能是因为事件循环已关闭): {e}")
  155.         if browser:
  156.             try:
  157.                 browser.close()
  158.             except Exception as e:
  159.                 logger.debug(f"关闭浏览器失败(可能是因为事件循环已关闭): {e}")
  160. def get_gold_price_from_api(url):
  161.     """
  162.     使用API方式获取黄金价格(更轻量级,速度更快)
  163.     :param url: API数据源URL
  164.     :return: 价格数据字典或None
  165.     """
  166.     try:
  167.         # 发送HTTP请求获取数据
  168.         response = requests.get(url, timeout=TIMEOUT)
  169.         response.raise_for_status()  # 检查HTTP状态码
  170.          
  171.         # 解析新浪财经API返回的数据
  172.         # 新浪财经API返回格式:var hq_str_njs_gold="黄金T+D,453.50,453.50,453.50,453.50,0.00,0.00,09:29:59,2023-12-01";
  173.         data = response.text.strip()
  174.         if data and "=" in data and """ in data:
  175.             # 提取引号内的数据部分
  176.             data_part = data.split("=")[1].strip().strip(""")
  177.             # 分割数据字段
  178.             fields = data_part.split(",")
  179.             if len(fields) >= 8:
  180.                 # 新浪财经API数据格式:产品名称,最新价,开盘价,最高价,最低价,涨跌额,涨跌幅,时间,日期
  181.                 price_str = fields[1].strip()
  182.                 try:
  183.                     price = float(price_str)
  184.                     if price <= 0:
  185.                         logger.error(f"API获取价格数据异常,获取到的价格为0或负数: {price}")
  186.                         return None
  187.                      
  188.                     # 返回包含所有价格相关信息的对象
  189.                     result = {
  190.                         "price": price,
  191.                         "change": fields[5].strip() if len(fields) > 5 else "",
  192.                         "timestamp": int(time.time()),
  193.                         "readable_time": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
  194.                     }
  195.                     return result
  196.                 except ValueError as e:
  197.                     logger.error(f"API价格文本转换为数字失败: {e}, 原始文本: {price_str}")
  198.                     return None
  199.             else:
  200.                 logger.error(f"API返回数据格式异常,字段数量不足: {fields}")
  201.                 return None
  202.         else:
  203.             logger.error(f"API返回数据格式异常,无法解析: {data}")
  204.             return None
  205.     except Exception as e:
  206.         logger.error(f"使用API获取数据失败: {e}")
  207.         return None
  208. def get_gold_price():
  209.     """
  210.     获取黄金价格的主函数,尝试多个数据源
  211.     使用缓存机制以提高性能
  212.     :return: 黄金价格(元/克)或None
  213.     """
  214.     # 检查缓存
  215.     cached_price = price_cache.get('gold_price')
  216.     if cached_price is not None:
  217.         logger.info("使用缓存的金价数据")
  218.         return cached_price
  219.      
  220.     # 遍历所有备选数据源
  221.     for source in GOLD_PRICE_SOURCES:
  222.         source_name = source["name"]
  223.         source_url = source["url"]
  224.         source_method = source["method"]
  225.          
  226.         logger.info(f"尝试从 {source_name} 获取黄金价格")
  227.          
  228.         # 根据数据源类型选择不同的获取方式
  229.         for retry in range(RETRY_COUNT):
  230.             try:
  231.                 if source_method == "api":
  232.                     # 使用API方式获取数据(更轻量级)
  233.                     price_data = get_gold_price_from_api(source_url)
  234.                 else:
  235.                     # 使用Playwright方式获取数据
  236.                     price_data = get_gold_price_from_sina_page_playwright(source_url)
  237.                  
  238.                 if price_data is not None:
  239.                     # 更新缓存
  240.                     price_cache.set('gold_price', price_data["price"])
  241.                     logger.info(f"成功获取黄金价格: &#165;{price_data['price']}/克")
  242.                     return price_data["price"]
  243.                 else:
  244.                     logger.warning(f"从 {source_name} 获取数据失败,第 {retry + 1} 次尝试")
  245.                     if retry < RETRY_COUNT - 1:
  246.                         time.sleep(RETRY_INTERVAL)
  247.             except Exception as e:
  248.                 logger.error(f"从 {source_name} 获取数据时发生异常: {e}, 第 {retry + 1} 次尝试")
  249.                 if retry < RETRY_COUNT - 1:
  250.                     time.sleep(RETRY_INTERVAL)
  251.     # 所有方案都失败
  252.     logger.error("所有数据源获取失败,无法获取黄金价格")
  253.     return None
  254. def display_price_info(price, last_price=None):
  255.     """
  256.     显示价格信息和涨跌情况
  257.     """
  258.     # 获取价格涨跌箭头
  259.     arrow, direction = get_price_arrow(price, last_price)
  260.     # 获取当前时间
  261.     time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
  262.     return arrow, direction
  263. def get_price_arrow(price, last_price):
  264.     """
  265.     根据当前价格与基准价格比较,判断价格上涨或下跌
  266.     返回相应的箭头符号和描述文字
  267.     """
  268.     # 如果没有上一次价格记录,则认为是持平
  269.     if last_price is None:
  270.         return "(持平)", "持平"
  271.     # 判断涨跌
  272.     if price > last_price:
  273.         return "(上涨)", "上涨"
  274.     elif price < last_price:
  275.         return "(下跌)", "下跌"
  276.     else:
  277.         return "(持平)", "持平"
复制代码
主程序main.py
  1. import time
  2. from datetime import datetime, time as dt_time
  3. # 导入配置文件
  4. from config import PUSH_START_TIME, PUSH_END_TIME, REGULAR_PUSH_MINUTES, REGULAR_PUSH_WINDOW, DATA_FETCH_INTERVAL
  5. # 延迟导入TEST_MODE,需要时重新导入
  6. import importlib
  7. import config
  8. # 导入数据获取和处理模块
  9. from data_source import get_gold_price, display_price_info
  10. # 导入黄金预警管理器
  11. from gold_alert import gold_alert_manager
  12. # 导入消息发送模块
  13. from message import MessageSender
  14. # 导入日志配置
  15. from logger_config import get_logger
  16. # 导入工具函数
  17. from utils import build_message_data, send_push_message, is_push_blocked
  18. # 获取日志记录器
  19. logger = get_logger(__name__)
  20. # 全局变量
  21. last_price = None  # 记录上一次的价格,用于比较价格变化
  22. first_run = True  # 标记是否为首次运行
  23. # 消息发送实例 - 延迟初始化,在run_gold_price_monitor函数中创建
  24. message_sender = None
  25. def is_within_push_time():
  26.     """
  27.     检查当前时间是否在推送时间范围内(工作日)
  28.     :return: bool 是否在推送时间范围内
  29.     """
  30.     # 获取当前日期和时间
  31.     current_datetime = datetime.now()
  32.     now = current_datetime.time()
  33.     weekday = current_datetime.weekday()  # 获取星期几,0=周一,4=周五,5=周六,6=周日
  34.      
  35.     # 检查是否为工作日(周一到周五)
  36.     if weekday > 4:  # 周六或周日
  37.         logger.debug(f"当前为非工作日({weekday}),跳过推送")
  38.         return False
  39.      
  40.     # 如果是测试模式,记录日志但不忽略时间限制
  41.     if config.TEST_MODE:
  42.         logger.info("[测试模式] 按照正常时间范围进行推送")
  43.      
  44.     start_time = dt_time.fromisoformat(PUSH_START_TIME)
  45.     end_time = dt_time.fromisoformat(PUSH_END_TIME)
  46.      
  47.     # 处理跨日期的情况(例如:22:00 到 06:00)
  48.     if start_time <= end_time:
  49.         # 不跨日期,正常区间判断
  50.         return start_time <= now <= end_time
  51.     else:
  52.         # 跨日期,例如 22:00 到 06:00
  53.         return now >= start_time or now <= end_time
  54. def is_push_minute():
  55.     """
  56.     检查当前时间是否应该进行定期推送
  57.     逻辑:
  58.     1. 检查当前时间是否在配置的推送分钟点的时间窗口内
  59.     2. 确保每个推送周期只推送一次
  60.     :return: bool 是否为推送分钟
  61.     """
  62.     now = datetime.now()
  63.     current_hour = now.hour
  64.     current_minute = now.minute
  65.      
  66.     # 获取上次推送的状态
  67.     last_push_status = gold_alert_manager.push_status_manager.last_regular_push_minute
  68.      
  69.     # 解析上次推送的小时和分钟
  70.     if last_push_status > 100:
  71.         last_push_hour = last_push_status // 100
  72.         last_push_min = last_push_status % 100
  73.     else:
  74.         # 兼容旧格式
  75.         last_push_hour = -1
  76.         last_push_min = last_push_status
  77.      
  78.     # 检查当前推送周期是否已经推送过
  79.     # 推送周期是指:当前小时的当前分钟(测试模式下)或配置的推送分钟点(正式模式下)
  80.     if config.TEST_MODE:
  81.         # 测试模式下,每个小时只推送一次
  82.         current_push_cycle = current_hour
  83.         last_push_cycle = last_push_hour
  84.         logger.info("[测试模式] 忽略推送分钟限制,允许随时推送,但每个小时只推送一次")
  85.     else:
  86.         # 正式模式下,检查当前时间是否在配置的推送分钟点的时间窗口内
  87.         # 计算当前应该推送的分钟点
  88.         should_push = False
  89.         target_minute = None
  90.          
  91.         for push_min in REGULAR_PUSH_MINUTES:
  92.             # 计算推送窗口的开始和结束时间
  93.             window_start = push_min
  94.             window_end = push_min + REGULAR_PUSH_WINDOW
  95.             
  96.             # 检查当前分钟是否在推送窗口内
  97.             if window_start <= current_minute <= window_end:
  98.                 should_push = True
  99.                 target_minute = push_min
  100.                 break
  101.          
  102.         if not should_push:
  103.             return False
  104.          
  105.         # 正式模式下,推送周期是当前小时的当前推送分钟点
  106.         current_push_cycle = current_hour * 100 + target_minute
  107.         last_push_cycle = last_push_hour * 100 + last_push_min if last_push_hour != -1 else -1
  108.      
  109.     # 如果当前推送周期已经推送过,直接返回False
  110.     if current_push_cycle == last_push_cycle:
  111.         logger.debug(f"当前推送周期 {current_push_cycle} 已经推送过,跳过本次推送")
  112.         return False
  113.      
  114.     # 当前时间符合推送条件,允许推送
  115.     return True
  116. def send_regular_price_update(price, arrow):
  117.     """
  118.     发送定期价格更新消息
  119.     :param price: 当前价格
  120.     :param arrow: 价格变化箭头
  121.     """
  122.     # 使用工具函数构建消息数据
  123.     message_data = build_message_data(price, arrow, gold_alert_manager, push_type="regular")
  124.      
  125.     # 使用通用推送函数发送消息
  126.     result = send_push_message(message_sender, message_data, push_type="regular")
  127.     return result['success']
  128. def run_gold_price_monitor():
  129.     """
  130.     运行黄金价格监控循环
  131.     根据需求调整逻辑:
  132.     1. 每5分钟抓取一次黄金价格数据
  133.     2. 在指定时间点(每小时01分和31分)进行定期推送
  134.     3. 当价格达到预警阈值时立即推送(不影响定时推送规则)
  135.     4. 生成HTML文件用于Web预览(根据配置)
  136.     5. 编译Windows运行文件(根据配置)
  137.     """
  138.     # 声明全局变量
  139.     global last_price, message_sender, first_run
  140.      
  141.     # 初始化消息发送实例
  142.     if message_sender is None:
  143.         message_sender = MessageSender()
  144.      
  145.     # 编译Windows运行文件(根据配置,只执行一次)
  146.     import config
  147.     if config.GENERATE_TYPE == 2:
  148.         logger.info("开始编译Windows运行文件...")
  149.         from windows_compile import WindowsCompiler
  150.         compiler = WindowsCompiler()
  151.         if compiler.compile(onefile=True, console=True):
  152.             logger.info("Windows运行文件编译成功")
  153.         else:
  154.             logger.error("Windows运行文件编译失败")
  155.             logger.error("编译失败,程序将停止运行")
  156.             return  # 编译失败,停止程序运行
  157.      
  158.     # 主循环
  159.     while True:
  160.         try:
  161.             # 检查是否被禁止推送
  162.             if is_push_blocked():
  163.                 logger.error("当天已被禁止推送,程序将停止运行")
  164.                 return  # 被禁止推送,停止程序运行
  165.             
  166.             # 1. 获取当前金价
  167.             price = get_gold_price()
  168.             if price is not None:
  169.                 # 2. 显示价格信息并获取价格变化趋势
  170.                 arrow, direction = display_price_info(price, last_price)
  171.                  
  172.                 # 3. 记录当前价格用于下次比较
  173.                 last_price = price
  174.                  
  175.                 # 4. 获取当前时间信息
  176.                 now = datetime.now()
  177.                 current_minute = now.minute
  178.                  
  179.                 # 5. 保存原始的dynamic_base_price,用于HTML生成
  180.                 # 这样可以在预警触发时正确显示预警信息
  181.                 original_base_price = gold_alert_manager.dynamic_base_price
  182.                  
  183.                 # 6. 检查预警条件(立即推送)
  184.                 # 这会更新dynamic_base_price
  185.                 within_push_time = is_within_push_time()
  186.                 if not is_push_blocked() and within_push_time:
  187.                     alert_result = gold_alert_manager.check_alert_conditions(price)
  188.                  
  189.                 # 7. 生成HTML文件(根据配置,不受时间范围和微信推送逻辑影响)
  190.                 import config
  191.                 if config.GENERATE_TYPE == 1:
  192.                     from generate_html import HTMLGenerator
  193.                     html_generator = HTMLGenerator()
  194.                      
  195.                     # 生成HTML时使用原始的base_price,这样可以显示预警信息
  196.                     html_data = {
  197.                         'current_price': price,
  198.                         'last_price': last_price,
  199.                         'base_price': original_base_price
  200.                     }
  201.                     html_path = html_generator.generate_html(html_data)
  202.                     if html_path:
  203.                         logger.info(f"成功生成HTML文件: {html_path}")
  204.                     else:
  205.                         logger.error("生成HTML文件失败")
  206.                         logger.error("HTML生成失败,程序将停止运行")
  207.                         return  # HTML生成失败,停止程序运行
  208.                 # 8. 检查是否需要进行定期推送
  209.                 if within_push_time:
  210.                     # 检查当前是否为推送分钟
  211.                     if is_push_minute():
  212.                         # 发送定期价格更新
  213.                         if send_regular_price_update(price, arrow):
  214.                             # 更新推送状态
  215.                             gold_alert_manager.push_status_manager.reset_regular_push_status(current_minute, price)
  216.                             logger.info(f"定期推送完成,重置推送状态: 分钟={current_minute}")
  217.                         else:
  218.                             # 如果定期推送失败,可能表示推送服务出现问题
  219.                             logger.warning("定期推送失败,检查是否被禁止推送")
  220.                             if is_push_blocked():
  221.                                 logger.error("当天已被禁止推送,程序将停止运行")
  222.                                 return  # 被禁止推送,停止程序运行
  223.                  
  224.                 # 9. 每5分钟抓取一次数据
  225.                 time.sleep(DATA_FETCH_INTERVAL)
  226.             else:
  227.                 # 10. 无法获取金价数据时的处理
  228.                 logger.error(f"无法获取金价数据,程序将停止运行")
  229.                 return  # 无法获取价格数据,停止程序运行
  230.         except KeyboardInterrupt:
  231.             # 9. 处理用户中断
  232.             logger.info("程序已退出")
  233.             break
  234.         except Exception as e:
  235.             # 10. 处理其他异常
  236.             logger.error(f"程序运行异常: {e}", exc_info=True)  # 记录完整的异常信息
  237.             # 检查是否被禁止推送
  238.             if is_push_blocked():
  239.                 logger.error("当天已被禁止推送,程序将停止运行")
  240.                 return
  241.             time.sleep(10)  # 短暂休眠后重试
  242. if __name__ == "__main__":
  243.     run_gold_price_monitor()
复制代码

整套程序下载地址:https://xiaok.lanzoum.com/inBqx3he2nwh(含有部署到宝塔教程!)

回复

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

您需要 登录 后才可以回复,轻松玩转社区,没有帐号?立即注册
快速回复
关灯 在本版发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表