Spaces:
Sleeping
Sleeping
| import re | |
| import threading | |
| import time | |
| from pathlib import Path | |
| import gradio as gr | |
| import requests | |
| from tqdm import tqdm | |
| import tempfile | |
| requests.packages.urllib3.disable_warnings() | |
| reference_subject = { | |
| '语文': '01', | |
| '历史': '12', | |
| '数学': '02', | |
| '生物': '13', | |
| '英语': '03', | |
| '通用技术': '102', | |
| '信息技术': '26', | |
| '物理': '05', | |
| '政治': '27', | |
| '化学': '06', | |
| '地理':"14" | |
| } | |
| subject_codes={ | |
| '01': '语文', | |
| '12': '历史', | |
| '02': '数学', | |
| '13': '生物', | |
| '03': '英语', | |
| '102': '通用技术', | |
| '26': '信息技术', | |
| '05': '物理', | |
| '27': '政治', | |
| '06': '化学', | |
| '14':'地理' | |
| } | |
| headers = [ | |
| { | |
| "Accept": "application/json, text/plain, */*", | |
| "Accept-Encoding": "gzip, deflate, br, zstd", | |
| "Accept-Language": "zh-CN,zh;q=0.9", | |
| "Connection": "keep-alive", | |
| "Host": "www.zhixue.com", | |
| "Referer": "https://www.zhixue.com/middlehomework/web-student/views/", | |
| "Sec-Fetch-Dest": "empty", | |
| "Sec-Fetch-Mode": "cors", | |
| "Sec-Fetch-Site": "same-origin", | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36", | |
| "appName": "com.iflytek.zxzy.web.zx.stu", | |
| "sec-ch-ua": '"Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"', | |
| "sec-ch-ua-mobile": "?0", | |
| "sec-ch-ua-platform": '"Windows"' | |
| }, | |
| { | |
| "Host": "www.zhixue.com", | |
| "sucOriginAppKey": "zhixue_student", | |
| "User-Agent": "zhixue_student/1.0.2026 (iPhone; iOS 16.2; Scale/3.00)", | |
| "appName": "com.zhixue.student", | |
| "Connection": "keep-alive", | |
| "Accept-Language": "zh-Hans-CN;q=1, zh-Hant-CN;q=0.9, en-CN;q=0.8", | |
| "Accept": "*/*", | |
| "Accept-Encoding": "gzip, deflate, br" | |
| }, | |
| { | |
| "Host": "mhw.zhixue.com", | |
| "Content-Type": "application/json", | |
| "Accept": "application/json, text/plain, */*", | |
| "appName": "com.zhixue.student", | |
| "sucOriginAppKey": "zhixue_student", | |
| "Accept-Language": "zh-CN,zh-Hans;q=0.9", | |
| "Origin": "https://mhw.zhixue.com", | |
| "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)", | |
| "Referer": "https://mhw.zhixue.com/zhixuestudent/views/homeworkReport/homework-report.html", | |
| "Accept-Encoding": "gzip, deflate, br", | |
| "Connection": "keep-alive" | |
| } | |
| ] | |
| def get_token(): | |
| global token | |
| response = requests.get("https://www.zhixue.com/middleweb/newToken", headers=headers[0], verify=False) | |
| response.encoding = "utf-8" | |
| response = response.json() | |
| result = response["result"]["token"] | |
| if result: | |
| token = result | |
| return True | |
| else: | |
| print("获取 token 失败。") | |
| return False | |
| def format_time(timestamp): | |
| return time.strftime("%Y-%m-%d %H:%M", time.localtime(timestamp // 1000)) | |
| def get(url): | |
| headers[1].update({"Host": url.split("/")[2], "sucUserToken": token}) | |
| response = requests.get(url, headers=headers[1], verify=False) | |
| response.encoding = "utf-8" | |
| return response.json() | |
| def post(url, data): | |
| headers[2].update({ | |
| "Host": url.split("/")[2], | |
| "Origin": f'https://{url.split("/")[2]}', | |
| "sucUserToken": token, | |
| "Authorization": token | |
| }) | |
| response = requests.post(url, headers=headers[2], json=data, verify=False) | |
| response.encoding = "utf-8" | |
| return response.json() | |
| def parse_range(s, max_value): | |
| result = [] | |
| for item in s.split(): | |
| if "-" in item: | |
| l = item.split("-") | |
| if len(l) == 2 and l[0].isdigit() and l[1].isdigit(): | |
| begin = int(l[0]) - 1 | |
| end = int(l[1]) - 1 | |
| if not (begin < 0 and end < 0 or begin >= max_value and end >= max_value): | |
| step = -1 if begin > end else 1 | |
| for i in range(begin, end + step, step): | |
| if 0 <= i < max_value and not i in result: | |
| result.append(i) | |
| elif item.isdigit(): | |
| n = int(item) - 1 | |
| if 0 <= n < max_value and not n in result: | |
| result.append(n) | |
| return result | |
| def to_file(file, source_type, name=""): | |
| result = ({"name": name or Path(file).name, "path": file, "is_text": bool(name)} if isinstance(file, str) else | |
| {"name": name, "path": file["description"], "is_text": True} if file["fileType"] == 5 else | |
| {"name": file.get("name", "") or Path(file["path"]).name, "path": file["path"], "is_text": False}) | |
| result["name"] = re.sub('[\\\\/:*?"<>|]', "_", result["name"]) | |
| result["type"] = source_type | |
| return result | |
| def analyze_homework(homework, include_text, uid): | |
| hwId = homework["hwId"] | |
| hwType = homework["hwType"] | |
| stuHwId = homework["stuHwId"] | |
| file_list = [] | |
| data = {"base": {"appId": "APP"}, "params": {"hwId": hwId, "stuHwId": stuHwId, "studentId": uid}} | |
| if hwType == 102: | |
| response = post("https://mhw.zhixue.com/hwreport/question/getStuReportDetail", data) | |
| if "result" in response: | |
| result = response["result"] | |
| file_list.append(to_file(result["hwDescription"], "题目", result["hwTitle"] + "_说明.txt")) | |
| for problem in result["mainTopics"]: | |
| content = problem["content"] + problem["answerHtml"] + problem["analysisHtml"] | |
| file_list += [to_file(path, "题目") for path in re.findall('bigger="(.+?)"', content)] | |
| file_list += [to_file(path, "提交") for item in problem["subTopics"] for path in item["answerResList"]] | |
| elif hwType == 105: | |
| response = post("https://mhw.zhixue.com/hw/homework/attachment/list", data) | |
| file_list += [to_file(item, "题目") for item in response["result"]] | |
| response = post("https://mhw.zhixue.com/hwreport/question/getStuReportDetail", data) | |
| if "result" in response: | |
| result = response["result"] | |
| file_list.append(to_file(result["hwDescription"], "题目", result["hwTitle"] + "_说明.txt")) | |
| file_list += [to_file(item, "答案") for item in result.get("answerAttachList", [])] | |
| for problem in result["mainTopics"]: | |
| file_list += [to_file(path, "提交") for item in problem["subTopics"] for path in item["answerResList"]] | |
| elif hwType == 107: | |
| response = post("https://mhw.zhixue.com/hw/clock/answer/getClockHomeworkDetail", data) | |
| result = response["result"] | |
| file_list.append(to_file(result["description"], "题目", result["title"] + "_说明.txt")) | |
| file_list += ([to_file(item, "题目") for item in result.get("hwTopicAttachments", [])] | |
| + [to_file(item, "答案") for item in result.get("hwAnswerAttachments", [])] | |
| + [to_file(item, "答案") for item in | |
| result["hwClockRecordPreviewResponses"][0].get("teacherAnswerAttachments", [])] | |
| + [to_file(item, "提交", result["title"] + "_提交.txt") for item in | |
| result["hwClockRecordPreviewResponses"][0].get("answerAttachments", [])]) | |
| file_list = [file for file in file_list if file["path"] and (include_text or not file["is_text"])] | |
| return file_list | |
| def query_homework(uid, tlsysSessionId, subject, status, max_count): | |
| headers[0].update({"Cookie": f"tlsysSessionId={tlsysSessionId}"}) | |
| successful = get_token() | |
| if not successful: | |
| return "获取 token 失败","" | |
| page_size = max_count | |
| if subject == ["语文", "数学", "英语", "物理", "化学", "生物", "地理", "历史", "政治",'通用技术', '信息技术']: | |
| subjects = ["-1"] | |
| else: | |
| subjects = [reference_subject[i] for i in subject] | |
| if status == "全部": | |
| status = "" | |
| elif status == "已完成": | |
| status = "1" | |
| else: | |
| status = "0" | |
| fetch_list = [] | |
| if status != "1": | |
| fetch_list += [{"subject": subject, "status": 0} for subject in subjects] | |
| if status != "0": | |
| fetch_list += [{"subject": subject, "status": 1} for subject in subjects] | |
| global homework_list | |
| homework_list = [] | |
| timestamps = [int(time.time() * 1000)] * len(fetch_list) | |
| finished = [False] * len(fetch_list) | |
| while not all(finished): | |
| print("\x9B1F\x9B0J", end="") | |
| index = len(homework_list) | |
| for i in tqdm(range(len(fetch_list)), unit=""): | |
| if finished[i]: | |
| continue | |
| response = get("https://mhw.zhixue.com/homework_middle_service/stuapp/getStudentHomeWorkList" | |
| f'?completeStatus={fetch_list[i]["status"]}&createTime={timestamps[i]}&pageIndex=2' | |
| f'&pageSize={page_size}&subjectCode={fetch_list[i]["subject"]}&token={token}') | |
| if response["code"] != 200: | |
| raise RuntimeError("获取作业列表失败") | |
| result_list = response["result"]["list"] | |
| homework_list += result_list | |
| if len(result_list) < page_size: | |
| finished[i] = True | |
| if result_list: | |
| timestamps[i] = result_list[-1]["beginTime"] | |
| print("\x9B1F\x9B0J", end="") | |
| homework_list_temp=[] | |
| global homework_list_oringin | |
| homework_list_oringin = homework_list | |
| for i in range(index, len(homework_list)): | |
| begin_time = format_time(homework_list[i]["beginTime"]) | |
| end_time = format_time(homework_list[i]["endTime"]) | |
| homework_list_temp.append(f"[{homework_list[i]['subjectName']}]|{homework_list[i]['hwTitle']}|{begin_time}-{end_time}") | |
| homework_list = homework_list_temp | |
| return token, gr.update(choices=homework_list, value=[]) | |
| def parse_homework(token, include_text, homework_selection, uid): | |
| result = [] | |
| for i in homework_selection: | |
| for j in range(len(homework_list)): | |
| if i == homework_list[j]: | |
| result.append(j) | |
| selected_homework = result | |
| file_list = [] | |
| for i in tqdm(selected_homework, unit=""): | |
| file_list += analyze_homework(homework_list_oringin[i], include_text, uid) | |
| global file_list_output | |
| file_list_output = [file["name"] for file in file_list] | |
| global homework_downloaded_path | |
| homework_downloaded_path = [file["path"] for file in file_list] | |
| global homework_is_text | |
| homework_is_text = [file["is_text"] for file in file_list] | |
| return gr.update(choices=file_list_output, value=[]) | |
| def download_file(homework_view): | |
| download_list = [] | |
| for i in homework_view: | |
| for j in range(len(file_list_output)): | |
| if i == file_list_output[j]: | |
| if not homework_is_text[j]: | |
| download_list.append(homework_downloaded_path[j]) | |
| else: | |
| with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as f: | |
| f.write(homework_downloaded_path[j]) | |
| temp_path = f.name | |
| download_list.append(temp_path) | |
| return download_list | |
| def save_config(uid, tlsysSessionId): | |
| config_content = f"uid={uid}\ntlsysSessionId={tlsysSessionId}" | |
| with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as f: | |
| f.write(config_content) | |
| return f.name | |
| def load_config(file): | |
| if not file: | |
| return None, None | |
| with open(file.name, "r") as f: | |
| content = f.read() | |
| uid = re.search(r"uid=(\S+)", content) | |
| tlsysSessionId = re.search(r"tlsysSessionId=(\S+)", content) | |
| return (uid.group(1) if uid else None, (tlsysSessionId.group(1) if tlsysSessionId else None)) | |
| with gr.Blocks(title="智学网作业获取器") as demo: | |
| gr.Markdown("# 🚀 智学网作业获取器") | |
| gr.Markdown("## Backfront Created by Levrium,UI Design by Start_ten") | |
| gr.Markdown("操作说明请见https://zhuanlan.zhihu.com/p/691808543") | |
| # 新增配置文件区域 | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| config_upload = gr.File(label="上传配置文件", type="filepath", file_types=[".txt"]) | |
| with gr.Column(scale=1): | |
| load_config_btn = gr.Button("识别配置文件", variant="secondary") | |
| save_config_btn = gr.Button("导出配置文件", variant="secondary") | |
| with gr.Column(scale=2): | |
| config_download = gr.File(label="下载配置文件", interactive=False) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### 📝 查询作业") | |
| uid = gr.Textbox( | |
| label="uid", | |
| placeholder="请输入uid...", | |
| ) | |
| tlsysSessionId = gr.Textbox( | |
| label="tlsysSessionId", | |
| placeholder="请输入tlsysSessionId...", | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| subject = gr.CheckboxGroup( | |
| choices=["语文", "数学", "英语", "物理", "化学", "生物", "地理", "历史", "政治",'通用技术', '信息技术'], | |
| label="具体学科", | |
| value=["语文", "数学", "英语", "物理", "化学", "生物", "地理", "历史", "政治",'通用技术', '信息技术'] | |
| ) | |
| with gr.Column(scale=1): | |
| all_chosen = gr.Button( | |
| value="全选", | |
| variant="secondary" | |
| ) | |
| all_chosen.click( | |
| fn=lambda: ["语文", "数学", "英语", "物理", "化学", "生物", "地理", "历史", "政治",'通用技术', '信息技术'], | |
| inputs=[], | |
| outputs=[subject] | |
| ) | |
| all_clear = gr.Button( | |
| value="全不选", | |
| variant="secondary" | |
| ) | |
| all_clear.click( | |
| fn=lambda: [], | |
| inputs=[], | |
| outputs=[subject] | |
| ) | |
| status = gr.Radio( | |
| choices=["全部", "已完成", "未完成"], | |
| label="作业状态", | |
| value="全部" | |
| ) | |
| max_count = gr.Slider( | |
| label="最大请求作业数", | |
| value=10, | |
| minimum=1, | |
| maximum=50, | |
| step=1 | |
| ) | |
| with gr.Column(): | |
| token = gr.Textbox( | |
| label="TOKEN", | |
| interactive=False | |
| ) | |
| homework_selection = gr.CheckboxGroup( | |
| label="作业列表(可多选)", | |
| choices=[], | |
| interactive=True | |
| ) | |
| submit_btn = gr.Button("查询作业", variant="primary") | |
| submit_btn.click( | |
| fn=query_homework, | |
| inputs=[uid, tlsysSessionId, subject, status, max_count], | |
| outputs=[token, homework_selection ] | |
| ) | |
| gr.Markdown("---") | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### 📄 解析作业并下载") | |
| text_parse = gr.Checkbox( | |
| label="是否解析题目、提交的文本?", | |
| value=False | |
| ) | |
| homework_view = gr.CheckboxGroup( | |
| label="作业内容", | |
| choices=[], | |
| interactive=True | |
| ) | |
| submit_btn = gr.Button("解析作业", variant="primary") | |
| submit_btn.click( | |
| fn=parse_homework, | |
| inputs=[token, text_parse, homework_selection, uid], | |
| outputs=[homework_view] | |
| ) | |
| with gr.Column(): | |
| file_output = gr.File(label="作业文件", interactive=False) | |
| download_btn = gr.Button("下载作业", variant="primary") | |
| download_btn.click( | |
| fn=download_file, | |
| inputs=[homework_view], | |
| outputs=[file_output] | |
| ) | |
| # 配置文件功能绑定 | |
| load_config_btn.click( | |
| fn=load_config, | |
| inputs=[config_upload], | |
| outputs=[uid, tlsysSessionId] | |
| ) | |
| save_config_btn.click( | |
| fn=save_config, | |
| inputs=[uid, tlsysSessionId], | |
| outputs=[config_download] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |