├── README.md ├── icve.py └── icveLogin.py /README.md: -------------------------------------------------------------------------------- 1 | # IcveSwipeVideo 2 | 智慧职教刷课 3 | 4 | 使用前关闭梯子 5 | 6 | 安装python 7 | 8 | 先安装依赖 完整安装python环境后 9 | 打开命令行输入: pip install requests 10 | 11 | [简单的zjy2.icve刷课(课件)小脚本](https://github.com/Cyenoch/zjy2.icve) 12 | [icveHelper](https://github.com/xinyunaha/icveHelper) 13 | 改动制作而成 14 | 15 | 16 | [Windows下载地址](https://github.com/shouzia/IcveSwipeVideo/releases/download/0.0.1/Windows-icve.exe) 17 | -------------------------------------------------------------------------------- /icve.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import random 4 | from datetime import date 5 | from icveLogin import Mooc 6 | try: 7 | from urllib3.util.retry import Retry 8 | from requests.packages import urllib3 9 | from requests.adapters import HTTPAdapter 10 | import requests as rqs # 首先安装依赖 完整安装python环境后 打开命令行输入: pip install requests 11 | except: 12 | try: 13 | retValue = os.system("pip install requests urllib3") 14 | if 1 == retValue: 15 | raise Exception("") 16 | elif 0 == retValue: 17 | os.system("python " + __file__) 18 | quit() 19 | except KeyboardInterrupt: 20 | quit() 21 | except Exception: 22 | print("\n请在完整安装python环境后,打开命令行输入: pip install requests urllib3") 23 | print("pip install requests urllib3") 24 | print("然后再运行本程序") 25 | print("Python Windows 64位下载地址: https://mirrors.huaweicloud.com/python/3.9.2/python-3.9.2rc1-amd64.exe") 26 | print("Python Windows 32位下载地址: https://mirrors.huaweicloud.com/python/3.7.2/python-3.7.2.exe") 27 | quit() 28 | 29 | sess = rqs.Session() 30 | 31 | x = Mooc() 32 | 33 | auth = x.login() # 设置为自己的auth 34 | # 谷歌(Chrome系浏览器): 浏览器上登录职教云后, 35 | # - 点击地址栏(显示网址的位置)左侧小锁 36 | # - 点击 [Cookie] 37 | # - 点击 [icve.com.cn] 38 | # - 点击 [Cookie] 39 | # - 点击 [auth] 40 | # - 双击下方内容右侧的一串字符串,全选,右键,复制 41 | # - 在本文件的第七行 [auth = ''] 的地方,将刚复制的内容粘贴在 [''] 之间 42 | 43 | tastInterval = 2 # 课件上报间隔(秒),太短会导致学习异常记录 (可能导致30分钟封禁) 44 | noteInterval = 2 # 发布note的间隔 45 | 46 | videoIncrementInterval = 5 # 视频课件上报间隔(秒),太短会导致学习异常记录 (可能导致30分钟封禁) 47 | 48 | videoIncrementBase = 14 # 视频上报进度的基本值 大于20极有可能导致学习异常记录 (可能导致30分钟封禁) 49 | 50 | # 视频上报进度的插值 随机数最大值 videoIncrementBase + videoIncrementX 不宜大于20 (可能导致30分钟封禁) 51 | videoIncrementX = 4 52 | 53 | # isSubmitComment isSubmitNote同时开启会导致 note提交失败,短时间内不能在相同课件提交评论和笔记 54 | # 选分数占比比较高的开 默认提交笔记 55 | # 已知该功能很可能导致封禁建议关闭 56 | isSubmitComment = False # 是否完成任务后提交评论 57 | isSubmitNote = False # 是否完成任务后提交笔记 58 | 59 | debug = False 60 | 61 | 62 | def debugFunc(): 63 | pass 64 | 65 | 66 | def sign(openClassId, courseOpenId, activityId, signId): 67 | """ 68 | 普通一键签到 69 | """ 70 | result = sess.post("https://security.zjy2.icve.com.cn/api/study/faceTeachInfo/stuSign", data={ 71 | 'courseOpenId': (None, courseOpenId), 72 | 'openClassId': (None, openClassId), 73 | 'activityId': (None, activityId), 74 | 'signId': (None, signId) 75 | }).json() 76 | if result['code'] == -3: 77 | return "签到已结束" 78 | else: 79 | return "签到完成" 80 | 81 | 82 | def getFaceTeachActivityInfo(openClassId, courseOpenId, activityId, type=2): 83 | """ 84 | 获取教学课堂活动详情信息 85 | 86 | Returns: 87 | [{Id, dataType(提问,签到, etc...), state, title, startDate, voteType, ...}] 88 | """ 89 | return sess.post("https://security.zjy2.icve.com.cn/api/study/faceTeachInfo/faceTeachActivityInfo", data={ 90 | 'courseOpenId': (None, courseOpenId), 91 | 'openClassId': (None, openClassId), 92 | 'activityId': (None, activityId), 93 | 'type': (None, type) 94 | }).json()['list'] 95 | 96 | 97 | def getTodayFaceTeachScheduleList(): 98 | """ 99 | 获取当天的所有课程的所有课堂列表 100 | Returns: 101 | [{Address, ClassSection, State, TeachDate, Title}] 102 | """ 103 | return sess.post("https://zjy2.icve.com.cn/api/student/faceTeachInfo/getFaceTeachSchedule", data={ 104 | 'calendar': (None, "week"), 105 | }).json()['faceTeachList'] 106 | 107 | 108 | def getTodayFaceTeachScheduleListWithClass(openClassId, courseOpenId, time=date.today().strftime('%Y-%m-%d')): 109 | """ 110 | 获取指定课程的某天的课程教学列表 111 | 112 | Returns: 113 | [{Address, ClassSection, State, TeachDate, Title}] 114 | """ 115 | calendar = "week" 116 | return sess.post("https://security.zjy2.icve.com.cn/api/study/faceTeachInfo/getFaceTeachSchedule", data={ 117 | 'courseOpenId': (None, courseOpenId), 118 | 'openClassId': (None, openClassId), 119 | 'currentTime': (None, time), 120 | 'calendar': (None, calendar), 121 | }).json()['faceTeachList'] 122 | 123 | 124 | def getCourseList(): 125 | """ 126 | 获取学习的课程的列表 127 | 128 | Retures: 129 | [{courseOpenId,openClassId}] 130 | """ 131 | return sess.get( 132 | "https://zjy2.icve.com.cn/api/student/learning/getLearnningCourseList").json()['courseList'] 133 | 134 | 135 | def getProcessList(courseOpenId, openClassId): 136 | """ 137 | 获取课件列表 138 | 139 | Retures: 140 | (moduleId, [{id}]) 141 | """ 142 | r = sess.post( 143 | "https://zjy2.icve.com.cn/api/study/process/getProcessList", data={ 144 | "courseOpenId": (None, courseOpenId), 145 | "openClassId": (None, openClassId) 146 | }) 147 | json = r.json() 148 | return (json['progress']['moduleId'], json['progress']['moduleList']) 149 | 150 | 151 | def getTopicByModuleId(courseOpenId, moduleId): 152 | """ 153 | 获取模块列表 154 | 155 | Retures: 156 | [{id, name}] 157 | """ 158 | r = sess.post("https://zjy2.icve.com.cn/api/study/process/getTopicByModuleId", data={ 159 | 'courseOpenId': (None, courseOpenId), 160 | 'moduleId': (None, moduleId) 161 | }) 162 | return r.json()['topicList'] 163 | 164 | 165 | def getCellByTopicId(courseOpenId, openClassId, topicId): 166 | """ 167 | 获取模块里的任务列表 168 | 169 | Returns: 170 | [{cellName, Id, categoryName( 171 | 子节点, 图片, 视频, ppt), courseOpenId, parentId, stuCellPercent, topicId, childNodeList}] 172 | """ 173 | r = sess.post("https://zjy2.icve.com.cn/api/study/process/getCellByTopicId", data={ 174 | 'courseOpenId': (None, courseOpenId), 175 | 'openClassId': (None, openClassId), 176 | 'topicId': (None, topicId) 177 | }) 178 | return r.json()['cellList'] 179 | 180 | 181 | def checkNote(courseOpenId, openClassId, cellId): 182 | return checkComment(courseOpenId, openClassId, cellId, activityType=2) 183 | 184 | 185 | def checkComment(courseOpenId, openClassId, cellId, activityType=0): 186 | """ 187 | 检查当前用户是非已评论 188 | Params: 189 | activityType: 0评价 2笔记 190 | """ 191 | r = sess.post("https://zjy2.icve.com.cn/api/common/Directory/getCellCommentData", { 192 | "courseOpenId": courseOpenId, 193 | "openClassId": openClassId, 194 | "cellId": cellId, 195 | "type": activityType 196 | }).json() 197 | # pagination { pageIndex, pageSize, totalCount } 198 | # list [{userId}] 199 | return True 200 | 201 | 202 | def submitNote(courseOpenId, openClassId, cellId): 203 | return submitComment(courseOpenId, openClassId, cellId, activityType=2) 204 | 205 | 206 | def submitComment(courseOpenId, openClassId, cellId, content="老师讲的很好!" + str(random.randint(0, 10000)), activityType=1): 207 | """ 208 | 提交评论/笔记/问答等 209 | Params: 210 | activityType: 1评价 2笔记 211 | """ 212 | r = sess.post("https://zjy2.icve.com.cn/api/common/Directory/addCellActivity", data={ 213 | "courseOpenId": courseOpenId, 214 | "openClassId": openClassId, 215 | "cellId": cellId, 216 | "content": content, 217 | "docJson": "", 218 | "star": 5, 219 | "activityType": activityType 220 | }).json() 221 | print(r) 222 | if (r["code"] != 1): 223 | # 发布异常 224 | return False 225 | return True 226 | 227 | 228 | def doneCellTask(cell, openClassId, moduleId): 229 | """ 230 | 完成模块里的单个任务,根据任务类型调用相应方法 231 | """ 232 | cate = cell['categoryName'] # 任务类型 视频 文档 图片 ppt 子节点 233 | print("\n试图完成 {type} 类型任务: 【{name}】 ...".format( 234 | type=cate, name=cell['cellName'])) 235 | retmsg = "Unknow" # 请求返回的状态 236 | percent = 0 # 任务进度 237 | if 'stuCellPercent' in cell: 238 | percent = cell['stuCellPercent'] 239 | elif 'stuCellFourPercent' in cell: 240 | percent = cell['stuCellFourPercent'] 241 | if percent == 100: 242 | retmsg = "100%进度,任务跳过" 243 | if (percent != 100): 244 | if cate == "视频": 245 | retmsg = doneCellVideo(cell, openClassId, moduleId) 246 | elif cate == "图片": 247 | retmsg = doneCellImage(cell, openClassId, moduleId) 248 | elif cate == "ppt": 249 | retmsg = doneCellPPT(cell, openClassId, moduleId) 250 | elif cate == "文档": 251 | retmsg = doneCellDoc(cell, openClassId, moduleId) 252 | elif cate == "压缩包": 253 | retmsg = doneCellZip(cell, openClassId, moduleId) 254 | elif cate == "子节点": 255 | print("试图完成 {name} 的多个子任务...".format( 256 | type=cate, name=cell['cellName'])) 257 | for cell in cell['childNodeList']: 258 | doneCellTask(cell, openClassId, moduleId) 259 | retmsg = "操作成功!" 260 | if cate != "子节点": 261 | # 追加间隔 262 | if isSubmitComment or isSubmitNote: 263 | time.sleep(noteInterval) 264 | if isSubmitComment and submitComment(cell['courseOpenId'], openClassId, cell['Id']): 265 | print("课件评论已发布") 266 | elif isSubmitComment: 267 | print("课件笔记发布失败") 268 | # 和评论同时发布 触发连续发布 会失败 269 | if isSubmitNote and submitNote(cell['courseOpenId'], openClassId, cell['Id']): 270 | print('课件笔记已发布') 271 | elif isSubmitNote: 272 | print("课件笔记发布失败") 273 | print("任务类型:【{type}】 结果: 【{retmsg}】 任务名称: 【{name}】 等待冷却时间".format( 274 | type=cate, name=cell['cellName'], retmsg=retmsg)) 275 | if percent != 100: 276 | time.sleep(tastInterval) # 等待5s 免得被ban了 277 | return None 278 | 279 | 280 | def doneCellZip(cell, openClassId, moduleId): 281 | """ 282 | 完成压缩包类任务 这。。。点进去看一下就完成了,不需要真的下载下来 所以这里只用两个请求足矣 (未经过测试...) 283 | """ 284 | vd = viewDirectory( 285 | cell['courseOpenId'], 286 | openClassId, 287 | cell['Id'], 288 | 's', 289 | moduleId 290 | ) 291 | return stuProcessCellLog( 292 | cell['courseOpenId'], 293 | openClassId, 294 | cell['Id'], 295 | vd['guIdToken'], 296 | 0, 297 | cellLogId=vd['cellLogId'] 298 | )['msg'] 299 | 300 | 301 | def doneCellDoc(cell, openClassId, moduleId): 302 | """ 303 | 完成文档任务 请求和ppt、图片一致... 304 | """ 305 | return doneCellPPT(cell, openClassId, moduleId) 306 | 307 | 308 | def doneCellVideo(cell, openClassId, moduleId): 309 | """ 310 | 完成视频观看任务 311 | """ 312 | vd = viewDirectory(cell['courseOpenId'], openClassId, 313 | cell['Id'], 's', moduleId) 314 | audioVideoLong = vd['audioVideoLong'] # 目标观看位置 315 | guIdToken = vd['guIdToken'] 316 | studyNewlyTime = vd['stuStudyNewlyTime'] # 上次观看位置 317 | inc = studyNewlyTime # inc下次上报的进度 初始化为已有进度 318 | linc = inc # 上次上报的进度 319 | while(inc < audioVideoLong): 320 | inc += videoIncrementBase + random.random() * videoIncrementX # 设置下次上报的进度 321 | if inc >= audioVideoLong: 322 | inc = audioVideoLong 323 | print(" > 等待冷却时间") 324 | time.sleep(videoIncrementInterval) # 等待5s 免得被ban了 325 | m, s = divmod(audioVideoLong, 60) # 总时长 分秒 326 | m2, s2 = divmod(inc, 60) # 进度时长 分秒 327 | msg = stuProcessCellLog(cell['courseOpenId'], 328 | openClassId, cell['Id'], 329 | guIdToken, "{:.6f}".format(inc), 330 | cellLogId=vd['cellLogId']) 331 | if '操作成功!' not in str(msg): 332 | return "试图提交进度到" + str(inc) + "时发生错误:" + str(msg) 333 | print(" 【{cellName}】 进度上报成功,当前完成度: {p:.2f}% 视频总时长: {sc} 当前进度时长: {ssc} 跳过{jump}".format( 334 | cellName=vd['cellName'], 335 | old=inc, 336 | max=audioVideoLong, 337 | p=(inc/audioVideoLong) * 100, 338 | sc="%02d分%02d秒" % (m, s), 339 | ssc="%02d分%02d秒" % (m2, s2), 340 | jump="%02d秒" % (inc - linc) 341 | )) 342 | linc = inc 343 | return "操作完成~" 344 | 345 | 346 | def doneCellImage(cell, openClassId, moduleId): 347 | """ 348 | 完成图片查看任务,传参与ppt一样故直接返回doneCellPPT的数据 349 | """ 350 | return doneCellPPT(cell, openClassId, moduleId) 351 | 352 | 353 | def doneCellPPT(cell, openClassId, moduleId): 354 | """ 355 | 完成ppt查看任务 356 | """ 357 | vd = viewDirectory(cell['courseOpenId'], openClassId, 358 | cell['Id'], 's', moduleId) 359 | guIdToken = vd['guIdToken'] 360 | studyNewlyTime = vd['stuStudyNewlyTime'] 361 | studyNewlyPicNum = vd['pageCount'] 362 | return stuProcessCellLog(cell['courseOpenId'], openClassId, cell['Id'], guIdToken, 363 | studyNewlyTime, studyNewlyPicNum=studyNewlyPicNum, cellLogId=vd['cellLogId'], picNum=studyNewlyPicNum)['msg'] 364 | 365 | 366 | def stuProcessCellLog(courseOpenId, openClassId, cellId, token, studyNewlyTime, studyNewlyPicNum=0, cellLogId="", picNum=0): 367 | """ 368 | 调用接口查询课件进度,完成课件进度主要通过该接口 369 | """ 370 | return sess.post("https://zjy2.icve.com.cn/api/common/Directory/stuProcessCellLog", data={ 371 | 'courseOpenId': (None, courseOpenId), 372 | 'openClassId': (None, openClassId), 373 | 'cellId': (None, cellId), 374 | 'picNum': (None, picNum), 375 | 'cellLogId': (None, cellLogId), 376 | 'studyNewlyTime': (None, studyNewlyTime), 377 | 'studyNewlyPicNum': (None, studyNewlyPicNum), 378 | 'token': token, 379 | }).json() 380 | 381 | 382 | def changeStuStudyProcessCellData(courseOpenId, openClassId, moduleId, cellId, cellName): 383 | r = sess.post("https://zjy2.icve.com.cn/api/common/Directory/changeStuStudyProcessCellData", data={ 384 | 'courseOpenId': (None, courseOpenId), 385 | 'openClassId': (None, openClassId), 386 | 'cellId': (None, cellId), 387 | 'moduleId': (None, moduleId), 388 | 'cellName': (None, cellName) 389 | }) 390 | print(r.json()) 391 | if r.json()['code'] != 1: 392 | raise Exception( 393 | "changeStuStudyProcessCellData 失败 {e}".format(e=r.text)) 394 | return 395 | 396 | 397 | def viewDirectory(courseOpenId, openClassId, cellId, flag, moduleId): 398 | """ 399 | 获取课件信息,主要用与获取guIdToken和视频的总时长、ppt的总页数 400 | 401 | Returns: 402 | { 403 | audioVideoLong(视频时长), 404 | courseOpenId, 405 | courseName, 406 | openClassId, 407 | moduleId, 408 | topicId, 409 | cellId, 410 | pageCount, 411 | cellLogId, 412 | downLoadUrl, 413 | guIdToken, 414 | stuCellViewTime(共观看时间), 415 | stuStudyNewlyPicCount, 416 | stuStudyNewlyTime(观看时间) 417 | } 418 | """ 419 | r = sess.post("https://zjy2.icve.com.cn/api/common/Directory/viewDirectory", data={ 420 | 'courseOpenId': (None, courseOpenId), 421 | 'openClassId': (None, openClassId), 422 | 'cellId': (None, cellId), 423 | 'flag': (None, flag), 424 | 'moduleId': (None, moduleId) 425 | }) 426 | json = r.json() 427 | if (json['code'] == -100): 428 | changeStuStudyProcessCellData( 429 | json['currCourseOpenId'], 430 | json['currOpenClassId'], 431 | json['currModuleId'], 432 | json['curCellId'], 433 | json['currCellName']) 434 | return viewDirectory( 435 | json['currCourseOpenId'], 436 | json['currOpenClassId'], 437 | json['curCellId'], 438 | flag, 439 | json['currModuleId'] 440 | ) 441 | elif '异常学习行为' in str(r.text): 442 | raise Exception(r.text) 443 | return r.json() 444 | 445 | # DEFAULT CLI SHELL 446 | 447 | 448 | def signAllTody(): 449 | classes = getTodayFaceTeachScheduleList() 450 | if len(classes) == 0: 451 | print(" 此刻没有课程") 452 | return 453 | for _class in classes: 454 | oid = _class['openClassId'] 455 | cid = _class['courseOpenId'] 456 | aid = _class['Id'] 457 | title = _class['Title'] 458 | actions = getFaceTeachActivityInfo(oid, cid, aid) 459 | hasSigned = False 460 | for action in actions: 461 | type = action['dataType'] if 'dataType' in action else "未知" 462 | signId = action['Id'] 463 | if type == "签到": 464 | hasSigned = True 465 | print(" => %s %s %s" % (title, sign(oid, cid, aid, signId), 466 | "已签到" if action['answerCount'] > 0 else "未签到")) 467 | if not hasSigned: 468 | print(" 当前没到可签") 469 | input("按回车返回...") 470 | 471 | 472 | def courseStudy(courseList): 473 | print("|=====课程列表=====|") 474 | for course in courseList: 475 | print("{idx}: {name}".format(idx=courseList.index( 476 | course), name=course["courseName"])) 477 | 478 | print("{idx}: {name}".format( 479 | idx=len(courseList), name="【完成多个指定课程】")) 480 | print("{idx}: {name}".format( 481 | idx=len(courseList)+1, name="【完成此时所有课程的签到】")) 482 | print("{idx}: {name}".format( 483 | idx=len(courseList)+2, name="【退出】")) 484 | 485 | i = int(input("> 选择课程:(0-{max})".format(max=len(courseList) + 1))) 486 | 487 | if i == len(courseList): 488 | kcs = input("用半角\",\"分割要顺序完成的课程, 如: 1,2,4,5\n: ") 489 | for kc in kcs.split(","): 490 | course = courseList[int(kc)] 491 | courseOpenId = course['courseOpenId'] 492 | openClassId = course['openClassId'] 493 | (currentProcessModuleId, processList) = getProcessList( 494 | courseOpenId, openClassId) 495 | while(processStudy(currentProcessModuleId, processList, courseOpenId, openClassId, direct=True)): # 进入模块学习循环 496 | pass 497 | return True 498 | if i == len(courseList) + 1: 499 | signAllTody() 500 | return True 501 | if i == len(courseList) + 2: 502 | return False 503 | course = courseList[i] 504 | courseOpenId = course['courseOpenId'] 505 | openClassId = course['openClassId'] 506 | 507 | # 获取课程下的模块 508 | (currentProcessModuleId, processList) = getProcessList( 509 | courseOpenId, openClassId) 510 | 511 | while(processStudy(currentProcessModuleId, processList, courseOpenId, openClassId)): # 进入模块学习循环 512 | pass 513 | return True 514 | 515 | 516 | def processStudy(currentProcessModuleId, processList, courseOpenId, openClassId, direct=False): 517 | if direct: # 直接完成所有process 并返回False 否则死循环 518 | for process in processList: 519 | moduleId = process['id'] 520 | topicList = getTopicByModuleId(courseOpenId, moduleId) 521 | topicStudy(topicList, courseOpenId, openClassId, 522 | moduleId, directDone=True) 523 | return False 524 | print("|=====模块列表=====|") 525 | # 系统认为当前正在学习的模块位于0 526 | currentProcess = next( 527 | (x for x in processList if x['id'] == currentProcessModuleId), None) 528 | print('当前教学模块: \n0: 【{name}】'.format( 529 | name=currentProcess['name'])) 530 | for pro in processList: 531 | print("{idx}: {name}".format( 532 | idx=processList.index(pro) + 1, name=pro['name'])) 533 | print("{idx}: {name}".format( 534 | idx=len(processList) + 1, name="【刷完某模块所有内容(输入8后输入模块前的数字)】")) 535 | print("{idx}: {name}".format( 536 | idx=len(processList) + 2, name="【刷完所有模块所有内容】")) 537 | print("{idx}: {name}".format( 538 | idx=len(processList) + 3, name="【返回】")) 539 | i = int(input("> 选择模块:(0-{max})".format(max=len(processList)+3))) 540 | if i == 0: 541 | moduleId = currentProcessModuleId 542 | elif i == len(processList) + 1: 543 | ii = int(input("> 要刷完模块的数字:(0-{max})".format(max=len(processList)))) 544 | moduleId = processList[ii-1]['id'] 545 | topicList = getTopicByModuleId(courseOpenId, moduleId) 546 | topicStudy(topicList, courseOpenId, openClassId, 547 | moduleId, directDone=True) 548 | return True # TODO: 直接刷完该模块 选择模块 549 | elif i == len(processList) + 2: # 刷完所有模块的内容 550 | for process in processList: 551 | moduleId = process['id'] 552 | topicList = getTopicByModuleId(courseOpenId, moduleId) 553 | topicStudy(topicList, courseOpenId, openClassId, 554 | moduleId, directDone=True) 555 | return True 556 | elif i == len(processList) + 3: 557 | return False # 退出当前模块 558 | else: 559 | moduleId = processList[i-1]['id'] 560 | 561 | topicList = getTopicByModuleId(courseOpenId, moduleId) 562 | while(topicStudy(topicList, courseOpenId, openClassId, moduleId)): # 选择刷课循环 563 | pass 564 | return True 565 | 566 | 567 | def topicStudy(topicList, courseOpenId, openClassId, moduleId, directDone=False): 568 | if directDone: # 如果是直接完成所有模式 569 | for topic in topicList: 570 | cellList = getCellByTopicId(courseOpenId, openClassId, topic['id']) 571 | for cell in cellList: # 对课件列表的课件逐个来完成进度 572 | doneCellTask(cell, openClassId, moduleId) 573 | return False 574 | 575 | print("|=====章节列表=====|") 576 | if len(topicList) == 0: 577 | print("无内容") 578 | for topic in topicList: 579 | print("{idx}: {name}".format( 580 | idx=topicList.index(topic), name=topic['name'])) 581 | print("{idx}: {name}".format( 582 | idx=len(topicList), name="【返回】")) 583 | i = int(input("> 选择章节:(0-{max})".format(max=len(topicList)))) 584 | if i == len(topicList): 585 | return False 586 | topic = topicList[i] 587 | cellList = getCellByTopicId(courseOpenId, openClassId, topic['id']) 588 | print('该章节共有{num}个课件:'.format(num=len(cellList))) 589 | for cell in cellList: 590 | percent = 0 591 | if 'stuCellPercent' in cell: 592 | percent = cell['stuCellPercent'] 593 | elif 'stuCellFourPercent' in cell: 594 | percent = cell['stuCellFourPercent'] 595 | print( 596 | " - {name} {percent}%".format(name=cell['cellName'], percent=percent)) 597 | for cell in cellList: # 对课件列表的课件逐个来完成进度 598 | doneCellTask(cell, openClassId, moduleId) 599 | return True 600 | 601 | 602 | # 登录后的cookie 603 | sess.cookies = rqs.sessions.cookiejar_from_dict({ 604 | 'auth': auth, 605 | }) 606 | 607 | 608 | retry = Retry(connect=3, backoff_factor=0.5) 609 | adapter = HTTPAdapter(max_retries=retry) 610 | urllib3.disable_warnings(True) 611 | 612 | 613 | def cliMain(): 614 | global sess 615 | global auth 616 | sess.mount("https://", adapter) 617 | if len(auth) < 5: 618 | print("""# 谷歌(Chrome系浏览器): 浏览器上登录职教云后, 619 | # - 点击地址栏(显示网址的位置)左侧小锁 620 | # - 点击 [Cookie] 621 | # - 点击 [icve.com.cn] 622 | # - 点击 [Cookie] 623 | # - 点击 [auth] 624 | # - 双击下方内容右侧的一串字符串,全选,右键,复制 625 | # - 在本窗口右键,粘贴内容,回车""") 626 | auth = input("请输入auth:") 627 | sess.cookies = rqs.sessions.cookiejar_from_dict({ 628 | 'auth': auth 629 | }) 630 | if len(auth) < 5: 631 | raise Exception("登录信息不能为空") 632 | if debug: 633 | debugFunc() 634 | return 635 | print("获取课程列表...") 636 | courseList = getCourseList() # 637 | while(courseStudy(courseList)): # 进入课程学习循环 638 | pass 639 | 640 | 641 | if __name__ == "__main__": 642 | cliMain() 643 | -------------------------------------------------------------------------------- /icveLogin.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from PIL import Image 3 | import time 4 | import base64 5 | import random 6 | import json 7 | import sys 8 | import math 9 | import requests 10 | 11 | 12 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) ' 13 | 'Chrome/86.0.4240.75 Safari/537.36'} 14 | 15 | 16 | class Mooc: 17 | def __init__(self): 18 | self.username = input("请输入您的账号:") 19 | self.password = input("请输入您的密码:") 20 | self.verifyCode = None 21 | self.session = requests.session() 22 | self.verify() 23 | self.login() 24 | 25 | def verify(self): 26 | codeContent = self.session.get(f'https://www.icve.com.cn/portal/VerifyCode/index?t={random.random()}', 27 | headers=headers).content 28 | byteIoObj = BytesIO() 29 | byteIoObj.write(codeContent) 30 | Image.open(byteIoObj).show() 31 | self.verifyCode = input('请输入验证码:') 32 | 33 | def login(self): 34 | data = {'userName': base64.b64encode(self.username.encode()), 35 | 'pwd': base64.b64encode(self.password.encode()), 36 | 'verifycode': self.verifyCode} 37 | res = self.session.post( 38 | 'https://www.icve.com.cn/portal/Register/Login_New', headers=headers, data=data).text 39 | _json = json.loads(res) 40 | if _json['code'] == 1: 41 | cookie = str(requests.utils.dict_from_cookiejar( 42 | self.session.cookies)) 43 | cookie1 = cookie.split('\'')[3] 44 | print('登录成功') 45 | return cookie1 46 | else: 47 | print(_json['msg']) 48 | sys.exit(-1) 49 | --------------------------------------------------------------------------------