公x课视频播放
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import random
课程ID范围
START_ID = 1406
END_ID = 1449
STAY_TIME = 500 # 每个页面停留时间(秒)
CHECK_INTERVAL = 5 # 检查间隔(秒)
def try_skip_completed_video(driver):
"""尝试快进视频到99%,检测是否已学习完成"""
try:
videos = driver.find_elements(By.TAG_NAME, "video")
if not videos:
return False # 没有视频,无法判断
print("尝试快进视频到99%位置...")
for i, video in enumerate(videos):
try:
# 首先获取视频时长
video_info = driver.execute_script("""
var video = arguments[0];
if(video.readyState >= 2 && video.duration > 0) {
return {
duration: video.duration,
currentTime: video.currentTime,
canSeek: true
};
}
return null;
""", video)
if video_info and video_info['duration'] > 0:
duration = video_info['duration']
target_time = duration * 0.99 # 99%位置
print(f"视频 {i+1}: 总时长 {duration:.0f}秒,尝试快进到 {target_time:.0f}秒")
# 尝试快进到99%位置
success = driver.execute_script("""
var video = arguments[0];
var targetTime = arguments[1];
try {
video.currentTime = targetTime;
return true;
} catch(e) {
return false;
}
""", video, target_time)
if success:
# 等待1秒让视频跳转
time.sleep(1)
# 检查当前时间是否在99%附近
new_info = driver.execute_script("""
var video = arguments[0];
var targetTime = arguments[1];
var current = video.currentTime;
var diff = Math.abs(current - targetTime);
return {
currentTime: current,
diff: diff,
isClose: diff <= 3 // 允许3秒误差
};
""", video, target_time)
if new_info and new_info['isClose']:
print(f"✓ 视频 {i+1} 可快进到99%,当前时间: {new_info['currentTime']:.1f}秒")
return True
else:
print(f"✗ 视频 {i+1} 无法快进到99%,当前时间: {new_info['currentTime']:.1f}秒")
return False
else:
print(f"✗ 视频 {i+1} 快进失败,可能无法快进")
return False
except Exception as e:
print(f"处理视频 {i+1} 时出错: {e}")
continue
return False
except Exception as e:
print(f"尝试快进视频时出错: {e}")
return False
def check_if_video_can_be_skipped(driver):
"""检查视频是否可以跳过(已学习完成)"""
先尝试快进检测
if try_skip_completed_video(driver):
return True
# 如果快进检测失败,再用传统方法检测完成状态
return check_completion_traditional(driver)
def check_completion_traditional(driver):
"""传统的完成状态检测"""
try:
等待页面加载
time.sleep(3)
# 1. 检查常见的完成状态标识
completion_indicators = [
# 文本标识
("已完成", "span,div,p,h1,h2,h3,h4,strong"),
("100%", "span,div,p"),
("已学完", "span,div,p"),
("完成学习", "span,div,p"),
("学习完成", "span,div,p"),
("已完成学习", "span,div,p"),
("100%", "span,div,p"),
# 进度条
("progress-bar", "[class*='progress']"),
("progress", "[class*='progress']"),
("bar", "[class*='bar']"),
]
for keyword, selector in completion_indicators:
try:
if "%" in keyword or "progress" in keyword or "bar" in keyword:
# 检查进度条是否显示100%
elements = driver.find_elements(By.CSS_SELECTOR, selector)
for element in elements:
try:
if element.is_displayed():
text = element.text.strip()
style = element.get_attribute("style") or element.get_attribute("class") or ""
# 检查文本中是否包含100%
if "100%" in text or "100%" in style:
print(f"检测到100%进度: {text[:30]}...")
return True
# 检查宽度是否为100%
if "width: 100%" in style or "width:100%" in style or "width:100%" in style.replace(" ", ""):
print("检测到宽度100%的进度条")
return True
except:
continue
else:
# 检查文本标识
elements = driver.find_elements(By.CSS_SELECTOR, f"{selector}")
for element in elements:
try:
if element.is_displayed() and keyword in element.text:
print(f"检测到完成标识: '{keyword}'")
return True
except:
continue
except:
continue
# 2. 检查是否有完成图标
icon_selectors = [
"[class*='complete']",
"[class*='finished']",
"[class*='success']",
"[class*='checked']",
"[class*='icon-success']",
"[class*='icon-check']",
"[class*='icon-complete']",
]
for selector in icon_selectors:
try:
elements = driver.find_elements(By.CSS_SELECTOR, selector)
for element in elements:
try:
if element.is_displayed():
print(f"检测到完成图标: {selector}")
return True
except:
continue
except:
continue
# 3. 检查按钮状态
button_texts = ["重新学习", "再次学习", "重新观看", "复习", "已学完", "已完成", "继续学习"]
for text in button_texts:
try:
all_buttons = driver.find_elements(By.TAG_NAME, "button")
for btn in all_buttons:
try:
btn_text = btn.text.strip()
if text in btn_text and btn.is_displayed():
print(f"检测到按钮文本: '{text}'")
return True
except:
continue
except:
continue
return False
except Exception as e:
print(f"检查完成状态时出错: {e}")
return False
def switch_to_window_with_timeout(driver, window_handle, timeout=5):
"""安全切换到指定窗口"""
try:
start_time = time.time()
while time.time() - start_time < timeout:
if window_handle in driver.window_handles:
try:
driver.switch_to.window(window_handle)
return True
except:
time.sleep(0.5)
else:
time.sleep(0.5)
return False
except:
return False
def open_new_tab_safely(driver, url):
"""安全打开新标签页"""
try:
获取当前窗口句柄
current_handle = driver.current_window_handle
# 在新标签页打开
driver.execute_script(f"window.open('{url}');")
# 等待新窗口打开
start_time = time.time()
while time.time() - start_time < 10: # 最多等待10秒
if len(driver.window_handles) > 1:
# 找到新窗口句柄
new_handles = [h for h in driver.window_handles if h != current_handle]
if new_handles:
new_handle = new_handles[-1]
try:
driver.switch_to.window(new_handle)
# 等待页面开始加载
time.sleep(3)
return new_handle, True
except:
pass
time.sleep(0.5)
return None, False
except Exception as e:
print(f"打开新标签页失败: {e}")
return None, False
def handle_quiz_modal(driver, quiz_count):
"""处理答题弹窗"""
try:
查找所有弹窗
modal_selectors = [
".modal",
"[class*='modal']",
"[class*='dialog']",
".el-dialog",
".ant-modal",
"[role='dialog']"
]
for selector in modal_selectors:
try:
modals = driver.find_elements(By.CSS_SELECTOR, selector)
for modal in modals:
try:
if modal.is_displayed():
print("检测到答题弹窗,尝试自动答题...")
# 查找选项
options = modal.find_elements(By.CSS_SELECTOR,
"input[type='radio'], "
"[type='radio'], "
"[class*='radio'], "
"[class*='option']")
if options and len(options) >= 2:
# 先尝试A选项
try:
options[0].click()
print("已选择A选项")
time.sleep(1)
except:
pass
# 查找提交按钮
buttons = modal.find_elements(By.TAG_NAME, "button")
for btn in buttons:
try:
if btn.is_displayed() and ("提交" in btn.text or "确定" in btn.text or "确认" in btn.text or "下一步" in btn.text):
btn.click()
quiz_count += 1
print(f"已提交答案(A),第{quiz_count}个答题")
time.sleep(3)
return quiz_count
except:
continue
# 如果上面没找到,再尝试B选项
try:
if len(options) >= 2:
options[1].click()
print("已选择B选项")
time.sleep(1)
for btn in buttons:
try:
if btn.is_displayed() and ("提交" in btn.text or "确定" in btn.text or "确认" in btn.text):
btn.click()
print("已提交答案(B)")
time.sleep(3)
return quiz_count
except:
continue
except:
pass
except:
continue
except:
continue
except Exception as e:
print(f"处理答题弹窗时发生异常: {e}")
return quiz_count
def check_and_handle_quiz(driver, quiz_count):
"""检查并处理答题弹窗"""
try:
检查是否有任何弹窗
modals = driver.find_elements(By.CSS_SELECTOR, "div.modal, div[class*='dialog'], div[role='dialog']")
for modal in modals:
try:
if modal.is_displayed():
modal_text = modal.text[:50]
# 如果是答题相关弹窗
if any(keyword in modal_text for keyword in ["请选择", "单选题", "多选题", "判断题", "选择", "选项", "答案"]):
print("发现可能的答题弹窗")
quiz_count = handle_quiz_modal(driver, quiz_count)
return quiz_count
except:
continue
except:
pass
return quiz_count
def close_current_tab(driver, current_handle):
"""安全关闭当前标签页"""
try:
处理可能的alert弹窗
try:
alert = driver.switch_to.alert
alert.accept()
time.sleep(1)
except:
pass
# 如果有弹窗,尝试点击任何关闭按钮
try:
close_buttons = driver.find_elements(By.CSS_SELECTOR,
"[class*='close'], "
"[aria-label*='关闭'], "
"button.close, "
"[class*='cancel']")
for btn in close_buttons:
try:
if btn.is_displayed() and btn.is_enabled():
btn.click()
time.sleep(1)
except:
continue
except:
pass
# 尝试关闭当前窗口
try:
if current_handle in driver.window_handles:
driver.switch_to.window(current_handle)
driver.close()
time.sleep(1)
except:
pass
except Exception as e:
print(f"关闭标签页时出错: {e}")
def ensure_video_playing(driver):
"""确保视频在播放"""
try:
videos = driver.find_elements(By.TAG_NAME, "video")
if videos:
for video in videos:
try:
is_playing = driver.execute_script("""
var video = arguments[0];
return !video.paused && video.currentTime > 0;
""", video)
if not is_playing:
driver.execute_script("""
var video = arguments[0];
if(video.paused && video.readyState >= 2) {
video.muted = true;
video.play().catch(e => {});
return true;
}
return false;
""", video)
except:
continue
except:
pass
def check_video_progress(driver):
"""检查视频播放进度和状态"""
try:
videos = driver.find_elements(By.TAG_NAME, "video")
if not videos:
return None, 0, False
all_videos_info = []
for i, video in enumerate(videos):
try:
video_info = driver.execute_script("""
var video = arguments[0];
if(video.readyState >= 2) {
return {
index: arguments[1],
ended: video.ended,
currentTime: video.currentTime,
duration: video.duration,
paused: video.paused,
readyState: video.readyState,
loop: video.loop
};
}
return null;
""", video, i)
if video_info:
all_videos_info.append(video_info)
except:
continue
if not all_videos_info:
return None, 0, False
# 计算总进度
total_duration = 0
total_current_time = 0
all_ended = True
for info in all_videos_info:
if info['duration'] > 0:
total_duration += info['duration']
total_current_time += info['currentTime']
if not info['ended']:
all_ended = False
if total_duration > 0:
overall_progress = (total_current_time / total_duration) * 100
else:
overall_progress = 0
return all_videos_info, overall_progress, all_ended
except Exception as e:
print(f"检查视频进度时出错: {e}")
return None, 0, False
def check_for_next_section_button(driver):
"""检查是否有下一节/下一章/完成按钮"""
try:
可能的下一节按钮文本
next_button_texts = ["下一节", "下一章", "下一个", "继续", "完成", "下一课", "下一视频", "继续学习", "进入下一节"]
# 查找按钮
all_buttons = driver.find_elements(By.TAG_NAME, "button")
for btn in all_buttons:
try:
btn_text = btn.text.strip()
for text in next_button_texts:
if text in btn_text and btn.is_displayed() and btn.is_enabled():
print(f"找到'{text}'按钮,尝试点击...")
btn.click()
time.sleep(3)
return True
except:
continue
# 查找链接
all_links = driver.find_elements(By.TAG_NAME, "a")
for link in all_links:
try:
link_text = link.text.strip()
for text in next_button_texts:
if text in link_text and link.is_displayed() and link.is_enabled():
print(f"找到'{text}'链接,尝试点击...")
link.click()
time.sleep(3)
return True
except:
continue
return False
except Exception as e:
print(f"检查下一节按钮时出错: {e}")
return False
def smart_video_playback(driver, max_wait_time):
"""智能视频播放管理"""
print(f"监控页面,最大等待 {max_wait_time} 秒...")
start_time = time.time()
quiz_count = 0
last_quiz_check = 0
video_end_count = 0
consecutive_ended = 0
last_progress = 0
stuck_count = 0
has_clicked_next = False
while time.time() - start_time < max_wait_time:
# 检查当前窗口是否仍然有效
try:
if driver.window_handles and driver.current_window_handle in driver.window_handles:
pass
else:
print("窗口已关闭,停止监控")
break
except:
print("窗口已关闭,停止监控")
break
current_time = time.time()
elapsed = current_time - start_time
# 检查视频进度
video_info, overall_progress, all_ended = check_video_progress(driver)
if video_info:
# 检查是否有视频在播放
has_playing_video = False
for info in video_info:
if not info['paused'] and not info['ended']:
has_playing_video = True
break
# 如果视频已全部结束
if all_ended:
consecutive_ended += 1
print(f"所有视频已播放完毕 ({consecutive_ended}/2)")
# 如果视频已结束,尝试点击下一节按钮
if not has_clicked_next:
print("尝试查找下一节按钮...")
if check_for_next_section_button(driver):
has_clicked_next = True
print("已点击下一节按钮,等待页面变化...")
time.sleep(5)
continue
# 如果连续2次检测到视频已结束,提前退出
if consecutive_ended >= 2:
print("视频已播放完毕,且无下一节按钮,提前结束本课程")
break
else:
consecutive_ended = 0
has_clicked_next = False
# 检查进度是否卡住
if abs(overall_progress - last_progress) < 0.1 and overall_progress > 0:
stuck_count += 1
else:
stuck_count = 0
last_progress = overall_progress
# 如果进度卡住超过30秒
if stuck_count >= 6: # 6 * 5秒 = 30秒
print("检测到视频进度卡住,尝试重新播放...")
try:
# 重新播放视频
driver.execute_script("""
var videos = document.querySelectorAll('video');
for(var v of videos) {
if(v.paused && v.readyState >= 2) {
v.muted = true;
v.play().catch(e => {});
}
}
""")
stuck_count = 0
time.sleep(3)
except:
pass
# 显示进度信息
if overall_progress > 0 and overall_progress < 100:
print(f"总进度: {overall_progress:.1f}%", end="\r")
# 每8秒检查一次答题弹窗
if current_time - last_quiz_check > 8:
quiz_count = check_and_handle_quiz(driver, quiz_count)
last_quiz_check = current_time
# 每10秒确保视频在播放
if int(elapsed) % 10 == 0:
ensure_video_playing(driver)
# 显示剩余时间
remaining = int(max_wait_time - elapsed)
if remaining > 0:
mins, secs = divmod(remaining, 60)
elapsed_mins, elapsed_secs = divmod(int(elapsed), 60)
print(f"已学: {elapsed_mins:02d}:{elapsed_secs:02d} | 剩余: {mins:02d}:{secs:02d} | 答题: {quiz_count} | 进度: {overall_progress:.1f}%", end="\r")
time.sleep(CHECK_INTERVAL)
actual_time = int(time.time() - start_time)
return quiz_count, actual_time
def main():
"""主函数"""
print("公需课学习助手")
print(f"课程范围: {START_ID} - {END_ID}")
print(f"每个课程停留: {STAY_TIME} 秒")
print("=" * 50)
# 初始化浏览器
options = webdriver.EdgeOptions()
options.add_argument("--disable-notifications")
options.add_argument("--disable-popup-blocking")
driver = webdriver.Edge(options=options)
# 设置页面加载超时
driver.set_page_load_timeout(30)
driver.set_script_timeout(30)
try:
# 打开第一个页面并登录
first_url = f"https://www.gdysxh.com/my_classes/public_course/video/course_id/{START_ID}/order_id/277288.html"
print("打开第一个课程,请手动登录...")
driver.get(first_url)
input("登录完成后按回车键继续...")
# 保存主窗口句柄
main_window = driver.current_window_handle
# 遍历所有课程
for course_id in range(START_ID, END_ID + 1):
url = f"https://www.gdysxh.com/my_classes/public_course/video/course_id/{course_id}/order_id/277288.html"
print(f"\n{'='*50}")
print(f"正在处理课程 {course_id}...")
print(f"URL: {url}")
# 安全打开新标签页
new_handle, success = open_new_tab_safely(driver, url)
if not success or not new_handle:
print(f"⚠ 无法打开课程 {course_id} 的页面,跳过")
continue
# 检查窗口是否有效
if not switch_to_window_with_timeout(driver, new_handle):
print(f"⚠ 无法切换到课程 {course_id} 的窗口,跳过")
continue
# 等待页面加载
time.sleep(3)
# 检查课程是否已完成(先尝试快进检测)
print("检查课程完成状态...")
try:
if check_if_video_can_be_skipped(driver):
print(f"✓ 课程 {course_id} 已完成(可快进),跳过")
# 关闭当前标签页
close_current_tab(driver, new_handle)
# 切换回主窗口
if not switch_to_window_with_timeout(driver, main_window):
# 如果主窗口不存在,使用第一个窗口
if driver.window_handles:
driver.switch_to.window(driver.window_handles[0])
main_window = driver.current_window_handle
continue
except Exception as e:
print(f"检查完成状态时出错: {e}")
# 继续尝试学习
print("课程未完成,开始学习...")
# 关闭初始弹窗
try:
close_buttons = driver.find_elements(By.CSS_SELECTOR,
"[class*='close'], "
"[aria-label*='关闭'], "
"button.close")
for btn in close_buttons:
try:
if btn.is_displayed():
btn.click()
time.sleep(1)
except:
continue
except:
pass
# 尝试播放视频
print("尝试播放视频...")
try:
driver.execute_script("""
var videos = document.querySelectorAll('video');
for(var v of videos) {
if(v.paused && v.readyState >= 2) {
v.muted = true;
v.play().catch(e => {});
}
}
""")
time.sleep(3)
except Exception as e:
print(f"播放视频失败: {e}")
# 继续监控页面
# 智能监控视频播放
quiz_count, actual_time = smart_video_playback(driver, STAY_TIME)
mins, secs = divmod(actual_time, 60)
print(f"\n✓ 课程 {course_id} 处理完成,用时 {mins:02d}:{secs:02d},共处理 {quiz_count} 个答题")
# 关闭当前标签页
print("关闭当前标签页...")
close_current_tab(driver, new_handle)
# 切换回主窗口
if not switch_to_window_with_timeout(driver, main_window):
# 如果主窗口不存在,使用第一个窗口
if driver.window_handles:
driver.switch_to.window(driver.window_handles[0])
main_window = driver.current_window_handle
# 短暂等待
if course_id < END_ID:
time.sleep(2)
print("\n" + "="*50)
print(f"所有课程处理完成!共 {END_ID - START_ID + 1} 个课程")
print("="*50)
except Exception as e:
print(f"\n程序出错: {e}")
import traceback
traceback.print_exc()
# 保持浏览器打开
input("按回车键关闭浏览器...")
try:
driver.quit()
except:
pass
if name == "main ":
main()