1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
from __future__ import absolute_import, unicode_literals |
3
|
|
|
|
4
|
|
|
import re |
5
|
|
|
from bs4 import BeautifulSoup |
6
|
|
|
from requests import RequestException, TooManyRedirects |
7
|
|
|
|
8
|
|
|
from school_api.client.api.base import BaseSchoolApi |
9
|
|
|
from school_api.client.api.utils import get_alert_tip |
10
|
|
|
from school_api.exceptions import ScoreException |
11
|
|
|
|
12
|
|
|
|
13
|
|
|
class Score(BaseSchoolApi): |
14
|
|
|
''' 学生成绩获取 ''' |
15
|
|
|
|
16
|
|
|
def get_score(self, score_year=None, score_term=None, use_api=0, **kwargs): |
17
|
|
|
''' 成绩信息 获取入口 |
18
|
|
|
:param score_year: 成绩学年 |
19
|
|
|
:param score_term: 成绩学期 |
20
|
|
|
:param use_api: 0.接口1, 1.接口2, 2.接口3 ... |
21
|
|
|
:param kwargs: requests模块参数 |
22
|
|
|
return |
23
|
|
|
''' |
24
|
|
|
score_url = self.school_url['SCORE_URL'][use_api] + self.user.account |
25
|
|
|
|
26
|
|
|
try: |
27
|
|
|
view_state = self._get_view_state(score_url, **kwargs) |
28
|
|
|
except TooManyRedirects: |
29
|
|
|
msg = '可能是成绩接口地址不对,请尝试更改use_api值' |
30
|
|
|
raise ScoreException(self.school_code, msg) |
31
|
|
|
except RequestException: |
32
|
|
|
msg = '获取成绩请求参数失败' |
33
|
|
|
raise ScoreException(self.school_code, msg) |
34
|
|
|
|
35
|
|
|
payload = { |
36
|
|
|
'__VIEWSTATE': view_state, |
37
|
|
|
'Button2': '在校学习成绩查询', |
38
|
|
|
'btn_zcj': '历年成绩', |
39
|
|
|
'btnCx': ' 查 询 ', |
40
|
|
|
'ddlXN': '', |
41
|
|
|
'ddlXQ': '' |
42
|
|
|
} |
43
|
|
|
try: |
44
|
|
|
res = self._post(score_url, data=payload, **kwargs) |
45
|
|
|
except TooManyRedirects: |
46
|
|
|
raise ScoreException(self.school_code, '成绩接口已关闭') |
47
|
|
|
except RequestException: |
48
|
|
|
raise ScoreException(self.school_code, '获取成绩信息失败') |
49
|
|
|
|
50
|
|
|
tip = get_alert_tip(res.text) |
51
|
|
|
if tip: |
52
|
|
|
raise ScoreException(self.school_code, tip) |
53
|
|
|
|
54
|
|
|
return ScoreParse(self.school_code, res.text, use_api).get_score(score_year, score_term) |
55
|
|
|
|
56
|
|
|
|
57
|
|
|
class ScoreParse(): |
58
|
|
|
''' 成绩页面解析模块 ''' |
59
|
|
|
|
60
|
|
|
def __init__(self, school_code, html, use_api): |
61
|
|
|
self.school_code = school_code |
62
|
|
|
self.use_api = use_api |
63
|
|
|
self.soup = BeautifulSoup(html, "html.parser") |
64
|
|
|
self._html_parse_of_score() |
65
|
|
|
|
66
|
|
|
def _html_parse_of_score(self): |
67
|
|
|
table = self.soup.find("table", {"id": re.compile("Datagrid1", re.IGNORECASE)}) |
68
|
|
|
if not table: |
69
|
|
|
raise ScoreException(self.school_code, '获取成绩信息失败') |
70
|
|
|
|
71
|
|
|
rows = table.find_all('tr') |
72
|
|
|
rows.pop(0) |
73
|
|
|
self.score_info = {} |
74
|
|
|
for row in rows: |
75
|
|
|
cells = row.find_all("td") |
76
|
|
|
# 学年学期 |
77
|
|
|
year = cells[0].text |
78
|
|
|
term = cells[1].text |
79
|
|
|
# 课程名 |
80
|
|
|
lesson_name = cells[3].text.strip() |
81
|
|
|
credit = cells[6].text.strip() or 0 |
82
|
|
|
point = cells[7].text.strip() or 0 |
83
|
|
|
score = cells[8].text.strip() or 0 |
84
|
|
|
score_dict = { |
85
|
|
|
"lesson_name": lesson_name, |
86
|
|
|
"credit": float(credit), |
87
|
|
|
"point": float(point), |
88
|
|
|
"score": self.handle_data(score) |
89
|
|
|
} |
90
|
|
|
# 有其他成绩内容则输出 |
91
|
|
|
makeup_score = cells[10].text |
92
|
|
|
retake_score = cells[11].text |
93
|
|
|
if makeup_score != '\xa0': |
94
|
|
|
# 补考成绩 |
95
|
|
|
score_dict['bkcj'] = makeup_score |
96
|
|
|
if retake_score != '\xa0': |
97
|
|
|
# 重修成绩 |
98
|
|
|
score_dict['cxcj'] = retake_score |
99
|
|
|
# 组装数组格式的数据备用 |
100
|
|
|
self.score_info[year] = self.score_info.get(year, {}) |
101
|
|
|
self.score_info[year][term] = self.score_info[year].get(term, []) |
102
|
|
|
self.score_info[year][term].append(score_dict) |
103
|
|
|
|
104
|
|
|
def get_score(self, year, term): |
105
|
|
|
''' 返回成绩信息json格式 ''' |
106
|
|
|
try: |
107
|
|
|
if not self.score_info: |
108
|
|
|
raise KeyError |
109
|
|
|
if year: |
110
|
|
|
if term: |
111
|
|
|
return self.score_info[year][term] |
112
|
|
|
return self.score_info[year] |
113
|
|
|
except KeyError: |
114
|
|
|
raise ScoreException(self.school_code, '暂无成绩信息') |
115
|
|
|
|
116
|
|
|
return self.score_info |
117
|
|
|
|
118
|
|
|
@staticmethod |
119
|
|
|
def handle_data(data): |
120
|
|
|
try: |
121
|
|
|
return float(data) |
122
|
|
|
except ValueError: |
123
|
|
|
return data |
124
|
|
|
|