赞
踩
优化了进度条判定逻辑, 当右侧圆环进度为100%时判定为已完成
播放时自动设置为1.5倍速并切换流畅画质
此次更新应当能解决"切换小节时提示课程已完成"的Bug, 建议更新此版本 !
智慧树视频课辅助工具,开启挂机摸鱼时代~
新学期必备干货, 建议收藏备用 !!
项目主页:CXRunfree/Autovisor(github.com) (不妨点个star吧)
这是一个可无人监督的自动化程序,由Python和JavaScript编写而成。相对于油猴脚本,本程序可有效防止被网页检测。
核心原理是使浏览器模拟用户的点击操作, 不会导致封号等问题
可以快速登录
自动播放和切换下一集
跳过弹窗和弹出的题目
自动静音、调整1.5倍速和流畅画质
检测视频是否暂停并续播 (不用担心视频意外暂停了~)
检测当前学习进度并后台实时更新
根据当前时间自动设置背景颜色(白昼/暗夜)
1.请确保系统为windows10及以上
2.文件夹内有 account.json文件(可能没显示.json后缀名),请用文本编辑器打开;
3.填写配置文件
注意:
此脚本仅支持共享课视频, 网址格式与需下面一致, 填入时请看仔细。
只能使用英文标点。
4.运行程序,会自动打开浏览器界面,滑块验证时请稍等片刻,进入网课界面后就可以自动刷课啦~ (~ ^-^ ~)
注: 登录界面的滑块验证请手动完成。
发行版下载:
Github: Releases · CXRunfree/Autovisor (github.com) (留下一个免费的 star吧?)
网盘备用: [蓝奏云] Autovisor-for-windows 密码:492l
为便于阅读, 源码已放在文末
如有疑问,可以在评论区留言, 每条留言作者都会认真看的 !
(报错问题请附上报错信息,在log.txt文件内)
1.为什么会出现一个命令行黑框?
2.为什么网页一片空白/无法加载课程界面,一段时间后程序就退出了?
大概率你没有在account文件里填入课程的网址;
此外从登录完成后到进入课程界面的过程不需要鼠标点击
3.为什么运行程序只出现后台却没出现浏览器界面?
已知Bug:
若出现其他异常崩溃,请在Github提交issue并附上日志文件log.txt的信息;
碎碎念:
觉得体验还不错? 来给项目发电支持一下吧~!
(其实作者也要吃饭的 ^-^)
注意:本程序只可用于学习和研究计算机原理(你懂的)
还等什么? 快开始愉快的刷课吧~ !
- # encoding=utf-8
- import asyncio
- import os
- import re
- import traceback
- import json
- import time
- from json import JSONDecodeError
- from playwright.sync_api import sync_playwright
- from playwright._impl._errors import TargetClosedError, TimeoutError
-
- # constants
- login_url = "https://passport.zhihuishu.com/login"
- # Xpath
- option1 = '//*[@id="playTopic-dialog"]/div/div[2]/div/div[1]/div/div/div[2]/ul/li[1]/div[2]'
- option2 = '//*[@id="playTopic-dialog"]/div/div[2]/div/div[1]/div/div/div[2]/ul/li[2]/div[2]'
- # javascript
- # 登录
- login_js = '''document.getElementsByClassName("wall-sub-btn")[0].click();'''
- block_js = '''return document.getElementsByClassName("yidun_jigsaw")[0].src'''
- bg_js = '''return document.getElementsByClassName("yidun_bg-img")[0].src'''
- # 弹窗
- pop_js = '''document.getElementsByClassName("iconfont iconguanbi")[0].click();'''
- # pop2_js = '''document.evaluate('//*[@id="app"]/div/div[1]/div[1]/span/a',document).iterateNext().click();'''
- # 其他
- night_js = '''document.getElementsByClassName("Patternbtn-div")[0].click()'''
-
-
- def auto_login(_user, _pwd):
- if not user or not pwd:
- raise UserWarning
- page.goto(login_url)
- page.locator('#lUsername').fill(_user)
- page.locator('#lPassword').fill(_pwd)
- page.wait_for_timeout(500)
- page.evaluate(login_js)
-
-
- def init_page():
- # 启动自带浏览器
- if driver == "Chrome":
- print("正在启动Chrome浏览器...")
- browser = p.chromium.launch(channel="chrome", headless=False)
- else:
- print("正在启动Edge浏览器...")
- browser = p.chromium.launch(channel="msedge", headless=False)
- context = browser.new_context()
- page = context.new_page()
- # 设置程序超时时限
- page.set_default_timeout(300 * 1000 * 1000)
- # 设置浏览器视口大小
- viewsize = page.evaluate('''() => {
- return {width: window.screen.availWidth,height: window.screen.availHeight};}''')
- viewsize["height"] -= 50
- page.set_viewport_size(viewsize)
- return page
-
-
- def optimize_page():
- # 关闭学习须知
- page.evaluate(pop_js)
- # 根据当前时间切换夜间模式
- hour = time.localtime().tm_hour
- if hour >= 18 or hour < 7:
- page.wait_for_selector(".Patternbtn-div")
- page.evaluate(night_js)
- try:
- # 关闭上方横幅
- page.wait_for_selector(".exploreTip", timeout=500)
- page.query_selector('a:has-text("不再提示")').click()
- finally:
- return
-
-
- def get_lesson_name():
- title_ele = page.wait_for_selector("#lessonOrder")
- page.wait_for_timeout(500)
- title_ = title_ele.get_attribute("title")
- return title_
-
-
- def move_mouse(elem):
- elem.hover()
- pos = elem.bounding_box()
- # 计算移动的目标位置
- target_x = pos['x'] + 30
- target_y = pos['y'] + 30
- page.mouse.move(target_x, target_y)
-
-
- def get_progress():
- curt = "0%"
- canvas = page.wait_for_selector(".videoArea")
- move_mouse(canvas)
- progress = page.query_selector(".current_play").query_selector(".progress-num")
- if not progress:
- finish = page.query_selector(".current_play").query_selector(".time_icofinish")
- if finish:
- curt = "100%"
- else:
- curt = progress.text_content()
- return curt
-
-
- def check_play():
- canvas = page.wait_for_selector(".videoArea")
- move_mouse(canvas)
- canvas.click()
-
-
- def video_optimize():
- canvas = page.wait_for_selector(".videoArea")
- move_mouse(canvas)
- page.wait_for_selector(".volumeBox").click() # 设置静音
- page.wait_for_selector(".definiBox").hover() # 切换流畅画质
- low_quality = page.query_selector(".line1bq")
- low_quality.hover()
- low_quality.click()
- page.wait_for_selector(".speedBox").hover() # 切换1.5倍速
- max_speed = page.query_selector(".speedTab15")
- max_speed.hover()
- max_speed.click()
-
-
- def play_next():
- canvas = page.wait_for_selector(".videoArea")
- move_mouse(canvas)
- next_but = page.wait_for_selector("#nextBtn")
- page.wait_for_timeout(200)
- next_but.click()
-
-
- def skip_question():
- try:
- page.wait_for_selector(".topic-item", timeout=2000)
- choices = page.query_selector_all(".topic-item")
- choices[0].click()
- choices[1].click()
- page.wait_for_timeout(500)
- page.query_selector_all(".btn")[3].click()
- except TimeoutError:
- return
-
-
- def main_function():
- # 进行登录
- print("等待登录完成...")
- auto_login(user, pwd)
- # 等待完成滑块验证,已设置5min等待时间
- page.wait_for_selector(".wall-main", state="hidden")
- # 遍历所有课程,加载网页
- for course_url in urls:
- id_pat = re.compile("recruitAndCourseId=[a-zA-Z0-9]+")
- matched = re.findall(id_pat, course_url)
- if not matched:
- print(f"\"{course_url.strip()}\"\n不是一个有效网址,即将自动跳过!")
- continue
- print("开始加载播放页...")
- page.goto(course_url)
- page.wait_for_selector(".studytime-div")
- # 关闭弹窗,优化页面体验
- optimize_page()
- # 获取当前课程名
- course_title = page.wait_for_selector(".source-name").text_content()
- print(f"当前课程:<<{course_title}>>")
- start_time = time.time() # 记录开始学习时间
- while True:
- # 获取课程小节名
- title = get_lesson_name()
- print("正在学习:%s" % title)
- # 根据进度条判断播放状态
- curtime = get_progress()
- check_play() # 开始播放
- video_optimize() # 对播放页进行初始化配置
- page.set_default_timeout(2000)
- while curtime != "100%":
- try:
- skip_question() # 跳过中途弹题(只支持选择题)
- playBut = page.query_selector_all(".pauseButton")
- curtime = get_progress()
- if not playBut and curtime != "100%":
- check_play()
- print("当前小节未刷满,将继续播放..")
- title = get_lesson_name()
- print("正在学习:%s" % title)
- else:
- print('完成进度:%s' % curtime)
- page.wait_for_timeout(2000)
- except TimeoutError:
- input("进度获取超时,可能存在安全验证?\n按Enter继续:")
- page.set_default_timeout(300 * 1000 * 1000)
- title = get_lesson_name()
- play_next() # 进度100%时开始下一集
- time_period = (time.time() - start_time) / 60
- if time_period >= 1: # 每完成一节提示一次时间
- print("本次课程已学习:%.1f min" % time_period)
- # 如果当前小节是最后一节代表课程学习完毕
- all_class = page.query_selector_all(".clearfix.video")
- class_name = all_class[-1].get_attribute('class')
- if "current_play" in class_name:
- print("已学完本课程全部内容!")
- print("==" * 10)
- break
- else: # 否则为完成当前课程的一个小节
- print(f"\"{title}\" Done !")
- page.wait_for_timeout(1000)
-
-
- if __name__ == "__main__":
- print("===== Runtime Log =====")
- try:
- print("正在载入数据...")
- with open("account.json", "r", encoding="utf-8") as f:
- account = json.loads(f.read())
- user = account["User"].strip()
- pwd = account["Password"].strip()
- driver = account["Driver"].strip()
- urls = account["Url"]
- if not isinstance(urls, list):
- print('[Error]"Url"项格式错误!')
- raise KeyError
- with sync_playwright() as p:
- page = init_page()
- main_function()
- print("==" * 10)
- print("所有课程学习完毕!")
- input()
- except Exception as e:
- if isinstance(e, JSONDecodeError):
- print("[Error]account文件内容有误!")
- elif isinstance(e, KeyError):
- print("[Error]可能是account文件的配置出错!")
- elif isinstance(e, UserWarning):
- print("[Error]是不是忘记填账号密码了?")
- elif isinstance(e, FileNotFoundError):
- print("[Error]程序缺失依赖文件,请重新安装程序!")
- elif isinstance(e, TargetClosedError):
- print("[Error]糟糕,网页关闭了!")
- elif isinstance(e, TimeoutError):
- print("[Error]网页长时间无响应,自动退出...")
- else:
- print(f"[Error]{e}")
- with open("log.txt", "w", encoding="utf-8") as doc:
- doc.write(traceback.format_exc())
- print("错误日志已保存至:log.txt")
- print("系统出错,要不重启一下?")
- input()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。