1
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
2
|
|
|
from __future__ import absolute_import, unicode_literals
|
3
|
|
|
|
4
|
|
|
import six.moves.urllib.parse as urlparse
|
5
|
|
|
|
6
|
|
|
from bs4 import BeautifulSoup
|
7
|
|
|
from requests import RequestException
|
8
|
|
|
|
9
|
|
|
from school_api.client.api.base import BaseSchoolApi
|
10
|
|
|
from school_api.client.api.utils.schedule_parse import ScheduleParse
|
11
|
|
|
from school_api.client.utils import ScheduleType
|
12
|
|
|
from school_api.exceptions import ScheduleException
|
13
|
|
|
|
14
|
|
|
|
15
|
|
|
class PlaceSchedule(BaseSchoolApi):
|
|
|
|
|
16
|
|
|
schedule_url = None
|
17
|
|
|
payload = None
|
18
|
|
|
|
19
|
|
|
def get_schedule(self,
|
|
|
|
|
20
|
|
|
campus_list=None,
|
21
|
|
|
building_list=None,
|
22
|
|
|
classroom_type_list=None,
|
23
|
|
|
classroom_name_list=None,
|
24
|
|
|
filter_campus_list=None,
|
25
|
|
|
filter_building_list=None,
|
26
|
|
|
filter_classroom_type_list=None,
|
27
|
|
|
**kwargs):
|
28
|
|
|
''' 课表信息 获取入口
|
29
|
|
|
返回一个生成器,生成器一旦报错则将退出:https://codeday.me/bug/20180125/124136.html
|
30
|
|
|
除了解析异常其他报错均不抛出
|
31
|
|
|
'''
|
32
|
|
|
self.schedule_url = self.school_url['PLACE_SCHEDULE_URL'] + \
|
33
|
|
|
urlparse.quote(self.account.encode('gb2312'))
|
34
|
|
|
|
35
|
|
|
if not self._update_payload(**kwargs):
|
|
|
|
|
36
|
|
|
yield {'error': "获取教学场地课表失败"}
|
37
|
|
|
else:
|
38
|
|
|
# 遍历校区
|
39
|
|
|
for campus in self.payload['campus_list']:
|
40
|
|
|
if self._is_skip(campus["name"], campus_list, filter_name_list=filter_campus_list):
|
41
|
|
|
continue
|
42
|
|
|
if not self._update_payload(campus, **kwargs):
|
43
|
|
|
continue
|
44
|
|
|
|
45
|
|
|
# 遍历楼号
|
46
|
|
|
for building in self.payload['building_list']:
|
47
|
|
|
kwargs['building'] = building
|
48
|
|
|
if self._is_skip(building["name"], building_list, filter_name_list=filter_building_list):
|
|
|
|
|
49
|
|
|
continue
|
50
|
|
|
if not self._update_payload(campus, **kwargs):
|
51
|
|
|
continue
|
52
|
|
|
|
53
|
|
|
# 遍历教室类别
|
54
|
|
|
for classroom_type in self.payload['classroom_type_list']:
|
55
|
|
|
kwargs['classroom_type'] = classroom_type
|
56
|
|
|
if self._is_skip(classroom_type["name"], classroom_type_list, filter_name_list=filter_classroom_type_list):
|
|
|
|
|
57
|
|
|
continue
|
58
|
|
|
if not self._update_payload(campus, **kwargs):
|
59
|
|
|
continue
|
60
|
|
|
# 遍历教室名称
|
61
|
|
|
for classroom_name in self.payload['classroom_name_list']:
|
62
|
|
|
kwargs['classroom_name'] = classroom_name
|
63
|
|
|
if self._is_skip(classroom_name["name"], classroom_name_list):
|
64
|
|
|
continue
|
65
|
|
|
try:
|
66
|
|
|
res = self._get_api(campus, **kwargs)
|
67
|
|
|
except ScheduleException:
|
68
|
|
|
continue
|
69
|
|
|
|
70
|
|
|
schedule = ScheduleParse(
|
71
|
|
|
res.content.decode('GB18030'),
|
72
|
|
|
self.time_list,
|
73
|
|
|
ScheduleType.CLASS
|
74
|
|
|
).get_schedule_dict()
|
75
|
|
|
|
76
|
|
|
data = {
|
77
|
|
|
"campus": campus["name"],
|
78
|
|
|
"building": building["name"],
|
79
|
|
|
"classroom_type": classroom_type["name"],
|
80
|
|
|
"classroom_name": classroom_name["name"]
|
81
|
|
|
}
|
82
|
|
|
data.update(schedule)
|
83
|
|
|
yield data
|
84
|
|
|
|
85
|
|
|
def _is_skip(self, name, name_list, filter_name_list=None):
|
|
|
|
|
86
|
|
|
if (name_list and name not in name_list) or (filter_name_list and name in filter_name_list):
|
87
|
|
|
return True
|
88
|
|
|
|
89
|
|
|
def _get_api(self, campus=None, **kwargs):
|
90
|
|
|
""" 请求函数 """
|
91
|
|
|
if self.payload and campus:
|
92
|
|
|
building = kwargs.pop('building', None)
|
93
|
|
|
classroom_type = kwargs.pop('classroom_type', None)
|
94
|
|
|
classroom_name = kwargs.pop('classroom_name', None)
|
95
|
|
|
data = {
|
96
|
|
|
"Button1": "",
|
97
|
|
|
"xq": self.payload['schedule_term'],
|
98
|
|
|
"xn": self.payload['schedule_year'],
|
99
|
|
|
"ddlXq": campus["value"],
|
100
|
|
|
'ddllh': building["value"] if building else '',
|
101
|
|
|
"ddlJslb": classroom_type["value"].encode('gb2312') if classroom_type else '',
|
102
|
|
|
"ddlJsmc": classroom_name["value"].encode('gb2312') if classroom_name else '',
|
103
|
|
|
"__VIEWSTATE": self.payload['view_state'],
|
104
|
|
|
}
|
105
|
|
|
_request = self._post
|
106
|
|
|
else:
|
107
|
|
|
data = ""
|
108
|
|
|
_request = self._get
|
109
|
|
|
|
110
|
|
|
try:
|
111
|
|
|
res = _request(self.schedule_url, data=data, **kwargs)
|
112
|
|
|
if res.status_code != 200:
|
113
|
|
|
raise RequestException
|
114
|
|
|
except RequestException:
|
115
|
|
|
raise ScheduleException(self.code, '获取教学场地课表失败')
|
116
|
|
|
return res
|
117
|
|
|
|
118
|
|
|
def _update_payload(self, *args, **kwargs):
|
119
|
|
|
# 更新提交参数 payload
|
120
|
|
|
try:
|
121
|
|
|
res = self._get_api(*args, **kwargs)
|
122
|
|
|
except ScheduleException:
|
123
|
|
|
return None
|
124
|
|
|
self.payload = self._get_payload(res.text)
|
125
|
|
|
return True
|
126
|
|
|
|
127
|
|
|
@staticmethod
|
128
|
|
|
def _get_payload(html):
|
|
|
|
|
129
|
|
|
pre_soup = BeautifulSoup(html, "html.parser")
|
130
|
|
|
view_state = pre_soup.find(attrs={"name": "__VIEWSTATE"})['value']
|
131
|
|
|
searchbox = pre_soup.find("div", {"class": "searchbox"})
|
132
|
|
|
|
133
|
|
|
schedule_year = searchbox.find("select", {"id": "xn"}).find(
|
134
|
|
|
"option", {"selected": "selected"}).text
|
135
|
|
|
schedule_term = searchbox.find("select", {"id": "xq"}).find(
|
136
|
|
|
"option", {"selected": "selected"}).text
|
137
|
|
|
|
138
|
|
|
campus = searchbox.find(id='ddlXq').find_all('option')
|
139
|
|
|
buildings = searchbox.find(id='ddllh').find_all('option')
|
140
|
|
|
types = searchbox.find(id='ddlJslb').find_all('option')
|
141
|
|
|
names = searchbox.find(id='ddlJsmc').find_all('option')
|
142
|
|
|
|
143
|
|
|
campus_list = [{"name": v.text, "value": v['value']} for v in campus]
|
144
|
|
|
building_list = [{"name": v.text, "value": v['value']} for v in buildings]
|
145
|
|
|
type_list = [{"name": v.text, "value": v['value']} for v in types]
|
146
|
|
|
name_list = [{"name": v.text, "value": v['value']} for v in names]
|
147
|
|
|
|
148
|
|
|
payload = {
|
149
|
|
|
'view_state': view_state,
|
150
|
|
|
'schedule_term': schedule_term,
|
151
|
|
|
'schedule_year': schedule_year,
|
152
|
|
|
'campus_list': campus_list,
|
153
|
|
|
'building_list': building_list,
|
154
|
|
|
'classroom_type_list': type_list,
|
155
|
|
|
'classroom_name_list': name_list,
|
156
|
|
|
}
|
157
|
|
|
return payload
|
158
|
|
|
|
The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:
If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.