1
This commit is contained in:
213
BUG修复_邮件课程列表不正确.md
Normal file
213
BUG修复_邮件课程列表不正确.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# BUG修复:邮件通知课程列表不正确
|
||||
|
||||
## 问题描述
|
||||
|
||||
**症状:**
|
||||
- 日志显示检测到2门新增课程:
|
||||
- 马克思主义基本原理 (A3505000018)
|
||||
- 软件需求分析与设计 (A0801050220)
|
||||
|
||||
- 但邮件通知显示的是3门**完全不同**的课程:
|
||||
- C++程序设计
|
||||
- 体育(三) 乒乓球
|
||||
- 概率论与数理统计
|
||||
|
||||
## 根本原因
|
||||
|
||||
代码逻辑错误:
|
||||
|
||||
```python
|
||||
# 错误的实现
|
||||
if self.check_grade_changes(grade_info, grade_html):
|
||||
courses_file = self.script_dir / '.last_courses.txt'
|
||||
if courses_file.exists():
|
||||
# ❌ 发送的是当前所有课程的前3门,而不是新增课程
|
||||
self.send_email_notification(current_courses[:3])
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
|
||||
1. `check_grade_changes` 函数内部计算了新增课程列表 `new_courses`
|
||||
2. 但函数只返回 `True/False`,**没有返回新增课程列表**
|
||||
3. 主循环中使用了 `current_courses[:3]`(当前所有课程的前3门)
|
||||
4. 导致邮件中显示的课程与实际新增的课程不一致
|
||||
|
||||
### 举例说明
|
||||
|
||||
**场景:**
|
||||
- 之前有6门课程:A、B、C、D、E、F
|
||||
- 新增2门课程:G、H
|
||||
- 当前总共8门课程:A、B、C、D、E、F、G、H
|
||||
|
||||
**错误行为:**
|
||||
- 日志显示:新增2门(G、H)✓ 正确
|
||||
- 邮件显示:A、B、C(前3门)❌ 错误
|
||||
|
||||
**期望行为:**
|
||||
- 邮件显示:G、H ✓ 正确
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 1. 修改 `check_grade_changes` 函数签名
|
||||
|
||||
```python
|
||||
# 修改前
|
||||
def check_grade_changes(self, current_content: str, current_html: str = None) -> bool:
|
||||
|
||||
# 修改后
|
||||
def check_grade_changes(self, current_content: str, current_html: str = None) -> tuple:
|
||||
"""检查成绩是否有变化
|
||||
|
||||
Returns:
|
||||
tuple: (是否有变化: bool, 新增课程列表: list)
|
||||
"""
|
||||
```
|
||||
|
||||
### 2. 返回新增课程列表
|
||||
|
||||
```python
|
||||
# 修改前
|
||||
if new_courses:
|
||||
# ... 记录日志 ...
|
||||
return True
|
||||
|
||||
# 修改后
|
||||
if new_courses:
|
||||
# ... 记录日志 ...
|
||||
return (True, new_courses) # 返回元组
|
||||
|
||||
# 其他情况
|
||||
return (False, []) # 无变化
|
||||
return (True, []) # 有变化但无新增课程(成绩更新)
|
||||
```
|
||||
|
||||
### 3. 更新调用代码
|
||||
|
||||
```python
|
||||
# 修改前
|
||||
if self.check_grade_changes(grade_info, grade_html):
|
||||
self.send_email_notification(current_courses[:3]) # ❌ 错误
|
||||
|
||||
# 修改后
|
||||
has_changes, new_courses = self.check_grade_changes(grade_info, grade_html)
|
||||
|
||||
if has_changes:
|
||||
if new_courses:
|
||||
# 有新增课程,发送真正的新增课程列表
|
||||
self.send_email_notification(new_courses) # ✅ 正确
|
||||
else:
|
||||
# 没有新增课程但内容变化(成绩更新),发送通用通知
|
||||
self.send_email_notification()
|
||||
```
|
||||
|
||||
## 修复效果
|
||||
|
||||
### 修复前
|
||||
```
|
||||
日志:
|
||||
[INFO] ✓ 检测到新增课程成绩!共 2 门
|
||||
[INFO] 1. [2025-2026 秋季] 马克思主义基本原理 (A3505000018)
|
||||
[INFO] 2. [2025-2026 秋季] 软件需求分析与设计 (A0801050220)
|
||||
|
||||
邮件:
|
||||
新增课程(共3门):
|
||||
- 2025-2026 秋季 C++程序设计 ❌ 错误!
|
||||
- 2025-2026 秋季 体育(三) 乒乓球 ❌ 错误!
|
||||
- 2025-2026 秋季 概率论与数理统计 ❌ 错误!
|
||||
```
|
||||
|
||||
### 修复后
|
||||
```
|
||||
日志:
|
||||
[INFO] ✓ 检测到新增课程成绩!共 2 门
|
||||
[INFO] 1. [2025-2026 秋季] 马克思主义基本原理 (A3505000018)
|
||||
[INFO] 2. [2025-2026 秋季] 软件需求分析与设计 (A0801050220)
|
||||
|
||||
邮件:
|
||||
新增课程(共2门):
|
||||
- 2025-2026 秋季 马克思主义基本原理 ✅ 正确!
|
||||
- 2025-2026 秋季 软件需求分析与设计 ✅ 正确!
|
||||
```
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 1. 单元测试场景
|
||||
|
||||
**场景1:新增课程**
|
||||
- 之前:3门课程
|
||||
- 现在:5门课程(新增2门)
|
||||
- 期望:邮件显示新增的2门
|
||||
|
||||
**场景2:成绩更新**
|
||||
- 之前:5门课程
|
||||
- 现在:5门课程(某门成绩变化)
|
||||
- 期望:邮件显示通用通知
|
||||
|
||||
**场景3:无变化**
|
||||
- 之前:5门课程
|
||||
- 现在:5门课程(完全相同)
|
||||
- 期望:不发送邮件
|
||||
|
||||
### 2. 集成测试
|
||||
|
||||
```bash
|
||||
# 1. 删除历史记录(模拟首次运行)
|
||||
cd ~/grade_monitor
|
||||
rm .last_*
|
||||
|
||||
# 2. 运行测试
|
||||
python3 monitor.py --test
|
||||
|
||||
# 3. 再次运行(模拟检测到变化)
|
||||
# 手动编辑 .last_courses.txt,删除几行
|
||||
python3 monitor.py --test
|
||||
|
||||
# 4. 检查邮件内容是否正确
|
||||
```
|
||||
|
||||
## 影响评估
|
||||
|
||||
### 向后兼容性
|
||||
- ✅ 完全兼容:函数返回元组,Python 会自动解包
|
||||
- ✅ 不影响测试模式:test_fetch 不调用此函数
|
||||
- ✅ 不影响旧版数据:历史文件格式不变
|
||||
|
||||
### 风险等级
|
||||
- 🟢 **低风险**:仅修改返回值类型
|
||||
- 🟢 所有调用点已更新
|
||||
- 🟢 已通过语法检查
|
||||
|
||||
## 部署步骤
|
||||
|
||||
```bash
|
||||
# 1. 本地打包
|
||||
cd /mnt/e/50425/Documents/Github/GPA_Monitoring
|
||||
./打包.sh
|
||||
|
||||
# 2. 上传到服务器
|
||||
scp gpa_monitor.tar.gz user@server:~/
|
||||
|
||||
# 3. 服务器上更新
|
||||
systemctl stop grade-monitor
|
||||
cd ~/grade_monitor
|
||||
cp monitor.py monitor.py.backup.$(date +%Y%m%d_%H%M%S)
|
||||
tar -xzf ~/gpa_monitor.tar.gz
|
||||
systemctl restart grade-monitor
|
||||
|
||||
# 4. 验证
|
||||
tail -f monitor.log
|
||||
```
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **添加单元测试** - 测试 `check_grade_changes` 函数
|
||||
2. **记录邮件内容** - 在日志中记录发送的课程列表
|
||||
3. **邮件确认机制** - 发送成功后记录一份副本
|
||||
|
||||
---
|
||||
|
||||
**BUG编号:** #001
|
||||
**严重程度:** 高(数据不一致)
|
||||
**修复日期:** 2026-01-21
|
||||
**修复人员:** GitHub Copilot
|
||||
**测试状态:** 待用户验证
|
||||
154
diagnose.sh
Normal file
154
diagnose.sh
Normal file
@@ -0,0 +1,154 @@
|
||||
#!/bin/bash
|
||||
# 快速修复成绩提取失败问题
|
||||
|
||||
echo "========================================="
|
||||
echo "成绩监控系统 - 故障诊断与修复工具"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
cd ~/grade_monitor || cd "$(dirname "$0")"
|
||||
|
||||
# 1. 检查 BeautifulSoup 安装
|
||||
echo "1. 检查依赖库..."
|
||||
if python3 -c "import bs4" 2>/dev/null; then
|
||||
echo " ✓ BeautifulSoup4 已安装"
|
||||
else
|
||||
echo " ✗ BeautifulSoup4 未安装,正在安装..."
|
||||
pip3 install beautifulsoup4
|
||||
fi
|
||||
|
||||
if python3 -c "import requests" 2>/dev/null; then
|
||||
echo " ✓ requests 已安装"
|
||||
else
|
||||
echo " ✗ requests 未安装,正在安装..."
|
||||
pip3 install requests
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 2. 运行测试模式
|
||||
echo "2. 运行诊断测试..."
|
||||
echo " 执行: python3 monitor.py --test --debug"
|
||||
echo "========================================="
|
||||
python3 monitor.py --test --debug 2>&1 | tee test_output.log
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# 3. 检查输出
|
||||
echo "3. 分析测试结果..."
|
||||
|
||||
if grep -q "找到成绩表格" test_output.log; then
|
||||
echo " ✓ 成功找到成绩表格"
|
||||
else
|
||||
echo " ✗ 未找到成绩表格"
|
||||
fi
|
||||
|
||||
if grep -q "共解析到.*门课程" test_output.log; then
|
||||
COURSE_COUNT=$(grep "共解析到.*门课程" test_output.log | tail -1 | grep -oP '\d+')
|
||||
if [ "$COURSE_COUNT" -gt 0 ]; then
|
||||
echo " ✓ 成功解析 $COURSE_COUNT 门课程"
|
||||
else
|
||||
echo " ✗ 解析到 0 门课程"
|
||||
fi
|
||||
else
|
||||
echo " ✗ 无法解析课程信息"
|
||||
fi
|
||||
|
||||
if grep -q "检测到登录页面\|LOGIN_REQUIRED" test_output.log; then
|
||||
echo " ⚠ 检测到登录失败,可能需要检查账号密码"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 4. 检查调试文件
|
||||
echo "4. 检查生成的调试文件..."
|
||||
if [ -f "debug_page.html" ]; then
|
||||
echo " ✓ 发现 debug_page.html"
|
||||
FILE_SIZE=$(stat -f%z "debug_page.html" 2>/dev/null || stat -c%s "debug_page.html" 2>/dev/null)
|
||||
echo " 文件大小: $FILE_SIZE 字节"
|
||||
|
||||
if grep -qi "gridtable\|dataList" debug_page.html; then
|
||||
echo " ✓ HTML 中包含成绩表格标签"
|
||||
else
|
||||
echo " ✗ HTML 中未找到预期的表格标签"
|
||||
echo " 建议: 手动检查 debug_page.html 文件"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f ".last_grade_content.txt" ]; then
|
||||
echo " ✓ 发现 .last_grade_content.txt"
|
||||
LINES=$(wc -l < .last_grade_content.txt)
|
||||
echo " 内容行数: $LINES"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 5. 提供建议
|
||||
echo "5. 修复建议:"
|
||||
echo "========================================="
|
||||
|
||||
if ! python3 -c "import bs4" 2>/dev/null; then
|
||||
echo "❌ 缺少 beautifulsoup4 库"
|
||||
echo " 修复: pip3 install beautifulsoup4"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if grep -q "共解析到 0 门课程" test_output.log; then
|
||||
echo "❌ 无法解析课程信息"
|
||||
echo " 可能原因:"
|
||||
echo " 1. 登录失效 - 检查 config.ini 中的账号密码"
|
||||
echo " 2. 网页结构变化 - 查看 debug_page.html"
|
||||
echo " 3. 网络问题 - 检查是否能访问学校网站"
|
||||
echo " 4. 临时性问题 - 程序会自动重试"
|
||||
echo ""
|
||||
echo " 下一步:"
|
||||
echo " - 查看详细日志: cat test_output.log"
|
||||
echo " - 查看HTML: cat debug_page.html | head -50"
|
||||
echo " - 阅读排查指南: cat 故障排查指南.md"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 检查间歇性错误
|
||||
if [ -f "monitor.log" ]; then
|
||||
FAILED_COUNT=$(grep -c "未找到成绩表格\|共解析到 0 门课程" monitor.log 2>/dev/null || echo "0")
|
||||
SUCCESS_COUNT=$(grep -c "共解析到 [1-9]" monitor.log 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$FAILED_COUNT" -gt 0 ] && [ "$SUCCESS_COUNT" -gt 0 ]; then
|
||||
FAILURE_RATE=$((FAILED_COUNT * 100 / (FAILED_COUNT + SUCCESS_COUNT)))
|
||||
echo "⚠️ 检测到间歇性解析失败"
|
||||
echo " 成功次数: $SUCCESS_COUNT"
|
||||
echo " 失败次数: $FAILED_COUNT"
|
||||
echo " 失败率: ${FAILURE_RATE}%"
|
||||
echo ""
|
||||
if [ "$FAILURE_RATE" -lt 20 ]; then
|
||||
echo " ✓ 失败率较低(<20%),属于正常范围"
|
||||
echo " 原因: 网络波动或服务器临时问题"
|
||||
echo " 处理: 程序已自动重试,无需干预"
|
||||
else
|
||||
echo " ✗ 失败率偏高(>20%),需要检查"
|
||||
echo " 建议: 检查网络连接和登录状态"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
if grep -q "找到.*行成绩数据" test_output.log && grep -q "共解析到.*门课程" test_output.log; then
|
||||
COURSE_COUNT=$(grep "共解析到.*门课程" test_output.log | tail -1 | grep -oP '\d+')
|
||||
if [ "$COURSE_COUNT" -gt 0 ]; then
|
||||
echo "✅ 系统工作正常!"
|
||||
echo " 成功解析了 $COURSE_COUNT 门课程"
|
||||
echo ""
|
||||
echo " 如果服务未运行,启动服务:"
|
||||
echo " systemctl start grade-monitor"
|
||||
echo ""
|
||||
echo " 查看服务状态:"
|
||||
echo " systemctl status grade-monitor"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "测试日志已保存到: test_output.log"
|
||||
echo "如需帮助,请查看: 故障排查指南.md"
|
||||
echo ""
|
||||
242
monitor.py
242
monitor.py
@@ -27,18 +27,31 @@ from typing import Optional, Dict
|
||||
import signal
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='[%(levelname)s] [%(asctime)s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
handlers=[
|
||||
logging.FileHandler('monitor.log', encoding='utf-8'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 避免重复添加处理器
|
||||
if not logger.handlers:
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# 设置日志格式
|
||||
formatter = logging.Formatter(
|
||||
'[%(levelname)s] [%(asctime)s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
|
||||
# 文件处理器
|
||||
file_handler = logging.FileHandler('monitor.log', encoding='utf-8')
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
# 控制台处理器
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(formatter)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
# 防止日志传播到根日志记录器
|
||||
logger.propagate = False
|
||||
|
||||
|
||||
class GradeMonitor:
|
||||
"""成绩监控类"""
|
||||
@@ -291,6 +304,11 @@ class GradeMonitor:
|
||||
|
||||
result = []
|
||||
|
||||
# 检查是否是登录页面
|
||||
if soup.find('input', {'name': 'username'}) or soup.find('input', {'name': 'password'}):
|
||||
logger.error("检测到登录页面,可能需要重新登录")
|
||||
return "LOGIN_REQUIRED"
|
||||
|
||||
# 提取总平均绩点
|
||||
gpa_div = soup.find('div', string=lambda x: x and '总平均绩点' in x)
|
||||
if gpa_div:
|
||||
@@ -299,21 +317,37 @@ class GradeMonitor:
|
||||
result.append(gpa_text)
|
||||
result.append("=" * 60)
|
||||
result.append("")
|
||||
logger.debug(f"找到总平均绩点: {gpa_text}")
|
||||
|
||||
# 提取成绩表格
|
||||
# 尝试多种方式查找成绩表格
|
||||
table = soup.find('table', {'class': 'gridtable'})
|
||||
if not table:
|
||||
table = soup.find('table', {'id': 'dataList'})
|
||||
if not table:
|
||||
# 尝试查找任何包含成绩相关标题的表格
|
||||
for tbl in soup.find_all('table'):
|
||||
headers = tbl.find_all('th')
|
||||
if headers and any('课程名称' in th.get_text() or '成绩' in th.get_text() for th in headers):
|
||||
table = tbl
|
||||
logger.info("通过表头关键词找到成绩表格")
|
||||
break
|
||||
|
||||
if table:
|
||||
logger.debug("找到成绩表格")
|
||||
# 提取表头
|
||||
thead = table.find('thead')
|
||||
if thead:
|
||||
headers = [th.get_text(strip=True) for th in thead.find_all('th')]
|
||||
result.append(' | '.join(headers))
|
||||
result.append("-" * 120)
|
||||
logger.debug(f"表头: {headers}")
|
||||
|
||||
# 提取每一行成绩
|
||||
tbody = table.find('tbody')
|
||||
if tbody:
|
||||
for row in tbody.find_all('tr'):
|
||||
rows = tbody.find_all('tr')
|
||||
logger.debug(f"找到 {len(rows)} 行成绩数据")
|
||||
for row in rows:
|
||||
cells = row.find_all('td')
|
||||
if cells:
|
||||
row_data = []
|
||||
@@ -322,11 +356,28 @@ class GradeMonitor:
|
||||
text = cell.get_text(strip=True, separator=' ')
|
||||
row_data.append(text)
|
||||
result.append(' | '.join(row_data))
|
||||
else:
|
||||
logger.warning("未找到成绩表格")
|
||||
|
||||
if not result:
|
||||
logger.warning("未能提取到成绩信息,返回原始文本")
|
||||
logger.warning("未能提取到成绩信息,页面可能结构异常")
|
||||
# 保存HTML以便调试
|
||||
debug_file = self.script_dir / 'debug_page.html'
|
||||
debug_file.write_text(html, encoding='utf-8')
|
||||
logger.info(f"已保存HTML到 {debug_file} 供调试")
|
||||
|
||||
# 记录HTML摘要用于诊断
|
||||
html_preview = html[:500].replace('\n', ' ')
|
||||
logger.debug(f"HTML前500字符: {html_preview}")
|
||||
|
||||
# 检查是否是网络错误或空页面
|
||||
if len(html) < 100:
|
||||
logger.warning(f"HTML内容过短({len(html)}字节),可能是网络问题")
|
||||
return "NETWORK_ERROR"
|
||||
|
||||
return self._fallback_extract(html)
|
||||
|
||||
logger.info(f"成功提取成绩信息,共 {len(result)} 行")
|
||||
return '\n'.join(result)
|
||||
|
||||
except ImportError:
|
||||
@@ -346,9 +397,27 @@ class GradeMonitor:
|
||||
|
||||
def parse_courses(self, grade_text: str) -> list:
|
||||
"""解析成绩文本,提取课程列表"""
|
||||
# 检查是否需要重新登录
|
||||
if grade_text == "LOGIN_REQUIRED":
|
||||
logger.warning("检测到登录失效,需要重新登录")
|
||||
return []
|
||||
|
||||
# 检查是否是网络错误
|
||||
if grade_text == "NETWORK_ERROR":
|
||||
logger.warning("检测到网络错误,返回空列表")
|
||||
return []
|
||||
|
||||
courses = []
|
||||
lines = grade_text.split('\n')
|
||||
|
||||
# 检查是否是原始HTML(未成功解析的标记)
|
||||
if '<html' in grade_text.lower() or '<body' in grade_text.lower():
|
||||
logger.warning("检测到未解析的HTML内容,成绩提取可能失败")
|
||||
logger.debug(f"文本前100字符: {grade_text[:100]}")
|
||||
return []
|
||||
|
||||
logger.debug(f"开始解析成绩,共 {len(lines)} 行")
|
||||
|
||||
for line in lines:
|
||||
# 跳过标题行、分隔行和空行
|
||||
stripped = line.strip()
|
||||
@@ -367,15 +436,21 @@ class GradeMonitor:
|
||||
if course_code and course_name:
|
||||
course_key = f"{semester}|{course_code}|{course_name}"
|
||||
courses.append(course_key)
|
||||
logger.debug(f"解析到课程: {course_name}")
|
||||
|
||||
logger.info(f"共解析到 {len(courses)} 门课程")
|
||||
return courses
|
||||
|
||||
def calculate_hash(self, content: str) -> str:
|
||||
"""计算内容的哈希值"""
|
||||
return hashlib.md5(content.encode('utf-8')).hexdigest()
|
||||
|
||||
def check_grade_changes(self, current_content: str, current_html: str = None) -> bool:
|
||||
"""检查成绩是否有变化"""
|
||||
def check_grade_changes(self, current_content: str, current_html: str = None) -> tuple:
|
||||
"""检查成绩是否有变化
|
||||
|
||||
Returns:
|
||||
tuple: (是否有变化: bool, 新增课程列表: list)
|
||||
"""
|
||||
# 保存当前内容(供用户查看)
|
||||
self.last_grade_content_file.write_text(current_content, encoding='utf-8')
|
||||
if current_html:
|
||||
@@ -400,7 +475,7 @@ class GradeMonitor:
|
||||
# 计算哈希
|
||||
current_hash = self.calculate_hash(current_content)
|
||||
self.last_grade_file.write_text(current_hash, encoding='utf-8')
|
||||
return False
|
||||
return (False, [])
|
||||
|
||||
# 读取上次的课程列表
|
||||
courses_file = self.script_dir / '.last_courses.txt'
|
||||
@@ -434,7 +509,7 @@ class GradeMonitor:
|
||||
courses_file.write_text('\n'.join(current_courses), encoding='utf-8')
|
||||
self.last_grade_file.write_text(current_hash, encoding='utf-8')
|
||||
|
||||
return True
|
||||
return (True, new_courses)
|
||||
elif content_changed and current_courses:
|
||||
# 课程数量没变,但内容变了(可能是成绩更新)
|
||||
logger.info("=" * 60)
|
||||
@@ -446,10 +521,10 @@ class GradeMonitor:
|
||||
# 更新哈希值
|
||||
self.last_grade_file.write_text(current_hash, encoding='utf-8')
|
||||
|
||||
return True
|
||||
return (True, []) # 内容变化但没有新增课程
|
||||
else:
|
||||
logger.info(f"成绩无变化(共 {len(current_courses)} 门课程)")
|
||||
return False
|
||||
return (False, [])
|
||||
|
||||
def send_email_notification(self, new_courses: list = None) -> bool:
|
||||
"""发送邮件通知"""
|
||||
@@ -641,10 +716,21 @@ class GradeMonitor:
|
||||
consecutive_errors += 1
|
||||
logger.error(f"获取成绩页面失败,等待下次检查 (连续失败: {consecutive_errors}/{max_consecutive_errors})")
|
||||
|
||||
# 连续失败3次尝试重新登录
|
||||
if consecutive_errors >= 3:
|
||||
logger.warning("⚠️ 尝试重新登录以解决页面获取问题...")
|
||||
if self.login():
|
||||
logger.info("✓ 重新登录成功,重置错误计数")
|
||||
consecutive_errors = 0 # 重置错误计数
|
||||
time.sleep(5)
|
||||
continue
|
||||
else:
|
||||
logger.error("✗ 重新登录失败")
|
||||
|
||||
if consecutive_errors >= max_consecutive_errors:
|
||||
error_msg = f"连续 {consecutive_errors} 次获取成绩页面失败"
|
||||
logger.error(error_msg)
|
||||
self.send_error_notification(error_msg)
|
||||
self.send_error_notification(f"{error_msg}\n\n可能需要检查网络或账号状态")
|
||||
consecutive_errors = 0 # 重置计数,避免重复发送
|
||||
|
||||
time.sleep(self.config['check_interval'])
|
||||
@@ -653,15 +739,104 @@ class GradeMonitor:
|
||||
# 提取成绩信息
|
||||
grade_info = self.extract_grade_info(grade_html)
|
||||
|
||||
# 检查变化
|
||||
if self.check_grade_changes(grade_info, grade_html):
|
||||
# 获取新增课程列表
|
||||
# 检查是否需要重新登录
|
||||
if grade_info == "LOGIN_REQUIRED":
|
||||
consecutive_errors += 1
|
||||
logger.warning(f"检测到会话过期,尝试重新登录... (连续失败: {consecutive_errors}/{max_consecutive_errors})")
|
||||
|
||||
if self.login():
|
||||
logger.info("重新登录成功,继续监控")
|
||||
consecutive_errors = 0 # 重置错误计数
|
||||
else:
|
||||
logger.error("重新登录失败")
|
||||
|
||||
if consecutive_errors >= max_consecutive_errors:
|
||||
error_msg = f"连续 {consecutive_errors} 次登录失败,会话可能已过期"
|
||||
logger.error(error_msg)
|
||||
self.send_error_notification(error_msg)
|
||||
consecutive_errors = 0
|
||||
|
||||
time.sleep(self.config['check_interval'])
|
||||
continue
|
||||
|
||||
# 检查是否是网络错误(临时性问题)
|
||||
if grade_info == "NETWORK_ERROR":
|
||||
consecutive_errors += 1
|
||||
logger.warning(f"检测到网络错误或页面过短,可能是临时问题 (连续失败: {consecutive_errors}/{max_consecutive_errors})")
|
||||
|
||||
# 连续失败3次尝试重新登录
|
||||
if consecutive_errors >= 3:
|
||||
logger.warning("⚠️ 尝试重新登录以解决网络问题...")
|
||||
if self.login():
|
||||
logger.info("✓ 重新登录成功,重置错误计数")
|
||||
consecutive_errors = 0 # 重置错误计数
|
||||
time.sleep(5)
|
||||
continue
|
||||
else:
|
||||
logger.error("✗ 重新登录失败")
|
||||
|
||||
if consecutive_errors >= max_consecutive_errors:
|
||||
error_msg = f"连续 {consecutive_errors} 次网络错误"
|
||||
logger.error(error_msg)
|
||||
self.send_error_notification(f"{error_msg}\n\n可能需要检查网络连接或服务器状态")
|
||||
consecutive_errors = 0
|
||||
else:
|
||||
# 临时错误,短暂等待后重试
|
||||
logger.info(f"等待 30 秒后重试...")
|
||||
time.sleep(30)
|
||||
continue
|
||||
|
||||
# 解析课程列表
|
||||
current_courses = self.parse_courses(grade_info)
|
||||
|
||||
# 如果解析失败(0门课程)且之前有课程,可能是临时问题
|
||||
if len(current_courses) == 0:
|
||||
# 检查是否之前有课程记录
|
||||
courses_file = self.script_dir / '.last_courses.txt'
|
||||
if courses_file.exists():
|
||||
current_courses = self.parse_courses(grade_info)
|
||||
# 简单通知有新课程
|
||||
self.send_email_notification(current_courses[:3]) # 只显示前3门
|
||||
try:
|
||||
previous_courses = courses_file.read_text(encoding='utf-8').strip().split('\n')
|
||||
previous_courses = [c for c in previous_courses if c]
|
||||
|
||||
if len(previous_courses) > 0:
|
||||
consecutive_errors += 1
|
||||
logger.warning(f"⚠️ 解析到0门课程,但之前有{len(previous_courses)}门课程,可能是临时问题")
|
||||
logger.warning(f"连续异常: {consecutive_errors}/{max_consecutive_errors}")
|
||||
|
||||
if consecutive_errors >= 3: # 连续3次解析失败尝试重新登录
|
||||
error_msg = f"连续 {consecutive_errors} 次无法解析课程(之前有{len(previous_courses)}门课程)"
|
||||
logger.error(error_msg)
|
||||
|
||||
# 尝试重新登录
|
||||
logger.warning("⚠️ 尝试重新登录以解决解析问题...")
|
||||
if self.login():
|
||||
logger.info("✓ 重新登录成功,重置错误计数")
|
||||
consecutive_errors = 0 # 重置错误计数
|
||||
time.sleep(5) # 等待一下再继续
|
||||
continue
|
||||
else:
|
||||
logger.error("✗ 重新登录失败")
|
||||
if consecutive_errors >= max_consecutive_errors:
|
||||
self.send_error_notification(f"{error_msg}\n\n重新登录失败,可能需要人工介入")
|
||||
consecutive_errors = 0
|
||||
else:
|
||||
# 短暂等待后重试
|
||||
logger.info(f"等待 30 秒后重试...")
|
||||
time.sleep(30)
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.debug(f"检查历史课程时出错: {e}")
|
||||
|
||||
# 检查变化
|
||||
has_changes, new_courses = self.check_grade_changes(grade_info, grade_html)
|
||||
|
||||
if has_changes:
|
||||
if new_courses:
|
||||
# 有新增课程,发送包含课程列表的通知
|
||||
logger.info(f"共解析到 {len(current_courses)} 门课程")
|
||||
self.send_email_notification(new_courses)
|
||||
else:
|
||||
# 没有新增课程但内容变化(成绩更新),发送通用通知
|
||||
self.send_email_notification()
|
||||
|
||||
# 成功执行,重置错误计数
|
||||
@@ -709,6 +884,9 @@ def main():
|
||||
# 测试模式(获取一次成绩并显示内容)
|
||||
python3 monitor.py --test
|
||||
|
||||
# 调试模式(显示详细日志)
|
||||
python3 monitor.py --debug
|
||||
|
||||
# 查看保存的成绩内容
|
||||
cat .last_grade_content.txt
|
||||
|
||||
@@ -723,6 +901,12 @@ def main():
|
||||
help='测试模式:仅获取一次成绩页面并显示内容,不进行监控'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--debug',
|
||||
action='store_true',
|
||||
help='调试模式:显示详细的调试信息'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--config',
|
||||
default='config.ini',
|
||||
@@ -731,6 +915,12 @@ def main():
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 如果启用调试模式,调整日志级别
|
||||
if args.debug:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.info("已启用调试模式")
|
||||
|
||||
monitor = GradeMonitor(config_file=args.config, test_mode=args.test)
|
||||
monitor.run()
|
||||
|
||||
|
||||
39
readme.md
39
readme.md
@@ -106,6 +106,44 @@ cat .last_grade_content.txt
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 快速诊断工具
|
||||
|
||||
如果遇到问题,使用诊断脚本快速检查:
|
||||
```bash
|
||||
chmod +x diagnose.sh
|
||||
./diagnose.sh
|
||||
```
|
||||
|
||||
该脚本会自动检查:
|
||||
- 依赖库是否安装
|
||||
- 能否成功提取成绩
|
||||
- 调试文件内容
|
||||
- 并提供修复建议
|
||||
|
||||
### 问题:未能提取到成绩信息(共 0 门课程)
|
||||
|
||||
**症状:** 日志显示 `[WARNING] 未能提取到成绩信息,返回原始文本`
|
||||
|
||||
**快速修复:**
|
||||
```bash
|
||||
# 1. 检查并安装依赖
|
||||
pip3 install beautifulsoup4 requests
|
||||
|
||||
# 2. 运行调试模式
|
||||
python3 monitor.py --test --debug
|
||||
|
||||
# 3. 查看详细信息
|
||||
cat .last_grade_content.txt
|
||||
cat debug_page.html | head -50
|
||||
```
|
||||
|
||||
**可能原因:**
|
||||
1. BeautifulSoup 库未安装
|
||||
2. 登录会话失效
|
||||
3. 网页结构变化
|
||||
|
||||
**详细解决方案:** 查看 [故障排查指南.md](故障排查指南.md)
|
||||
|
||||
### 问题:登录失败
|
||||
|
||||
**解决:**
|
||||
@@ -125,6 +163,7 @@ cat .last_grade_content.txt
|
||||
**解决:**
|
||||
- 程序会自动增加等待时间
|
||||
- 可在 `config.ini` 中增大 `CHECK_INTERVAL`
|
||||
- 详见 [常见问题解决.md](常见问题解决.md)
|
||||
|
||||
## 配置说明
|
||||
|
||||
|
||||
202
修复说明_2026-01-21.md
Normal file
202
修复说明_2026-01-21.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# 问题修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
服务器运行时出现以下错误:
|
||||
```
|
||||
[WARNING] 未能提取到成绩信息,返回原始文本
|
||||
[INFO] 成绩无变化(共 0 门课程)
|
||||
```
|
||||
|
||||
## 根本原因
|
||||
|
||||
成绩提取函数 `extract_grade_info` 在某些情况下无法正确解析HTML页面,返回原始HTML文本,导致 `parse_courses` 函数无法从原始HTML中提取课程信息。
|
||||
|
||||
可能的具体原因包括:
|
||||
1. BeautifulSoup 库未安装
|
||||
2. 登录会话失效,返回的是登录页面
|
||||
3. 学校网页结构发生变化
|
||||
4. 网络问题导致获取的HTML不完整
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 改进 `extract_grade_info` 函数 (monitor.py 第286-370行)
|
||||
|
||||
**新增功能:**
|
||||
- ✅ 检测登录页面,返回特殊标记 `LOGIN_REQUIRED`
|
||||
- ✅ 支持多种表格选择器(`gridtable`, `dataList`, 关键词搜索)
|
||||
- ✅ 添加详细的调试日志(表头、行数等)
|
||||
- ✅ 自动保存问题HTML到 `debug_page.html` 便于排查
|
||||
- ✅ 提供成功提取的反馈信息
|
||||
|
||||
**改进代码片段:**
|
||||
```python
|
||||
# 检查是否是登录页面
|
||||
if soup.find('input', {'name': 'username'}) or soup.find('input', {'name': 'password'}):
|
||||
logger.error("检测到登录页面,可能需要重新登录")
|
||||
return "LOGIN_REQUIRED"
|
||||
|
||||
# 尝试多种方式查找成绩表格
|
||||
table = soup.find('table', {'class': 'gridtable'})
|
||||
if not table:
|
||||
table = soup.find('table', {'id': 'dataList'})
|
||||
if not table:
|
||||
# 尝试查找任何包含成绩相关标题的表格
|
||||
for tbl in soup.find_all('table'):
|
||||
headers = tbl.find_all('th')
|
||||
if headers and any('课程名称' in th.get_text() or '成绩' in th.get_text() for th in headers):
|
||||
table = tbl
|
||||
logger.info("通过表头关键词找到成绩表格")
|
||||
break
|
||||
```
|
||||
|
||||
### 2. 改进 `parse_courses` 函数 (monitor.py 第347-378行)
|
||||
|
||||
**新增功能:**
|
||||
- ✅ 检测 `LOGIN_REQUIRED` 标记
|
||||
- ✅ 检测未解析的原始HTML
|
||||
- ✅ 添加详细的解析日志
|
||||
- ✅ 统计解析的课程数量
|
||||
|
||||
**改进代码片段:**
|
||||
```python
|
||||
# 检查是否需要重新登录
|
||||
if grade_text == "LOGIN_REQUIRED":
|
||||
logger.warning("检测到登录失效,需要重新登录")
|
||||
return []
|
||||
|
||||
# 检查是否是原始HTML(未成功解析的标记)
|
||||
if '<html' in grade_text.lower() or '<body' in grade_text.lower():
|
||||
logger.warning("检测到未解析的HTML内容,成绩提取可能失败")
|
||||
logger.debug(f"文本前100字符: {grade_text[:100]}")
|
||||
return []
|
||||
```
|
||||
|
||||
### 3. 添加调试模式 (monitor.py 第743-797行)
|
||||
|
||||
**新增命令行参数:**
|
||||
```bash
|
||||
python3 monitor.py --debug # 启用详细日志
|
||||
```
|
||||
|
||||
**功能:**
|
||||
- 显示详细的调试信息
|
||||
- 帮助快速定位问题
|
||||
- 不影响正常运行模式
|
||||
|
||||
### 4. 创建诊断工具
|
||||
|
||||
**新增文件:**
|
||||
1. **diagnose.sh** - 自动诊断脚本
|
||||
- 检查依赖库安装
|
||||
- 运行测试并分析结果
|
||||
- 提供修复建议
|
||||
|
||||
2. **故障排查指南.md** - 详细的排查文档
|
||||
- 常见问题及解决方案
|
||||
- 调试流程说明
|
||||
- 联系支持信息
|
||||
|
||||
3. **更新 常见问题解决.md** - 添加新问题说明
|
||||
4. **更新 readme.md** - 添加快速诊断指引
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 方式1:自动诊断(推荐)
|
||||
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
chmod +x diagnose.sh
|
||||
./diagnose.sh
|
||||
```
|
||||
|
||||
### 方式2:手动调试
|
||||
|
||||
```bash
|
||||
# 1. 检查依赖
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
# 2. 运行调试模式
|
||||
python3 monitor.py --test --debug
|
||||
|
||||
# 3. 查看输出
|
||||
cat .last_grade_content.txt
|
||||
cat debug_page.html | head -50
|
||||
|
||||
# 4. 如果正常,重启服务
|
||||
systemctl restart grade-monitor
|
||||
```
|
||||
|
||||
### 方式3:启用服务调试模式
|
||||
|
||||
编辑服务配置:
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/grade-monitor.service
|
||||
```
|
||||
|
||||
修改启动命令:
|
||||
```ini
|
||||
ExecStart=/usr/bin/python3 /home/yourusername/grade_monitor/monitor.py --debug
|
||||
```
|
||||
|
||||
重新加载:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart grade-monitor
|
||||
```
|
||||
|
||||
## 预期效果
|
||||
|
||||
### 正常运行时的日志
|
||||
```
|
||||
[DEBUG] 找到总平均绩点: 总平均绩点:3.85
|
||||
[DEBUG] 找到成绩表格
|
||||
[DEBUG] 表头: ['学年学期', '课程代码', '课程序号', '课程名称', ...]
|
||||
[DEBUG] 找到 15 行成绩数据
|
||||
[INFO] 成功提取成绩信息,共 17 行
|
||||
[DEBUG] 开始解析成绩,共 17 行
|
||||
[DEBUG] 解析到课程: 高等数学A(1)
|
||||
[DEBUG] 解析到课程: 线性代数
|
||||
...
|
||||
[INFO] 共解析到 15 门课程
|
||||
[INFO] 成绩无变化(共 15 门课程)
|
||||
```
|
||||
|
||||
### 出错时的日志
|
||||
```
|
||||
[WARNING] 未找到成绩表格
|
||||
[WARNING] 未能提取到成绩信息,页面可能结构异常
|
||||
[INFO] 已保存HTML到 /home/user/grade_monitor/debug_page.html 供调试
|
||||
[WARNING] 检测到未解析的HTML内容,成绩提取可能失败
|
||||
[DEBUG] 文本前100字符: <!DOCTYPE html><html><head>...
|
||||
[INFO] 共解析到 0 门课程
|
||||
```
|
||||
|
||||
## 优势
|
||||
|
||||
1. **更好的错误提示** - 明确告知问题原因
|
||||
2. **自动保存调试信息** - debug_page.html 便于排查
|
||||
3. **灵活的表格查找** - 支持多种网页结构
|
||||
4. **详细的日志** - 方便追踪每一步
|
||||
5. **完善的文档** - 提供多种解决方案
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **不要过度使用调试模式** - 生产环境可能不需要
|
||||
2. **定期检查 debug_page.html** - 了解实际获取的内容
|
||||
3. **及时更新表格选择器** - 如果学校网页结构变化
|
||||
4. **保护敏感信息** - debug_page.html 可能包含个人信息
|
||||
|
||||
## 下一步
|
||||
|
||||
如果问题仍未解决:
|
||||
1. 查看 `debug_page.html` 的实际内容
|
||||
2. 检查HTML中的表格结构
|
||||
3. 可能需要调整 `extract_grade_info` 中的选择器
|
||||
4. 咨询开发者或提交 Issue
|
||||
|
||||
---
|
||||
|
||||
**版本:** 2026-01-21
|
||||
**修复人员:** GitHub Copilot
|
||||
**测试状态:** 待用户测试确认
|
||||
94
修复说明_2026-01-22_重复日志和自动重登录.md
Normal file
94
修复说明_2026-01-22_重复日志和自动重登录.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# 修复说明 - 2026年1月22日
|
||||
|
||||
## 修复的问题
|
||||
|
||||
### 1. 日志重复输出问题
|
||||
**现象**:每条日志都会输出两次
|
||||
|
||||
**原因**:`logging.basicConfig()` 配置会在模块每次被导入时重复添加处理器,导致日志重复输出。
|
||||
|
||||
**解决方案**:
|
||||
- 使用 `if not logger.handlers:` 检查,确保处理器只被添加一次
|
||||
- 设置 `logger.propagate = False` 防止日志传播到根记录器
|
||||
- 使用显式的logger配置替代 `basicConfig`
|
||||
|
||||
### 2. 连续异常时自动重新登录
|
||||
**现象**:当连续多次出现解析失败、获取页面失败或网络错误时,程序不会尝试重新登录,导致持续失败。
|
||||
|
||||
**解决方案**:
|
||||
在以下场景达到3次连续失败时,自动触发重新登录:
|
||||
|
||||
1. **获取成绩页面失败**
|
||||
- 连续3次失败 → 尝试重新登录
|
||||
- 登录成功 → 重置错误计数,继续监控
|
||||
- 登录失败 → 继续累积错误计数
|
||||
|
||||
2. **网络错误(页面过短)**
|
||||
- 连续3次网络错误 → 尝试重新登录
|
||||
- 登录成功 → 重置错误计数
|
||||
- 登录失败 → 继续累积错误计数
|
||||
|
||||
3. **解析课程失败(0门课程)**
|
||||
- 连续3次解析失败 → 尝试重新登录
|
||||
- 登录成功 → 重置错误计数
|
||||
- 登录失败 → 发送通知,可能需要人工介入
|
||||
|
||||
## 技术细节
|
||||
|
||||
### 日志配置改进
|
||||
```python
|
||||
# 旧代码
|
||||
logging.basicConfig(...)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 新代码
|
||||
logger = logging.getLogger(__name__)
|
||||
if not logger.handlers:
|
||||
# 配置处理器
|
||||
logger.propagate = False
|
||||
```
|
||||
|
||||
### 重新登录逻辑
|
||||
```python
|
||||
if consecutive_errors >= 3:
|
||||
logger.warning("⚠️ 尝试重新登录以解决问题...")
|
||||
if self.login():
|
||||
logger.info("✓ 重新登录成功,重置错误计数")
|
||||
consecutive_errors = 0
|
||||
continue
|
||||
else:
|
||||
logger.error("✗ 重新登录失败")
|
||||
```
|
||||
|
||||
## 使用建议
|
||||
|
||||
1. **重启服务**:修改后需要重启服务使改动生效
|
||||
```bash
|
||||
sudo systemctl restart grade-monitor
|
||||
```
|
||||
|
||||
2. **查看日志**:确认日志不再重复
|
||||
```bash
|
||||
sudo journalctl -u grade-monitor -f
|
||||
# 或
|
||||
tail -f monitor.log
|
||||
```
|
||||
|
||||
3. **监控效果**:
|
||||
- 日志应该只输出一次
|
||||
- 连续3次异常会自动尝试重新登录
|
||||
- 重新登录成功后会显示 "✓ 重新登录成功"
|
||||
|
||||
## 预期效果
|
||||
|
||||
- ✅ 日志不再重复输出
|
||||
- ✅ 连续3次异常自动重新登录
|
||||
- ✅ 重新登录成功后重置错误计数
|
||||
- ✅ 减少因登录会话过期导致的持续失败
|
||||
- ✅ 更好的容错性和自愈能力
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 重新登录会记录在日志中,便于追踪
|
||||
- 如果重新登录失败,仍会继续累积错误计数
|
||||
- 达到5次最大连续错误后会发送邮件通知
|
||||
95
常见问题解决.md
95
常见问题解决.md
@@ -1,5 +1,100 @@
|
||||
# 常见问题解决方案
|
||||
|
||||
## 🚨 "未能提取到成绩信息,共 0 门课程" 错误
|
||||
|
||||
### 问题症状
|
||||
日志中反复出现:
|
||||
```
|
||||
[WARNING] 未能提取到成绩信息,返回原始文本
|
||||
[INFO] 成绩无变化(共 0 门课程)
|
||||
```
|
||||
|
||||
### 快速诊断
|
||||
|
||||
#### 1. 检查 BeautifulSoup 是否安装
|
||||
```bash
|
||||
python3 -c "import bs4; print('BeautifulSoup4 已安装')"
|
||||
```
|
||||
|
||||
如果报错,安装依赖:
|
||||
```bash
|
||||
pip3 install beautifulsoup4
|
||||
# 或完整安装所有依赖
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
#### 2. 使用调试模式查看详细信息
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
python3 monitor.py --test --debug
|
||||
```
|
||||
|
||||
查看输出中是否有:
|
||||
- ✅ "找到成绩表格"
|
||||
- ✅ "找到 X 行成绩数据"
|
||||
- ✅ "共解析到 X 门课程"
|
||||
|
||||
如果都没有,继续下一步。
|
||||
|
||||
#### 3. 检查是否登录失效
|
||||
查看日志是否有 "检测到登录页面" 错误:
|
||||
```bash
|
||||
grep "登录页面\|LOGIN_REQUIRED" ~/grade_monitor/monitor.log
|
||||
```
|
||||
|
||||
如果有,重启服务重新登录:
|
||||
```bash
|
||||
systemctl restart grade-monitor
|
||||
```
|
||||
|
||||
#### 4. 查看调试HTML文件
|
||||
程序会自动保存问题页面到 `debug_page.html`:
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
|
||||
# 检查文件是否存在
|
||||
ls -lh debug_page.html
|
||||
|
||||
# 查看是否包含成绩表格
|
||||
grep -i "gridtable\|课程名称\|成绩" debug_page.html | head -5
|
||||
```
|
||||
|
||||
### 解决方案
|
||||
|
||||
**方案1:重新安装依赖并重启**
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
pip3 install -r requirements.txt
|
||||
systemctl restart grade-monitor
|
||||
tail -f monitor.log
|
||||
```
|
||||
|
||||
**方案2:启用调试模式监控**
|
||||
编辑服务配置:
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/grade-monitor.service
|
||||
```
|
||||
|
||||
修改 ExecStart 行:
|
||||
```ini
|
||||
ExecStart=/usr/bin/python3 /home/yourusername/grade_monitor/monitor.py --debug
|
||||
```
|
||||
|
||||
重新加载并重启:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart grade-monitor
|
||||
tail -f ~/grade_monitor/monitor.log
|
||||
```
|
||||
|
||||
**方案3:如果是网页结构变化**
|
||||
查看详细排查指南:
|
||||
```bash
|
||||
cat ~/grade_monitor/故障排查指南.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ "请不要过快点击" 错误
|
||||
|
||||
### 问题原因
|
||||
|
||||
179
快速更新指南.md
Normal file
179
快速更新指南.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# 快速更新指南 - 修复会话过期问题
|
||||
|
||||
## 🎯 本次更新解决的问题
|
||||
|
||||
**症状:** 程序运行几小时后出现:
|
||||
```
|
||||
[WARNING] 未能提取到成绩信息,返回原始文本
|
||||
[INFO] 成绩无变化(共 0 门课程)
|
||||
```
|
||||
|
||||
**原因:** 登录会话过期,需要重新登录
|
||||
|
||||
**解决:** ✅ 自动检测并重新登录
|
||||
|
||||
---
|
||||
|
||||
## 📦 方式1:快速更新(推荐)
|
||||
|
||||
### 在本地打包
|
||||
```bash
|
||||
cd /mnt/e/50425/Documents/Github/GPA_Monitoring
|
||||
chmod +x 打包.sh
|
||||
./打包.sh
|
||||
```
|
||||
|
||||
### 上传到服务器
|
||||
```bash
|
||||
scp gpa_monitor.tar.gz 用户名@服务器IP:~/
|
||||
```
|
||||
|
||||
### 在服务器上更新
|
||||
```bash
|
||||
# 停止服务
|
||||
systemctl stop grade-monitor
|
||||
|
||||
# 备份
|
||||
cd ~/grade_monitor
|
||||
cp monitor.py monitor.py.backup.$(date +%Y%m%d)
|
||||
|
||||
# 解压更新
|
||||
tar -xzf ~/gpa_monitor.tar.gz -C ~/grade_monitor
|
||||
|
||||
# 赋予权限
|
||||
chmod +x diagnose.sh
|
||||
|
||||
# 重启服务
|
||||
systemctl restart grade-monitor
|
||||
|
||||
# 查看日志(确认自动重新登录功能)
|
||||
tail -f monitor.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 方式2:仅更新 monitor.py
|
||||
|
||||
如果只想更新主程序文件:
|
||||
|
||||
### 在本地
|
||||
```bash
|
||||
# WSL中
|
||||
cd /mnt/e/50425/Documents/Github/GPA_Monitoring
|
||||
scp monitor.py 用户名@服务器IP:~/grade_monitor/
|
||||
```
|
||||
|
||||
### 在服务器上
|
||||
```bash
|
||||
systemctl stop grade-monitor
|
||||
systemctl start grade-monitor
|
||||
tail -f ~/grade_monitor/monitor.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验证更新成功
|
||||
|
||||
查看日志,应该能看到:
|
||||
|
||||
### 正常运行
|
||||
```
|
||||
[INFO] 开始新一轮检查
|
||||
[INFO] 获取成绩页面...
|
||||
[DEBUG] 找到成绩表格
|
||||
[INFO] 成功提取成绩信息,共 17 行
|
||||
[INFO] 共解析到 15 门课程
|
||||
[INFO] 成绩无变化(共 15 门课程)
|
||||
```
|
||||
|
||||
### 会话过期时自动处理
|
||||
```
|
||||
[WARNING] 检测到会话过期,尝试重新登录...
|
||||
[INFO] 开始登录统一身份认证...
|
||||
[INFO] 登录成功!成功访问成绩页面
|
||||
[INFO] 重新登录成功,继续监控
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 故障诊断
|
||||
|
||||
如果更新后仍有问题:
|
||||
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
|
||||
# 运行诊断工具
|
||||
./diagnose.sh
|
||||
|
||||
# 或手动测试
|
||||
python3 monitor.py --test --debug
|
||||
|
||||
# 查看生成的调试文件
|
||||
ls -lh debug_page.html
|
||||
cat .last_grade_content.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 更新内容详细列表
|
||||
|
||||
1. **会话过期自动重新登录** (monitor.py 第697-718行)
|
||||
- 检测 `LOGIN_REQUIRED` 状态
|
||||
- 自动调用 `login()` 重新登录
|
||||
- 记录详细日志
|
||||
|
||||
2. **改进成绩提取** (monitor.py 第286-370行)
|
||||
- 检测登录页面
|
||||
- 支持多种表格选择器
|
||||
- 自动保存 debug_page.html
|
||||
- 添加详细调试日志
|
||||
|
||||
3. **改进课程解析** (monitor.py 第375-405行)
|
||||
- 检测登录失效
|
||||
- 识别未解析的HTML
|
||||
- 统计课程数量
|
||||
|
||||
4. **新增调试模式**
|
||||
- `python3 monitor.py --debug`
|
||||
- 显示详细运行信息
|
||||
|
||||
5. **新增诊断工具**
|
||||
- `diagnose.sh` - 自动检测并给出建议
|
||||
- `故障排查指南.md` - 详细排查步骤
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
1. **定期检查日志**(每天1-2次即可)
|
||||
```bash
|
||||
tail -n 50 ~/grade_monitor/monitor.log
|
||||
```
|
||||
|
||||
2. **不要频繁重启服务**
|
||||
- 新版本会自动处理会话过期
|
||||
- 只在真正出问题时才重启
|
||||
|
||||
3. **保留备份**
|
||||
```bash
|
||||
# 每次更新前都会自动备份
|
||||
ls -lh ~/grade_monitor/monitor.py.backup.*
|
||||
```
|
||||
|
||||
4. **合理设置检查间隔**
|
||||
- 建议 120-300 秒
|
||||
- 避免触发学校系统限流
|
||||
|
||||
---
|
||||
|
||||
## 🆘 需要帮助?
|
||||
|
||||
1. 先运行诊断工具:`./diagnose.sh`
|
||||
2. 查看故障排查指南:`cat 故障排查指南.md`
|
||||
3. 查看详细修复说明:`cat 修复说明_2026-01-21.md`
|
||||
|
||||
---
|
||||
|
||||
**更新日期:** 2026-01-21
|
||||
**版本:** v2.0 - 自动会话恢复版
|
||||
48
打包.sh
48
打包.sh
@@ -34,9 +34,14 @@ tar -czf "$ARCHIVE_NAME" \
|
||||
config.ini \
|
||||
requirements.txt \
|
||||
setup_python.sh \
|
||||
diagnose.sh \
|
||||
grade-monitor.service \
|
||||
readme.md \
|
||||
"常见问题解决.md" \
|
||||
"故障排查指南.md" \
|
||||
"重启服务.md" \
|
||||
"Debian服务器部署指南.md" \
|
||||
"修复说明_2026-01-21.md" \
|
||||
2>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
@@ -45,23 +50,46 @@ if [ $? -eq 0 ]; then
|
||||
echo "压缩包信息:"
|
||||
ls -lh "$ARCHIVE_NAME"
|
||||
echo ""
|
||||
echo "包含文件:"
|
||||
echo "📄 包含文件:"
|
||||
tar -tzf "$ARCHIVE_NAME"
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "下一步:"
|
||||
echo "1. 上传到服务器:"
|
||||
echo "✨ 本次更新内容(2026-01-21):"
|
||||
echo " 1. ✅ 修复会话过期问题 - 自动重新登录"
|
||||
echo " 2. ✅ 改进成绩提取 - 多种表格选择器"
|
||||
echo " 3. ✅ 新增调试模式 - --debug 参数"
|
||||
echo " 4. ✅ 新增诊断工具 - diagnose.sh"
|
||||
echo " 5. ✅ 自动保存 debug_page.html 供排查"
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "📤 上传到服务器:"
|
||||
echo " scp $ARCHIVE_NAME 用户名@服务器IP:~/"
|
||||
echo ""
|
||||
echo "2. 在服务器上解压:"
|
||||
echo " tar -xzf $ARCHIVE_NAME"
|
||||
echo " cd gpa_monitor"
|
||||
echo "🔧 服务器上部署更新:"
|
||||
echo " # 停止服务"
|
||||
echo " systemctl stop grade-monitor"
|
||||
echo ""
|
||||
echo "3. 运行安装脚本:"
|
||||
echo " chmod +x setup_python.sh"
|
||||
echo " ./setup_python.sh"
|
||||
echo " # 备份旧文件"
|
||||
echo " cd ~/grade_monitor"
|
||||
echo " cp monitor.py monitor.py.backup"
|
||||
echo ""
|
||||
echo "详细说明请查看: Debian服务器部署指南.md"
|
||||
echo " # 解压新文件"
|
||||
echo " tar -xzf ~/gpa_monitor.tar.gz -C ~/grade_monitor"
|
||||
echo ""
|
||||
echo " # 赋予执行权限"
|
||||
echo " chmod +x diagnose.sh"
|
||||
echo ""
|
||||
echo " # 重启服务"
|
||||
echo " systemctl restart grade-monitor"
|
||||
echo ""
|
||||
echo " # 查看日志"
|
||||
echo " tail -f monitor.log"
|
||||
echo ""
|
||||
echo "🔍 如果遇到问题:"
|
||||
echo " cd ~/grade_monitor"
|
||||
echo " ./diagnose.sh"
|
||||
echo ""
|
||||
echo "详细说明: 修复说明_2026-01-21.md"
|
||||
echo "========================================="
|
||||
else
|
||||
echo "✗ 打包失败"
|
||||
|
||||
143
故障排查指南.md
Normal file
143
故障排查指南.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 故障排查指南
|
||||
|
||||
## 问题:未能提取到成绩信息,共 0 门课程
|
||||
|
||||
### 症状
|
||||
日志中出现以下警告信息:
|
||||
```
|
||||
[WARNING] 未能提取到成绩信息,返回原始文本
|
||||
[INFO] 成绩无变化(共 0 门课程)
|
||||
```
|
||||
|
||||
### 可能的原因和解决方案
|
||||
|
||||
#### 1. BeautifulSoup 未安装
|
||||
**检查方法:**
|
||||
```bash
|
||||
python3 -c "import bs4; print('BeautifulSoup4 已安装')"
|
||||
```
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
pip3 install beautifulsoup4
|
||||
# 或
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
#### 2. 登录会话失效
|
||||
**症状:** 日志中可能出现 "检测到登录页面" 的错误
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 停止服务
|
||||
systemctl stop grade-monitor
|
||||
|
||||
# 重新启动服务(会重新登录)
|
||||
systemctl start grade-monitor
|
||||
```
|
||||
|
||||
#### 3. 网页结构变化
|
||||
**检查方法:**
|
||||
程序会自动保存 HTML 到 `debug_page.html`,查看这个文件:
|
||||
```bash
|
||||
# 查看 debug_page.html 是否存在
|
||||
ls -la ~/grade_monitor/debug_page.html
|
||||
|
||||
# 查看 HTML 内容,确认是否有成绩表格
|
||||
grep -i "gridtable\|课程名称\|成绩" ~/grade_monitor/debug_page.html
|
||||
```
|
||||
|
||||
**解决方案:**
|
||||
如果网页结构变化,可能需要修改 `monitor.py` 中的 `extract_grade_info` 函数。
|
||||
当前支持的表格选择器:
|
||||
- `<table class="gridtable">`
|
||||
- `<table id="dataList">`
|
||||
- 任何包含"课程名称"或"成绩"关键词的表格
|
||||
|
||||
#### 4. 启用调试模式排查
|
||||
|
||||
**临时测试(手动运行):**
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
python3 monitor.py --test --debug
|
||||
```
|
||||
|
||||
**查看详细日志:**
|
||||
```bash
|
||||
# 启用调试模式修改服务配置
|
||||
sudo nano /etc/systemd/system/grade-monitor.service
|
||||
|
||||
# 在 ExecStart 行添加 --debug 参数
|
||||
ExecStart=/usr/bin/python3 /home/yourusername/grade_monitor/monitor.py --debug
|
||||
|
||||
# 重新加载并重启
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart grade-monitor
|
||||
|
||||
# 查看详细日志
|
||||
tail -f ~/grade_monitor/monitor.log
|
||||
```
|
||||
|
||||
#### 5. 检查保存的文件
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
|
||||
# 查看上次提取的成绩内容
|
||||
cat .last_grade_content.txt
|
||||
|
||||
# 查看上次保存的 HTML(如果存在)
|
||||
cat .last_grade_page.html | head -50
|
||||
|
||||
# 查看调试 HTML(出错时自动保存)
|
||||
cat debug_page.html | head -50
|
||||
```
|
||||
|
||||
### 调试流程
|
||||
|
||||
1. **启用调试模式运行一次测试:**
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
python3 monitor.py --test --debug
|
||||
```
|
||||
|
||||
2. **查看输出信息:**
|
||||
- 是否显示 "找到成绩表格"?
|
||||
- 是否显示 "找到 X 行成绩数据"?
|
||||
- 是否显示 "共解析到 X 门课程"?
|
||||
|
||||
3. **检查保存的文件:**
|
||||
```bash
|
||||
# 如果生成了 debug_page.html
|
||||
ls -lh debug_page.html
|
||||
|
||||
# 查看是否包含成绩表格
|
||||
grep -A 5 "gridtable\|dataList" debug_page.html
|
||||
```
|
||||
|
||||
4. **如果是网页结构问题:**
|
||||
- 将 `debug_page.html` 发给开发者
|
||||
- 或者自己检查 HTML 中的表格结构
|
||||
- 修改 `extract_grade_info` 函数中的选择器
|
||||
|
||||
### 常见问题
|
||||
|
||||
**Q: 日志显示"未找到成绩表格"**
|
||||
A: 网页结构可能变化,需要查看 debug_page.html 确认实际的表格结构
|
||||
|
||||
**Q: 日志显示"找到 0 行成绩数据"**
|
||||
A: 表格找到了但是为空,可能是:
|
||||
- 登录失效,返回的是空表格
|
||||
- 实际就是没有成绩数据
|
||||
|
||||
**Q: 日志显示"检测到未解析的HTML内容"**
|
||||
A: BeautifulSoup 解析失败,返回了原始 HTML。检查:
|
||||
1. BeautifulSoup 是否正确安装
|
||||
2. HTML 格式是否正确(查看 debug_page.html)
|
||||
|
||||
### 联系支持
|
||||
|
||||
如果以上方法都无法解决问题,请提供:
|
||||
1. `monitor.log` 的最后 50 行
|
||||
2. `debug_page.html` 文件(如果存在)
|
||||
3. `.last_grade_content.txt` 文件内容
|
||||
4. 是否能手动登录并看到成绩页面
|
||||
43
重启服务.md
43
重启服务.md
@@ -10,10 +10,47 @@ tar -xzf ~/gpa_monitor.tar.gz -C ~/grade_monitor --strip-components=0
|
||||
# 或者只替换 monitor.py
|
||||
# 你可以直接编辑:
|
||||
nano monitor.py
|
||||
# 找到 parse_courses 函数,修改那一行判断条件
|
||||
|
||||
# 重启服务
|
||||
systemctl restart grade-monitor
|
||||
|
||||
# 查看日志
|
||||
tail -f monitor.log
|
||||
# 查看实时日志
|
||||
tail -f monitor.log
|
||||
|
||||
# 或查看最近的日志(包含完整上下文)
|
||||
tail -n 100 monitor.log
|
||||
|
||||
---
|
||||
|
||||
## 常见问题:会话过期
|
||||
|
||||
如果日志显示:
|
||||
```
|
||||
[WARNING] 未能提取到成绩信息,返回原始文本
|
||||
[INFO] 成绩无变化(共 0 门课程)
|
||||
```
|
||||
|
||||
**原因:** 登录会话几小时后自动过期
|
||||
|
||||
**解决:** 最新版本已自动处理!代码会:
|
||||
1. 检测到 "LOGIN_REQUIRED" 时自动重新登录
|
||||
2. 记录日志:`[WARNING] 检测到会话过期,尝试重新登录...`
|
||||
3. 重新登录后继续监控
|
||||
|
||||
**手动重启(如果需要):**
|
||||
```bash
|
||||
systemctl restart grade-monitor
|
||||
tail -f monitor.log
|
||||
```
|
||||
|
||||
**调试模式(查看详细信息):**
|
||||
```bash
|
||||
# 手动测试
|
||||
python3 monitor.py --test --debug
|
||||
|
||||
# 或修改服务启用调试
|
||||
sudo nano /etc/systemd/system/grade-monitor.service
|
||||
# 在 ExecStart 添加 --debug 参数
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart grade-monitor
|
||||
```
|
||||
194
间歇性解析失败说明.md
Normal file
194
间歇性解析失败说明.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# 间歇性解析失败说明
|
||||
|
||||
## 现象
|
||||
|
||||
日志显示偶尔出现解析失败,但下一次检查又恢复正常:
|
||||
|
||||
```
|
||||
17:34:05 - ✅ 成功提取成绩信息,共 14 行,解析到 8 门课程
|
||||
17:36:12 - ✅ 成功提取成绩信息,共 14 行,解析到 8 门课程
|
||||
17:38:12 - ❌ 未找到成绩表格,解析到 0 门课程
|
||||
17:40:12 - ✅ 成功提取成绩信息,共 14 行,解析到 8 门课程
|
||||
```
|
||||
|
||||
## 原因分析
|
||||
|
||||
这种**偶发性失败**通常是由以下原因造成的:
|
||||
|
||||
### 1. 网络波动(最常见)
|
||||
- 数据包传输不完整
|
||||
- HTML内容被截断
|
||||
- 连接临时中断
|
||||
|
||||
### 2. 服务器临时问题
|
||||
- 学校服务器负载高
|
||||
- 短暂的维护或重启
|
||||
- 数据库查询超时
|
||||
|
||||
### 3. WebVPN不稳定
|
||||
- WebVPN代理服务波动
|
||||
- SSL握手失败
|
||||
- 重定向异常
|
||||
|
||||
### 4. 时机问题
|
||||
- 正好在服务器更新数据时访问
|
||||
- 缓存失效瞬间
|
||||
- 负载均衡切换
|
||||
|
||||
## 改进措施
|
||||
|
||||
最新版本已添加**智能容错机制**:
|
||||
|
||||
### 1. 识别网络错误
|
||||
```python
|
||||
# 检查HTML内容长度
|
||||
if len(html) < 100:
|
||||
logger.warning(f"HTML内容过短({len(html)}字节),可能是网络问题")
|
||||
return "NETWORK_ERROR"
|
||||
```
|
||||
|
||||
### 2. 自动重试
|
||||
当检测到临时性问题时:
|
||||
- ⚠️ 记录警告但不立即报错
|
||||
- ⏱️ 等待30秒后自动重试
|
||||
- 📊 只有连续3次失败才报警
|
||||
|
||||
### 3. 智能判断
|
||||
```python
|
||||
# 如果解析到0门课程,但之前有课程记录
|
||||
if len(current_courses) == 0 and len(previous_courses) > 0:
|
||||
logger.warning("可能是临时问题,30秒后重试")
|
||||
time.sleep(30)
|
||||
continue # 重新检查
|
||||
```
|
||||
|
||||
### 4. 保存诊断信息
|
||||
- 自动保存 `debug_page.html`
|
||||
- 记录HTML长度和摘要
|
||||
- 便于事后分析
|
||||
|
||||
## 预期行为
|
||||
|
||||
### 场景1:偶发性失败(正常)
|
||||
```
|
||||
[INFO] 成功提取成绩信息,共 14 行
|
||||
[INFO] 共解析到 8 门课程
|
||||
[INFO] 等待 120 秒...
|
||||
|
||||
[WARNING] 未找到成绩表格
|
||||
[WARNING] ⚠️ 解析到0门课程,但之前有8门课程,可能是临时问题
|
||||
[INFO] 等待 30 秒后重试...
|
||||
|
||||
[INFO] 成功提取成绩信息,共 14 行 ← 重试成功
|
||||
[INFO] 共解析到 8 门课程
|
||||
```
|
||||
|
||||
### 场景2:持续失败(异常)
|
||||
```
|
||||
[WARNING] ⚠️ 解析到0门课程,可能是临时问题
|
||||
[WARNING] 连续异常: 1/5
|
||||
|
||||
[WARNING] ⚠️ 解析到0门课程,可能是临时问题
|
||||
[WARNING] 连续异常: 2/5
|
||||
|
||||
[WARNING] ⚠️ 解析到0门课程,可能是临时问题
|
||||
[WARNING] 连续异常: 3/5 ← 开始报警
|
||||
|
||||
[ERROR] 连续 3 次无法解析课程(之前有8门课程)
|
||||
```
|
||||
|
||||
## 判断标准
|
||||
|
||||
### ✅ 无需担心的情况
|
||||
- 失败率 < 20%(每10次失败少于2次)
|
||||
- 失败后1-2次就恢复
|
||||
- 日志显示"等待30秒后重试"
|
||||
- 下次检查自动恢复正常
|
||||
|
||||
### ⚠️ 需要关注的情况
|
||||
- 失败率 > 20%
|
||||
- 连续3次以上失败
|
||||
- 日志显示"连续异常"
|
||||
- 出现错误通知邮件
|
||||
|
||||
### 🚨 需要处理的情况
|
||||
- 连续5次以上失败
|
||||
- 失败率 > 50%
|
||||
- 伴随"会话过期"错误
|
||||
- debug_page.html 显示登录页面
|
||||
|
||||
## 统计失败率
|
||||
|
||||
使用诊断工具检查:
|
||||
```bash
|
||||
cd ~/grade_monitor
|
||||
./diagnose.sh
|
||||
|
||||
# 或手动统计
|
||||
echo "成功次数: $(grep -c '共解析到 [1-9]' monitor.log)"
|
||||
echo "失败次数: $(grep -c '共解析到 0 门课程' monitor.log)"
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 不要过度担心
|
||||
- 偶尔失败是**正常现象**
|
||||
- 程序已自动处理
|
||||
- 只要失败率低,无需干预
|
||||
|
||||
### 2. 定期检查日志
|
||||
```bash
|
||||
# 查看最近的失败记录
|
||||
grep "未找到成绩表格\|共解析到 0 门课程" ~/grade_monitor/monitor.log | tail -10
|
||||
|
||||
# 查看是否自动恢复
|
||||
tail -n 20 ~/grade_monitor/monitor.log
|
||||
```
|
||||
|
||||
### 3. 调整检查间隔
|
||||
如果失败频繁,可以增加间隔:
|
||||
```ini
|
||||
[monitor]
|
||||
CHECK_INTERVAL = 180 # 从120改为180秒
|
||||
```
|
||||
|
||||
### 4. 信任自动化
|
||||
- 程序会自动重试
|
||||
- 只有持续失败才报警
|
||||
- 收到邮件通知时再处理
|
||||
|
||||
## 何时需要手动干预
|
||||
|
||||
只在以下情况需要重启服务:
|
||||
|
||||
1. **连续多次收到错误邮件**
|
||||
2. **日志显示持续登录失败**
|
||||
3. **debug_page.html 显示异常内容**
|
||||
4. **失败率持续超过50%**
|
||||
|
||||
其他情况下,让程序自动处理即可。
|
||||
|
||||
## 对比:改进前 vs 改进后
|
||||
|
||||
### 改进前
|
||||
```
|
||||
17:38:12 - ❌ 未找到成绩表格,解析到 0 门课程
|
||||
17:38:12 - 成绩无变化(共 0 门课程) ← 直接当作正常
|
||||
17:38:12 - 等待 120 秒... ← 不重试
|
||||
17:40:12 - 开始新一轮检查 ← 2分钟后才重试
|
||||
```
|
||||
|
||||
### 改进后
|
||||
```
|
||||
17:38:12 - ❌ 未找到成绩表格
|
||||
17:38:12 - ⚠️ 解析到0门课程,但之前有8门课程
|
||||
17:38:12 - 等待 30 秒后重试... ← 智能判断
|
||||
17:38:42 - ✅ 成功提取成绩信息 ← 快速恢复
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**总结:** 偶发性失败是网络应用的正常现象。新版本通过智能重试机制,将影响降到最低。只要失败率在合理范围(<20%),无需任何手动干预。
|
||||
|
||||
**更新日期:** 2026-01-21
|
||||
**版本:** v2.1 - 智能容错版
|
||||
Reference in New Issue
Block a user