您现在的位置是:首页 > 技术教程 正文

2023智慧树刷课脚本 基于Python selenium的自动化刷智慧树程序 [有免安装版]

admin 阅读: 2024-03-25
后台-插件-广告管理-内容页头部广告(手机)
2024/3/20 rebuild-3.9.1 更新
  • 修复点击下一集时出现"卡死/闪退"的问题.

经过测试发现是网站官方播放器的Bug, 当下一集是不含视频的假标签时,点击下一集会陷入卡死状态; 通过调整切换下一集的程序逻辑已经适配此问题。

(可能有时会出现不稳定情况, 但应该不影响正常使用)


Autovisor

智慧树视频课辅助工具,开启挂机摸鱼时代~

新学期必备干货, 建议收藏备用 !!

项目主页:CXRunfree/Autovisor(github.com) (不妨点个star吧)

程序介绍

这是一个可无人监督的自动化程序,由Python和JavaScript编写而成。相对于油猴脚本,本程序可有效防止被网页检测。

核心原理是使浏览器模拟用户的点击操作, 不会导致封号等问题

程序功能

  • 可以快速登录

  • 自动播放和切换下一集

  • 跳过弹窗和弹出的题目

  • 自动静音、调整1.5倍速和流畅画质

  • 检测视频是否暂停并续播 (不用担心视频意外暂停了~)

  • 检测当前学习进度并后台实时更新

  • 根据当前时间自动设置背景颜色(白昼/暗夜)

  • 加入了定时模拟鼠标滑动功能 (减少被检测到的概率)
  • 完成章节时将提示已刷课时长

使用须知(重要 !!)

1.请确保系统为windows10及以上

  • 默认启动Edge(win10以上系统会自带);
  • 请确保Edge或Chrome安装在系统默认位置

​2.文件夹内有 account.json文件(可能没显示.json后缀名),请用文本编辑器打开;

3.填写配置文件

  • “User”:输入你的 账户名
  • “Password”:输入你的 密码
  • “Driver”:指定启动的 浏览器(可选Chrome);
  • “Url”:输入网课的 具体网址,保存后关闭,例如:

注意:

  • 此脚本仅支持共享课视频, 网址格式与需下面一致, 填入时请看仔细;

  • 只能使用英文标点

4.运行程序,会自动打开浏览器,登录界面的滑块验证请手动完成,进入网课界面后就可以自动刷课了 !


发行版下载:

Github: Releases · CXRunfree/Autovisor (github.com) (留下一个免费的 star吧?)

网盘备用: [蓝奏云] Autovisor-for-windows  密码:492l

为便于阅读, 源码已放在文末

如有疑问,可以在评论区留言, 每条留言作者都会认真看的 !

(报错问题请附上报错信息,在log.txt文件内)

常见问题

1.为什么会出现一个命令行黑框?

  • 这是程序运行的后台,你可以查看当前运行的状态

2.为什么网页一片空白/无法加载课程界面,一段时间后程序就退出了?

  • 大概率你没有在account文件里填入课程的网址;

    此外从登录完成后到进入课程界面的过程不需要鼠标点击

3.为什么运行程序只出现后台却没出现浏览器界面?

  • 只要后台未异常退出就不必担心; 如果出错可能是你的浏览器安装路径有问题

已知Bug:

  • 长时间挂机有概率弹出人机验证, 如果1.5h内未通过验证, 程序将自动结束进程;
  • 若出现其他异常崩溃,请在Github提交issue并附上日志文件log.txt的信息;

碎碎念:

觉得体验还不错? 来给项目发电支持一下吧~! 

(其实作者也要吃饭的 ^-^)

注意:本程序只可用于学习和研究计算机原理(你懂的)

  还等什么? 快开始愉快的刷课吧~ !

源代码
  1. #encoding=utf-8
  2. import re
  3. import traceback
  4. import json
  5. import time
  6. from json import JSONDecodeError
  7. from playwright.sync_api import sync_playwright
  8. from playwright._impl._errors import TargetClosedError, TimeoutError
  9. # constants
  10. login_url = "https://passport.zhihuishu.com/login"
  11. # Xpath
  12. option1 = '//*[@id="playTopic-dialog"]/div/div[2]/div/div[1]/div/div/div[2]/ul/li[1]/div[2]'
  13. option2 = '//*[@id="playTopic-dialog"]/div/div[2]/div/div[1]/div/div/div[2]/ul/li[2]/div[2]'
  14. # javascript
  15. # 登录
  16. login_js = '''document.getElementsByClassName("wall-sub-btn")[0].click();'''
  17. block_js = '''return document.getElementsByClassName("yidun_jigsaw")[0].src'''
  18. bg_js = '''return document.getElementsByClassName("yidun_bg-img")[0].src'''
  19. # 弹窗
  20. pop_js = '''document.getElementsByClassName("iconfont iconguanbi")[0].click();'''
  21. # pop2_js = '''document.evaluate('//*[@id="app"]/div/div[1]/div[1]/span/a',document).iterateNext().click();'''
  22. # 其他
  23. night_js = '''document.getElementsByClassName("Patternbtn-div")[0].click()'''
  24. def auto_login(_user, _pwd):
  25. if not user or not pwd:
  26. raise UserWarning
  27. page.goto(login_url)
  28. page.locator('#lUsername').fill(_user)
  29. page.locator('#lPassword').fill(_pwd)
  30. page.wait_for_timeout(500)
  31. page.evaluate(login_js)
  32. def init_page():
  33. # 启动自带浏览器
  34. if driver == "Chrome":
  35. print("正在启动Chrome浏览器...")
  36. browser = p.chromium.launch(channel="chrome", headless=False)
  37. else:
  38. print("正在启动Edge浏览器...")
  39. browser = p.chromium.launch(channel="msedge", headless=False)
  40. context = browser.new_context()
  41. page = context.new_page()
  42. # 设置程序超时时限
  43. page.set_default_timeout(300 * 1000 * 1000)
  44. # 设置浏览器视口大小
  45. viewsize = page.evaluate('''() => {
  46. return {width: window.screen.availWidth,height: window.screen.availHeight};}''')
  47. viewsize["height"] -= 50
  48. page.set_viewport_size(viewsize)
  49. return page
  50. def optimize_page():
  51. # 关闭学习须知
  52. page.evaluate(pop_js)
  53. # 根据当前时间切换夜间模式
  54. hour = time.localtime().tm_hour
  55. if hour >= 18 or hour < 7:
  56. page.wait_for_selector(".Patternbtn-div")
  57. page.evaluate(night_js)
  58. try:
  59. # 关闭上方横幅
  60. page.wait_for_selector(".exploreTip", timeout=500)
  61. page.query_selector('a:has-text("不再提示")').click()
  62. finally:
  63. return
  64. def get_lesson_name():
  65. title_ele = page.wait_for_selector("#lessonOrder")
  66. page.wait_for_timeout(500)
  67. title_ = title_ele.get_attribute("title")
  68. return title_
  69. def move_mouse():
  70. page.wait_for_selector(".videoArea", state="attached", timeout=10000)
  71. elem = page.locator(".videoArea")
  72. elem.hover()
  73. pos = elem.bounding_box()
  74. if not pos:
  75. return
  76. # 计算移动的目标位置
  77. target_x = pos['x'] + 30
  78. target_y = pos['y'] + 30
  79. page.mouse.move(target_x, target_y)
  80. def get_progress():
  81. curt = "0%"
  82. move_mouse()
  83. cur_play = page.query_selector(".current_play")
  84. progress = cur_play.query_selector(".progress-num")
  85. if not progress:
  86. finish = cur_play.query_selector(".time_icofinish")
  87. if finish:
  88. curt = "100%"
  89. else:
  90. curt = progress.text_content()
  91. return curt
  92. def check_play():
  93. playing = page.query_selector(".pauseButton")
  94. if not playing:
  95. canvas = page.wait_for_selector(".videoArea", state="attached")
  96. move_mouse()
  97. canvas.click()
  98. return False
  99. else:
  100. return True
  101. def video_optimize():
  102. move_mouse()
  103. page.wait_for_selector(".volumeBox").click() # 设置静音
  104. page.wait_for_selector(".definiBox").hover() # 切换流畅画质
  105. low_quality = page.wait_for_selector(".line1bq")
  106. low_quality.hover()
  107. low_quality.click()
  108. page.wait_for_selector(".speedBox").hover() # 切换1.5倍速
  109. max_speed = page.wait_for_selector(".speedTab15")
  110. max_speed.hover()
  111. max_speed.click()
  112. def skip_question():
  113. try:
  114. page.wait_for_selector(".topic-item", timeout=2000)
  115. choices = page.locator(".topic-item").all()
  116. choices[0].click()
  117. choices[1].click()
  118. page.wait_for_timeout(500)
  119. page.locator(".btn").all()[3].click()
  120. except TimeoutError:
  121. return
  122. def main_function():
  123. # 进行登录
  124. print("等待登录完成...")
  125. auto_login(user, pwd)
  126. # 等待完成滑块验证,已设置5min等待时间
  127. page.wait_for_selector(".wall-main", state="hidden")
  128. # 遍历所有课程,加载网页
  129. for course_url in urls:
  130. id_pat = re.compile("recruitAndCourseId=[a-zA-Z0-9]+")
  131. matched = re.findall(id_pat, course_url)
  132. if not matched:
  133. print(f"\"{course_url.strip()}\"\n不是一个有效网址,即将自动跳过!")
  134. continue
  135. print("开始加载播放页...")
  136. page.set_default_timeout(90 * 60 * 1000)
  137. page.goto(course_url)
  138. page.wait_for_selector(".studytime-div")
  139. # 关闭弹窗,优化页面体验
  140. optimize_page()
  141. # 获取当前课程名
  142. course_title = page.wait_for_selector(".source-name").text_content()
  143. print(f"当前课程:<<{course_title}>>")
  144. page.set_default_timeout(5000)
  145. start_time = time.time() # 记录开始学习时间
  146. page.wait_for_selector(".clearfix.video", state="attached")
  147. all_class = page.locator(".clearfix.video").all()
  148. for each in all_class:
  149. try:
  150. page.wait_for_selector(".current_play", state="attached")
  151. each.click()
  152. page.wait_for_timeout(1000)
  153. # 获取课程小节名
  154. title = get_lesson_name()
  155. print("正在学习:%s" % title)
  156. # 根据进度条判断播放状态
  157. curtime = get_progress()
  158. if curtime != "100%":
  159. check_play() # 开始播放
  160. video_optimize() # 对播放页进行初始化配置
  161. while curtime != "100%":
  162. skip_question() # 跳过中途弹题(只支持选择题)
  163. playing = check_play()
  164. curtime = get_progress()
  165. if not playing and curtime != "100%":
  166. print("当前小节未刷满,将继续播放..")
  167. print("正在学习:%s" % title)
  168. else:
  169. print('完成进度:%s' % curtime)
  170. page.wait_for_timeout(1000)
  171. time_period = (time.time() - start_time) / 60
  172. if time_period >= 1: # 每完成一节提示一次时间
  173. print("本次课程已学习:%.1f min" % time_period)
  174. # 如果当前小节是最后一节代表课程学习完毕
  175. class_name = all_class[-1].get_attribute('class')
  176. if "current_play" in class_name:
  177. print("已学完本课程全部内容!")
  178. print("==" * 10)
  179. break
  180. else: # 否则为完成当前课程的一个小节
  181. print(f"\"{title}\" Done !")
  182. except TimeoutError:
  183. if page.query_selector(".yidun_modal__title"):
  184. print("检测到安全验证,正在等待手动完成...")
  185. page.wait_for_selector(".yidun_modal__title", state="hidden", timeout=90 * 60 * 1000)
  186. elif page.query_selector(".topic-item"):
  187. skip_question()
  188. if __name__ == "__main__":
  189. print("项目作者:CXRunfree 版权所有")
  190. print("===== Runtime Log =====")
  191. try:
  192. print("正在载入数据...")
  193. with open("account.json", "r", encoding="utf-8") as f:
  194. account = json.loads(f.read())
  195. user = account["User"].strip()
  196. pwd = account["Password"].strip()
  197. driver = account["Driver"].strip()
  198. urls = account["Url"]
  199. if not isinstance(urls, list):
  200. print('[Error]"Url"项格式错误!')
  201. raise KeyError
  202. with sync_playwright() as p:
  203. page = init_page()
  204. main_function()
  205. print("==" * 10)
  206. print("所有课程学习完毕!")
  207. input()
  208. except Exception as e:
  209. if isinstance(e, JSONDecodeError):
  210. print("[Error]account文件内容有误!")
  211. elif isinstance(e, KeyError):
  212. print("[Error]可能是account文件的配置出错!")
  213. elif isinstance(e, UserWarning):
  214. print("[Error]是不是忘记填账号密码了?")
  215. elif isinstance(e, FileNotFoundError):
  216. print("[Error]程序缺失依赖文件,请重新安装程序!")
  217. elif isinstance(e, TargetClosedError):
  218. print("[Error]糟糕,网页关闭了!")
  219. elif isinstance(e, TimeoutError):
  220. print("[Error]网页长时间无响应,自动退出...")
  221. else:
  222. print(f"[Error]{e}")
  223. with open("log.txt", "w", encoding="utf-8") as doc:
  224. doc.write(traceback.format_exc())
  225. print("错误日志已保存至:log.txt")
  226. print("系统出错,要不重启一下?")
  227. input()

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索