1
This commit is contained in:
@@ -1,202 +0,0 @@
|
|||||||
# 问题修复说明
|
|
||||||
|
|
||||||
## 问题描述
|
|
||||||
|
|
||||||
服务器运行时出现以下错误:
|
|
||||||
```
|
|
||||||
[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
|
|
||||||
**测试状态:** 待用户测试确认
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
# 修复说明 - 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次最大连续错误后会发送邮件通知
|
|
||||||
194
间歇性解析失败说明.md
194
间歇性解析失败说明.md
@@ -1,194 +0,0 @@
|
|||||||
# 间歇性解析失败说明
|
|
||||||
|
|
||||||
## 现象
|
|
||||||
|
|
||||||
日志显示偶尔出现解析失败,但下一次检查又恢复正常:
|
|
||||||
|
|
||||||
```
|
|
||||||
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