课程页面数据导入方法
plaintext
/* Blob.js
* A Blob implementation.
* 2014-07-24
*
* By Eli Grey, http://eligrey.com
* By Devin Samarin, https://github.com/dsamarin
* License: MIT
* See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md
*/
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
(function (a) { a.URL = a.URL || a.webkitURL; if (a.Blob && a.URL) { try { new Blob; return } catch (d) { } } var c = a.BlobBuilder || a.WebKitBlobBuilder || a.MozBlobBuilder || (function (p) { var g = function (z) { return Object.prototype.toString.call(z).match(/^\[object\s(.*)\]$/)[1] }, y = function m() { this.data = [] }, w = function i(B, z, A) { this.data = B; this.size = B.length; this.type = z; this.encoding = A }, q = y.prototype, v = w.prototype, s = p.FileReaderSync, e = function (z) { this.code = this[this.name = z] }, r = ("NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR").split(" "), u = r.length, l = p.URL || p.webkitURL || p, t = l.createObjectURL, f = l.revokeObjectURL, k = l, o = p.btoa, j = p.atob, h = p.ArrayBuffer, n = p.Uint8Array, x = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/; w.fake = v.fake = true; while (u--) { e.prototype[r[u]] = u + 1 } if (!l.createObjectURL) { k = p.URL = function (A) { var z = document.createElementNS("http://www.w3.org/1999/xhtml", "a"), B; z.href = A; if (!("origin" in z)) { if (z.protocol.toLowerCase() === "data:") { z.origin = null } else { B = A.match(x); z.origin = B && B[1] } } return z } } k.createObjectURL = function (A) { var B = A.type, z; if (B === null) { B = "application/octet-stream" } if (A instanceof w) { z = "data:" + B; if (A.encoding === "base64") { return z + ";base64," + A.data } else { if (A.encoding === "URI") { return z + "," + decodeURIComponent(A.data) } } if (o) { return z + ";base64," + o(A.data) } else { return z + "," + encodeURIComponent(A.data) } } else { if (t) { return t.call(l, A) } } }; k.revokeObjectURL = function (z) { if (z.substring(0, 5) !== "data:" && f) { f.call(l, z) } }; q.append = function (D) { var F = this.data; if (n && (D instanceof h || D instanceof n)) { var E = "", A = new n(D), B = 0, C = A.length; for (; B < C; B++) { E += String.fromCharCode(A[B]) } F.push(E) } else { if (g(D) === "Blob" || g(D) === "File") { if (s) { var z = new s; F.push(z.readAsBinaryString(D)) } else { throw new e("NOT_READABLE_ERR") } } else { if (D instanceof w) { if (D.encoding === "base64" && j) { F.push(j(D.data)) } else { if (D.encoding === "URI") { F.push(decodeURIComponent(D.data)) } else { if (D.encoding === "raw") { F.push(D.data) } } } } else { if (typeof D !== "string") { D += "" } F.push(unescape(encodeURIComponent(D))) } } } }; q.getBlob = function (z) { if (!arguments.length) { z = null } return new w(this.data.join(""), z, "raw") }; q.toString = function () { return "[object BlobBuilder]" }; v.slice = function (C, z, B) { var A = arguments.length; if (A < 3) { B = null } return new w(this.data.slice(C, A > 1 ? z : this.data.length), B, this.encoding) }; v.toString = function () { return "[object Blob]" }; v.close = function () { this.size = 0; delete this.data }; return y }(a)); a.Blob = function (j, h) { var l = h ? (h.type || "") : ""; var g = new c(); if (j) { for (var k = 0, e = j.length; k < e; k++) { if (Uint8Array && j[k] instanceof Uint8Array) { g.append(j[k].buffer) } else { g.append(j[k]) } } } var f = g.getBlob(l); if (!f.slice && f.webkitSlice) { f.slice = f.webkitSlice } return f }; var b = Object.getPrototypeOf || function (e) { return e.__proto__ }; a.Blob.prototype = b(new a.Blob()) }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
/* FileSaver
* A saveAs() FileSaver implementation.
* 1.3.2
* 2016-06-16 18:25:19
*
* By Eli Grey, http://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
var saveAs = saveAs || function (e) { "use strict"; if (typeof e === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { return } var t = e.document, n = function () { return e.URL || e.webkitURL || e }, r = t.createElementNS("http://www.w3.org/1999/xhtml", "a"), o = "download" in r, a = function (e) { var t = new MouseEvent("click"); e.dispatchEvent(t) }, i = /constructor/i.test(e.HTMLElement) || e.safari, f = /CriOS\/[\d]+/.test(navigator.userAgent), u = function (t) { (e.setImmediate || e.setTimeout)(function () { throw t }, 0) }, s = "application/octet-stream", d = 1e3 * 40, c = function (e) { var t = function () { if (typeof e === "string") { n().revokeObjectURL(e) } else { e.remove() } }; setTimeout(t, d) }, l = function (e, t, n) { t = [].concat(t); var r = t.length; while (r--) { var o = e["on" + t[r]]; if (typeof o === "function") { try { o.call(e, n || e) } catch (a) { u(a) } } } }, p = function (e) { if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)) { return new Blob([String.fromCharCode(65279), e], { type: e.type }) } return e }, v = function (t, u, d) { if (!d) { t = p(t) } var v = this, w = t.type, m = w === s, y, h = function () { l(v, "writestart progress write writeend".split(" ")) }, S = function () { if ((f || m && i) && e.FileReader) { var r = new FileReader; r.onloadend = function () { var t = f ? r.result : r.result.replace(/^data:[^;]*;/, "data:attachment/file;"); var n = e.open(t, "_blank"); if (!n) e.location.href = t; t = undefined; v.readyState = v.DONE; h() }; r.readAsDataURL(t); v.readyState = v.INIT; return } if (!y) { y = n().createObjectURL(t) } if (m) { e.location.href = y } else { var o = e.open(y, "_blank"); if (!o) { e.location.href = y } } v.readyState = v.DONE; h(); c(y) }; v.readyState = v.INIT; if (o) { y = n().createObjectURL(t); setTimeout(function () { r.href = y; r.download = u; a(r); h(); c(y); v.readyState = v.DONE }); return } S() }, w = v.prototype, m = function (e, t, n) { return new v(e, t || e.name || "download", n) }; if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { return function (e, t, n) { t = t || e.name || "download"; if (!n) { e = p(e) } return navigator.msSaveOrOpenBlob(e, t) } } w.abort = function () { }; w.readyState = w.INIT = 0; w.WRITING = 1; w.DONE = 2; w.error = w.onwritestart = w.onprogress = w.onwrite = w.onabort = w.onerror = w.onwriteend = null; return m }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content); if (typeof module !== "undefined" && module.exports) { module.exports.saveAs = saveAs } else if (typeof define !== "undefined" && define !== null && define.amd !== null) { define("FileSaver.js", function () { return saveAs }) }
/* icsFormatter
* A file generator
* By eastpiger
*/
var fileGenerator = function () {
'use strict';
if (navigator.userAgent.indexOf('MSIE') > -1 && navigator.userAgent.indexOf('MSIE 10') == -1) {
console.log('Unsupported Browser');
return;
}
return {
/**
* Download calendar using the saveAs function from filesave.js
* @param {string} filename Filename
* @param {string} ext Extention
*/
'download': function (filename, ext) {
var courseJson = JSON.stringify(rawdata);
if (courseJson) {
var targetStr = "index.js');"
var afterIndex = courseJson.indexOf(targetStr)
if (afterIndex != -1) {
courseJson = courseJson.substring(afterIndex + targetStr.length)
}
ext = (typeof ext !== 'undefined') ? ext : '.html';
filename = (typeof filename !== 'undefined') ? filename : '教务';
var blob = new Blob([courseJson], { type: 'attachment/csv;charset=utf-8' });
saveAs(blob, filename + ext);
} else return false;
}
};
};
var VER = "0.6";
var rawdata = undefined;
var mode = undefined;
function check_page_allow() {
try {
rawdata = $($("#frmright")[0].contentDocument).find("#div-table tbody")[0];
mode = "grad";
return true;
}
catch (error) {
console.error(error);
}
try {
rawdata = table0;
mode = "eams";
if (window.hasOwnProperty("unitCount")) {
rawdata.unitCount = unitCount;
}
return true;
}
catch (error) {
console.error(error);
}
try {
rawdata = window.table0;
if (window.hasOwnProperty("unitCount")) {
rawdata.unitCount = unitCount;
}
if (typeof rawdata !== 'undefined') {
mode = "eams";
return true;
}
}
catch (error) {
console.error(error);
}
var ifrs = $("iframe");
if (ifrs.length > 0) {
for (var i = 0; i < ifrs.length; i++) {
try {
rawdata = ifrs[i].contentWindow.table0;
if (ifrs[i].contentWindow.hasOwnProperty("unitCount")) {
rawdata.unitCount = ifrs[i].contentWindow.unitCount;
}
if (typeof rawdata !== 'undefined') {
mode = "eams";
return true;
}
} catch (error) {
console.error(error);
}
}
}
}
if (Boolean(window.$) && check_page_allow()) {
var semester = "";
try {
if (mode == "eams") {
semester = $("#courseTableForm input.calendar-text")[0].value;
} else if (mode == "grad") {
semester = $($($("#frmright")[0].contentDocument).find("#div-table div")[0]).html();
}
} catch (error) {
var semester = "教务";
}
window.icsObj = fileGenerator();
window.icsObj.download(semester);
if (mode == "eams")
setTimeout("alert('文件' + semester + '.html' + '已经保存到默认的下载文件夹中,请按照教程完成后续操作。————WakeUp课程表')", 500)
else if (mode == "grad")
alert("这种类型还没有支持哦,请到App关于页面联系开发者适配")
} else {
alert('加载失败,请确认是否进入正确页面!');
}
将下载后的html文件通过py处理为json
plaintext
import json
import re
import os
import datetime
# 1. 【在此处指定文件路径】
TARGET_FILE = "py/课程/1.html"
# 基础时间表 (Key: 节次, Value: (开始时间, 结束时间))
# 脚本会根据这个时间表,自动合并连续的课程时间
TIME_SLOTS = {
1: ("08:00", "08:45"),
2: ("08:55", "09:40"),
3: ("10:15", "11:00"),
4: ("11:10", "11:55"),
5: ("14:00", "14:45"),
6: ("14:55", "15:40"),
7: ("16:10", "16:55"),
8: ("17:05", "17:50"),
9: ("19:00", "19:45"),
10:("19:55", "20:40")
}
def calculate_semester_start(current_date_str, current_week):
"""根据当前日期和当前周次,推算学期开始日期(第一周的周一)"""
try:
curr_date = datetime.datetime.strptime(current_date_str, "%Y-%m-%d")
except ValueError:
print("日期格式错误,使用默认今天。")
curr_date = datetime.datetime.now()
# 计算当前是周几 (0=周一, 6=周日)
weekday_idx = curr_date.weekday()
# 找到本周的周一
this_week_monday = curr_date - datetime.timedelta(days=weekday_idx)
# 倒推到第一周的周一
semester_start = this_week_monday - datetime.timedelta(days=(current_week - 1) * 7)
return semester_start.strftime("%Y-%m-%d")
def clean_course_name(name):
"""清洗课程名称,去除末尾的 ID (xxxx)"""
pattern = r'[\(\(][A-Za-z0-9\.\-_]+[\)\)]$'
return re.sub(pattern, '', name).strip()
def extract_json_from_html(content):
"""从HTML中提取JSON"""
try:
return json.loads(content)
except:
pass
anchor = "unitCounts"
start = content.find(anchor)
if start == -1: return None
# 向左找 {
bracket_start = content.rfind('{', 0, start + len(anchor))
if bracket_start == -1: return None
# 向右匹配 }
count = 0
for i in range(bracket_start, len(content)):
if content[i] == '{': count += 1
elif content[i] == '}': count -= 1
if count == 0:
try:
return json.loads(content[bracket_start:i+1])
except:
return None
return None
def parse_weeks(week_str):
"""二进制转周次数组"""
return [i for i, c in enumerate(week_str) if c == '1']
def process_schedule(file_path, current_date, current_week):
print(f"🚀 正在读取文件: {file_path}")
# 1. 读取文件 (尝试多种编码)
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
except UnicodeDecodeError:
try:
with open(file_path, 'r', encoding='gb18030') as f:
content = f.read()
except Exception as e:
print(f"❌ 文件读取失败: {e}")
return
data = extract_json_from_html(content)
if not data:
print("❌ 解析失败:无法从文件中提取有效的 JSON 数据。")
return
# 2. 提取数据
unit_count = data.get('unitCount', 10)
activities = data.get('activities', [])
raw_courses = []
for index, slot_list in enumerate(activities):
if not slot_list: continue
day_idx = (index // unit_count) + 1
slot_idx = (index % unit_count) + 1
for item in slot_list:
raw_courses.append({
"title": clean_course_name(item.get('courseName', '')),
"location": item.get('roomName', ''),
"day": day_idx,
"slot": slot_idx,
"weeks": parse_weeks(item.get('vaildWeeks', '')),
"raw_weeks": item.get('vaildWeeks', ''),
"teacher": item.get('teacherName', '')
})
# 3. 合并相邻课程
raw_courses.sort(key=lambda x: (x['day'], x['location'], x['title'], x['slot']))
merged_courses = []
if raw_courses:
current_course = raw_courses[0]
s_time, e_time = TIME_SLOTS.get(current_course['slot'], ("00:00", "00:00"))
current_course['startTime'] = s_time
current_course['endTime'] = e_time
for i in range(1, len(raw_courses)):
next_course = raw_courses[i]
is_same_class = (
next_course['day'] == current_course['day'] and
next_course['title'] == current_course['title'] and
next_course['location'] == current_course['location'] and
next_course['raw_weeks'] == current_course['raw_weeks'] and
next_course['slot'] == current_course['slot'] + 1
)
if is_same_class:
_, new_end = TIME_SLOTS.get(next_course['slot'], ("00:00", "00:00"))
current_course['endTime'] = new_end
current_course['slot'] = next_course['slot']
else:
merged_courses.append(current_course)
current_course = next_course
s_time, e_time = TIME_SLOTS.get(current_course['slot'], ("00:00", "00:00"))
current_course['startTime'] = s_time
current_course['endTime'] = e_time
merged_courses.append(current_course)
# 4. 构建输出
semester_start = calculate_semester_start(current_date, current_week)
final_output = {
"config": {
"semesterStart": semester_start,
"totalWeeks": 17 # 课程总周数
},
"courses": []
}
for c in merged_courses:
# 将每一门课按照指定键值对填入
final_output["courses"].append({
"title": c['title'],
"location": c['location'],
"day": c['day'],
"startTime": c['startTime'],
"endTime": c['endTime'],
"weeks": c['weeks']
})
# 5. 导出 JSON
output_filename = "py/课程/schedule_final.json"
with open(output_filename, 'w', encoding='utf-8') as f:
# ensure_ascii=False 保证中文正常显示
# indent=2 保证缩进美观
json.dump(final_output, f, ensure_ascii=False, indent=2)
print(f"\n✅ 处理完成!")
print(f"📅 开学日期: {semester_start} (基于当前是第 {current_week} 周)")
print(f"📄 结果已保存: {os.path.abspath(output_filename)}")
if __name__ == "__main__":
print("--- 课程表转换工具 (输出 JSON 包含 config 和 courses) ---")
if not os.path.exists(TARGET_FILE):
print(f"❌ 错误:在配置区域指定的文件 '{TARGET_FILE}' 不存在。")
print(" 请在代码顶部修改 TARGET_FILE 变量。")
exit()
# 获取时间信息用于计算 semesterStart
today = datetime.datetime.now().strftime("%Y-%m-%d")
print(f"处理目标文件: {TARGET_FILE}")
date_input = input(f"请输入当前日期 (格式 YYYY-MM-DD,默认 {today}): ").strip()
if not date_input:
date_input = today
week_input = input("请输入当前是第几周 (数字,例如 17): ").strip()
if not week_input:
current_week = 1
print(">> 未输入周次,默认为第 1 周。")
else:
current_week = int(week_input)
process_schedule(TARGET_FILE, date_input, current_week)
可以控制台通过导入json或手动编辑进行数据导入
其余系统可参考wakeup文档
评论
隐私政策
0/500
滚动到此处加载评论...