英华学堂刷课脚本(自动化播放)
仅供参考,需要会代码
#!/usr/bin/env python # -*- coding: utf-8 -*- """ 自动刷课脚本 - 连续nodeId模式 功能:按nodeId连续递增播放视频,从起始ID播放到结束ID 视频时长固定为16分钟 """ import requests import time import json import re import sys import ddddocr # ========== 配置区域 ========== # 请在这里填写您的完整Cookie COOKIE = "token=sid.mox88nl692lGUfL62UlMiOvlmjFI4C; __root_domain_v=.leykeji.com; _qddaz=QD.226976319675740; _qdda=3-1.1; _qddab=3-ezjvz0.mo12vdsk" # 课程相关ID COURSE_ID = "1011725" CHAPTER_ID = "1095185" # 要播放的nodeId范围 START_NODE_ID = 1432744 END_NODE_ID = 1432772 # 视频时长固定为16分钟(960秒) VIDEO_DURATION = 960 # 16分钟 = 960秒 # 播放速度控制(秒/帧,数值越小越快,建议0.8-1.0) PLAY_SPEED = 0.85 # 上报间隔(秒) REPORT_INTERVAL = 30 # 请求头 HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "zh-CN,zh;q=0.9", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With": "XMLHttpRequest", "Origin": "https://cqcst.leykeji.com", "Referer": "https://cqcst.leykeji.com/user/course", "Cookie": COOKIE, } # API地址 STUDY_URL = "https://cqcst.leykeji.com/user/node/study" NODE_PAGE_URL = "https://cqcst.leykeji.com/user/node" CODE_URL = "https://cqcst.leykeji.com/service/code" class SequentialStudyBot: def __init__(self): self.session = requests.Session() self.session.headers.update(HEADERS) # 初始化验证码识别器 try: self.ocr = ddddocr.DdddOcr() print("✓ 验证码识别器加载成功") except Exception as e: print(f"⚠️ 验证码识别器加载失败: {e},将使用手动输入") self.ocr = None # 状态变量 self.study_id = 0 self.current_time = 0 self.reported_time = 0 def solve_captcha(self): """自动识别或手动输入验证码""" if not self.ocr: return input(" 请手动输入验证码: ").strip() try: # 获取验证码图片 timestamp = int(time.time() * 1000) img_url = f"{CODE_URL}?r={timestamp}" response = self.session.get(img_url, timeout=10) if response.status_code != 200: print(" 获取验证码失败") return input(" 请手动输入验证码: ").strip() # 识别验证码 captcha_text = self.ocr.classification(response.content) print(f" 自动识别验证码: {captcha_text}") if len(captcha_text) >= 3: return captcha_text else: return input(f" 识别结果'{captcha_text}'异常,请手动输入: ").strip() except Exception as e: print(f" 验证码识别失败: {e}") return input(" 请手动输入验证码: ").strip() def init_study(self, node_id): """初始化学习会话""" print(f" 初始化学习会话...") data = { "nodeId": node_id, "studyId": "0", "studyTime": "1" } try: response = self.session.post(STUDY_URL, data=data, timeout=10) result = response.json() if result.get("status"): self.study_id = result.get("studyId", 0) print(f" ✓ studyId: {self.study_id}") return True else: error_msg = result.get("msg", "未知错误") print(f" ✗ 初始化失败: {error_msg}") if result.get("need_code"): print(" 需要验证码,尝试处理...") return self.handle_captcha(node_id, 1) return False except Exception as e: print(f" ✗ 初始化异常: {e}") return False def handle_captcha(self, node_id, study_time): """处理验证码""" captcha = self.solve_captcha() if not captcha: return False data = { "nodeId": node_id, "studyId": "0", "studyTime": str(study_time), "code": captcha } try: response = self.session.post(STUDY_URL, data=data, timeout=10) result = response.json() if result.get("status"): self.study_id = result.get("studyId", 0) print(f" ✓ 验证通过,studyId: {self.study_id}") return True else: print(f" ✗ 验证失败: {result.get('msg', '')}") return False except Exception as e: print(f" ✗ 请求异常: {e}") return False def report_progress(self, node_id, study_time): """上报学习进度""" data = { "nodeId": node_id, "studyId": str(self.study_id), "studyTime": str(study_time) } try: response = self.session.post(STUDY_URL, data=data, timeout=10) result = response.json() if result.get("need_code"): print(f" ⚠️ 触发验证码,正在处理...") if self.handle_captcha(node_id, study_time): return self.report_progress(node_id, study_time) return False if result.get("status"): if result.get("studyId"): self.study_id = result.get("studyId") self.reported_time = study_time return True else: print(f" ✗ 上报失败: {result.get('msg', '未知错误')}") return False except Exception as e: print(f" ✗ 上报异常: {e}") return False def watch_video(self, node_id): """观看单个视频 - 固定16分钟""" print(f"\n{'=' * 60}") print(f"🎬 开始播放视频 - Node ID: {node_id}") # 使用固定的16分钟时长 duration = VIDEO_DURATION print(f"⏱️ 视频时长: {duration}秒 (16分钟)") print(f"{'=' * 60}") # 初始化 self.study_id = 0 self.current_time = 0 self.reported_time = 0 if not self.init_study(node_id): print(f" ✗ 初始化失败,跳过此视频") return False # 计算上报时间点 report_points = [1] point = REPORT_INTERVAL while point < duration: report_points.append(point) point += REPORT_INTERVAL if report_points[-1] != duration: report_points.append(duration) print(f" 上报计划: 共{len(report_points)}次") if len(report_points) <= 10: print(f" 上报节点: {report_points}") # 模拟播放 start_time = time.time() for target in report_points: if target > self.current_time: wait = target - self.current_time # 逐秒播放 for _ in range(wait): time.sleep(PLAY_SPEED) self.current_time += 1 if self.current_time % 15 == 0 or self.current_time == duration: elapsed = int(time.time() - start_time) prog = self.current_time / duration * 100 remaining = int((duration - self.current_time) * PLAY_SPEED) sys.stdout.write( f"\r 播放进度: {self.current_time}/{duration} ({prog:.1f}%) | 已用: {elapsed}s | 预计剩余: {remaining}s") sys.stdout.flush() print() # 上报 print(f" 📤 上报: {target}s") if not self.report_progress(node_id, target): print(f" ✗ 上报失败,停止此视频") return False if target >= duration: print(f"\n✅ 视频 {node_id} 完成!") return True return True def run(self, start_id, end_id): """批量运行""" print("\n" + "=" * 60) print("🤖 自动刷课脚本 - 连续nodeId模式") print("=" * 60) print(f"播放范围: {start_id} → {end_id}") print(f"共 {end_id - start_id + 1} 个视频") print(f"每个视频时长: 16分钟 (960秒)") print(f"播放速度: {1 / PLAY_SPEED:.2f}x") print(f"预计总耗时: {(end_id - start_id + 1) * 16 / (1 / PLAY_SPEED):.1f} 分钟") print("=" * 60) success = 0 failed = [] for node_id in range(start_id, end_id + 1): print(f"\n{'#' * 60}") print(f"进度: {success + len(failed) + 1}/{end_id - start_id + 1}") print(f"统计: 成功={success}, 失败={len(failed)}") print(f"{'#' * 60}") if self.watch_video(node_id): success += 1 print(f"✅ 成功: {node_id}") else: failed.append(node_id) print(f"❌ 失败: {node_id}") time.sleep(5) # 视频间隔 if node_id < end_id: print("\n⏸️ 等待3秒后继续下一个视频...") time.sleep(3) # 最终统计 print("\n" + "=" * 60) print("📊 刷课完成统计") print("=" * 60) print(f"总视频数: {end_id - start_id + 1}") print(f"成功: {success}") print(f"失败: {len(failed)}") if failed: print(f"失败的nodeId: {failed}") print("=" * 60) def main(): print("\n" + "=" * 60) print("自动刷课脚本启动") print("=" * 60) print("提示: 请确保Cookie有效") print("提示: 每个视频固定播放16分钟") print("=" * 60) bot = SequentialStudyBot() print(f"\n将播放 nodeId 从 {START_NODE_ID} 到 {END_NODE_ID}") print(f"共 {END_NODE_ID - START_NODE_ID + 1} 个视频") print(f"每个视频播放16分钟,总时长约 {(END_NODE_ID - START_NODE_ID + 1) * 16} 分钟") confirm = input("\n确认开始? (y/n): ").strip().lower() if confirm == 'y': try: bot.run(START_NODE_ID, END_NODE_ID) except KeyboardInterrupt: print("\n\n⚠️ 用户中断程序") except Exception as e: print(f"\n❌ 程序异常: {e}") import traceback traceback.print_exc() else: print("已取消") print("\n程序结束,按Enter键退出...") input() if __name__ == "__main__": main()