Computer Science,  tools

自动刷课程序DEMO V1 基于慕课

程序内容

  • 程序概述

前置操作:脚本使用前置环境
此程序主要是通过 Selenium 自动化浏览器,模拟登录并观看在线视频的过程。包括了一些自动化操作,比如获取视频信息、控制视频播放、暂停以及跳转到下一个视频等
目前程序在B站,A站和mooc网测试使用,在mooc网使用时需要调用登录函数,其他两个不用

  • 程序目标

自动登录到一个视频网站(例如慕课网或B站),然后模拟观看视频。通过获取视频的状态,监控视频的播放、暂停、进度等信息,并在视频结束后自动切换到下一个视频。

  • 程序结构与主要功能
  • 配置登录信息:
    指定登录页面的URL和目标课程的视频页面URL。需要登陆的页面可以需要输入用户名(username)和密码(password),但目前仅适配mooc网。
  • 浏览器配置 (setup_browser):
    使用 Selenium WebDriver(Chrome)设置浏览器选项,并打开指定的课程视频页面。还可以配置为无头模式(即不显示浏览器界面)。
  • 登录功能 (login_with_selenium):
    使用 Selenium 自动填写用户名和密码,提交登录表单。此部分功能目前被注释掉,仅作为mooc网功能使用。
  • 视频播放监控与模拟观看 (simulate_video_watch):
    这个函数不断监控当前视频的状态(播放、暂停、播放进度等),并且在视频结束后,自动跳转到下一个视频,直到播放所有视频或出现异常。
  • 视频信息获取 (get_video_info):
    使用 JavaScript 通过 Selenium 获取视频元素的时长、当前播放时间、暂停状态、结束状态和视频地址。此信息用于判断视频的播放进度。
  • 按钮查找功能 (seach_video_button):
    通过 XPath 查找播放、暂停、下一个视频等按钮。程序根据页面中的不同元素进行匹配。这个函数有两个主要类型的按钮:播放按钮和下一个按钮。
  • 按钮确认与点击功能:
    confirm_play_button: 确认视频播放按钮并点击,确保视频能正常播放。
    confirm_next_button: 确认下一个按钮并点击,切换到下一个视频。
  • 自动播放检测 (is_auto_play_next):
    通过检测视频是否发生变化来判断是否启用了自动播放功能。自动播放检测帮助程序确认是否继续进行下一个视频的播放。
  • 按钮点击 (click_button):
    通过执行 JavaScript,模拟点击指定的按钮(例如播放、暂停或跳过按钮)。
  • 程序流程
  1. 初始化和浏览器设置
    程序首先设置 Selenium 的浏览器并打开目标视频页面。
  2. 登录操作
    如果需要,程序会自动登录指定的网站账号。
  3. 确认播放按钮
    程序尝试通过查找页面上的播放按钮,确认视频是否已准备好播放,并点击播放按钮(如果未播放)。
  4. 模拟观看视频
    1. 程序获取视频的状态(例如当前播放时间、是否暂停等),并每隔10秒检查一次视频的状态。
    2. 如果视频暂停,则自动恢复播放。
    3. 如果视频播放完毕,则继续查找下一个视频并进行播放。
  5. 自动跳转到下一个视频
    程序检测视频是否结束,并在结束后自动点击“下一个”按钮,跳转到下一个视频。
  6. 统计数据
    程序统计已观看视频的数量和总时长,并输出到控制台。
  • 后续改进计划 To Be Continue
  • 2025.1.15
    • 错误处理:完善错误处理,增强鲁棒性,调整参数如页面加载时间的延迟等。
    • 提高自动化:登录部分有注释仅适用于mooc网,且浏览器默认是可视化的(显示界面),后续完善测试无头模式和自动登录可行性,提高效率,避免干扰。
    • 并发支持:目前程序是单线程的,适用于逐个视频播放。如果需要,可以通过多线程或并发来提高效率,自动化播放多个视频。
  • 平台支持
    1. 慕课网IMOOC
    2. Bilibili
    3. Acfun
  • 更新日志
  • 2025.1.15 更新初版功能 V1.0
  • 2025.1.20 V1.0.1:修复bug:部分网站拦截javascript执行点击按钮的操作,还原回Selenium的模拟点击操作

程序代码

import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
import time

# 配置登录信息
username = "xxxx"  # 替换为实际的用户名
password = "xxxx"  # 替换为实际的密码
login_url = "https://www.imooc.com/login"  # 登录页面 URL
course_url = "https://www.bilibili.com/video/BV1sJcJeREHv/?spm_id_from=333.1007.tianma.1-1-1.click"  # 课程视频页面 URL

# 是否启用 requests.Session 优化(True 使用 Session,False 使用正常 Selenium 登录)
#USE_SESSION = True


#统计数据
VIDEO_COUNT = 0
VIDEO_TIME = 0
VIDEO_SRC = None

#按钮/发现存在页面不同,元素不同的可能性,因此只有每次使用再去遍历
BUTTON_PLAY = None  #播放按钮
BUTTON_PAUSE = None #暂停按钮
BUTTON_NEXT = None  #下一个按钮
auto_play_next = None #自动播放下一个视频检测




# 使用 Selenium 执行正常的浏览器登录
def login_with_selenium(driver):

    # 找到用户名、密码输入框并输入相应的账号和密码
    username_input = driver.find_element(By.NAME, "email")  # 假设用户名字段的 name 为 'email'
    password_input = driver.find_element(By.NAME, "password")  # 假设密码字段的 name 为 'password'

    # 输入用户名和密码
    username_input.send_keys(username)
    password_input.send_keys(password)

    # 提交表单(按 Enter 键)
    password_input.send_keys(Keys.RETURN)

    # 等待页面加载,直到课程视频页面加载完成
    time.sleep(5)

    return driver

# 设置 Selenium 浏览器
def setup_browser():
    options = webdriver.ChromeOptions()
    options.add_argument("--disable-blink-features=AutomationControlled")
    # options.add_argument('--headless')  # 无头模式(可选,不显示浏览器界面)
    options.add_argument("--start-maximized")
    options.add_argument('--disable-gpu')  # 禁用GPU加速(可选)
    options.add_argument("--no-sandbox")
    options.add_argument("user-agent=xxxx") 查找并输入自己的浏览器代理
    driver = webdriver.Chrome(options=options)

    # 打开登录页面
    driver.get(course_url)
    time.sleep(2)  # 等待页面加载
    print(f"等待完毕")
    return driver


# 模拟观看视频
def simulate_video_watch(driver):
    video_info = get_video_info(driver)
    global VIDEO_COUNT,VIDEO_TIME,VIDEO_SRC
    if video_info is not None:
        if VIDEO_SRC != video_info['src']:
            VIDEO_SRC = video_info['src']
            VIDEO_COUNT += 1
            VIDEO_TIME += video_info['duration']
            print(f"播放视频{VIDEO_COUNT},视频时长:{video_info['duration']} 秒")
            while not video_info['ended'] and VIDEO_SRC == video_info['src']:
                if video_info['paused']:
                    print(f"WARNING:检测到视频暂停")
                    #click_play_button(driver)
                    click_button(driver,BUTTON_PLAY)
                if video_info['duration'] > 0:
                    VIDEO_PROGRESS = round((video_info['currentTime'] / video_info['duration']) * 100, 2)
                else:
                    VIDEO_PROGRESS = 0
                print(f"check:")
                print(f"      当前播放时间:{round(video_info['currentTime'], 2)}秒")
                print(f"      当前视频进度:{VIDEO_PROGRESS}%")
                time.sleep(10)  # 模拟观看视频,每10s检查视频状态
                video_info = get_video_info(driver)
            print(f"播放完毕")
            print(f"********************************")
            time.sleep(2)
            if not is_auto_play_next(driver):
                if not confirm_next_button(driver):
                    print("无可进行操作,无法进行下一步")
                    return 
            time.sleep(5)
            simulate_video_watch(driver)
                    
        else:
            while VIDEO_SRC == video_info['src']:
                print("等待2s")
                time.sleep(2)
                video_info = get_video_info(driver)
            simulate_video_watch(driver)


#获取视频元素的信息
def get_video_info(driver):
    try:
    # 执行 JavaScript 获取视频元素的时长
        video_info = driver.execute_script("""
            var video = document.querySelector('video');  // 获取页面中的第一个 <video> 标签
            if (video) {
                return {
                    duration: video.duration,  // 视频时长(单位:秒)
                    currentTime: video.currentTime,  // 当前播放时间(单位:秒)
                    paused: video.paused,  // 是否暂停
                    ended: video.ended,  // 是否播放完毕
                    src: video.src    //视频地址
                };
            } else {
                return null;  // 如果没有找到视频元素,返回 null
            }
        """)

        if video_info:
            return video_info
        else:
            print("未能找到视频元素")
            return None
    except Exception as e:
        print(f"获取视频信息失败:{e}")
        return None


#查找播放按钮
def seach_video_button(driver, Type):
    print(f"查找")
    try:
        # 查找按钮
        if Type == 'play':
            play_buttons = WebDriverWait(driver, 10).until(
            EC.presence_of_all_elements_located(
                (By.XPATH,
                    "//button[contains(translate(text(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'PLAY')] | "
                    "//button[contains(translate(@aria-label, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'PLAY')] | "
                    "//button[contains(translate(@data-action, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'PLAY')] | "
                    "//button[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'PLAY')] | "
                    "//i[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'PLAY')] | "
                    "//div[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'PLAY')] | "
                    "//span[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'PLAY')]"
                 )
                )
            )
        if Type == 'next':
            play_buttons = WebDriverWait(driver, 10).until(
            EC.presence_of_all_elements_located(
                (By.XPATH,
                    "//button[contains(translate(text(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'NEXT')] | "
                    "//button[contains(translate(@aria-label, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'NEXT')] | "
                    "//button[contains(translate(@data-action, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'NEXT')] | "
                    "//button[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'NEXT')] | "
                    "//i[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'NEXT')] | "
                    "//div[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'NEXT')] | "
                    "//span[contains(translate(@class, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'NEXT')]"
                 )
                )
            )
            
        if not play_buttons:
            print("没有找到符合条件的按钮")
            return False
        else:
            # 输出所有找到的按钮
            print(f"共找到 {len(play_buttons)} 个按钮")
            return play_buttons
    except Exception as e:
        print(f"无法找到按钮")
        return False

#确认真正的播放按钮
def confirm_play_button(driver):
    buttons = seach_video_button(driver, 'play')
    if not buttons:
        return
    video_info = get_video_info(driver)
    if video_info['ended'] or video_info is None:
        return
    global BUTTON_PLAY,BUTTON_PAUSE
    VIDEO_STATUS = video_info['paused']
    index = 0
    while index < len(buttons):
        button = buttons[index]
        if BUTTON_PAUSE is not None and BUTTON_PLAY is not None:
            break
        click_button(driver,button)
        video_info = get_video_info(driver)
        if VIDEO_STATUS != video_info['paused']:
            print(f"找到有效按钮!")
            time.sleep(2)
            VIDEO_STATUS = video_info['paused']
            if video_info['paused']:
                BUTTON_PAUSE = button
                print(f"已找到暂停按钮")
            else:
                BUTTON_PLAY = button
                print(f"已找到播放按钮")
        else:
            index += 1
        time.sleep(1)
    print(f"检索完毕")


#确认真正的下一个按钮
def confirm_next_button(driver):
    global BUTTON_NEXT
    if BUTTON_NEXT is not None:
        click_button(driver,BUTTON_NEXT)
        time.sleep(1)
        video_info = get_video_info(driver)
        if VIDEO_SRC == video_info['src']:
            print(f"原下一步按钮失效,尝试重新获取")
        else:
            return True
    buttons = seach_video_button(driver, 'next')
    if not buttons:
        print(f"无有效button")
        return False
    video_info = get_video_info(driver)
    if video_info is None:
        print(f"无视频信息")
        return False
    for button in buttons:
        click_button(driver,button)
        time.sleep(1)
        video_info = get_video_info(driver)
        if VIDEO_SRC != video_info['src']:
            print(f"找到有效按钮!")
            BUTTON_NEXT = button
            return True
    print(f"检索完毕")
    return False


#查找是否自动播放
def is_auto_play_next(driver):
    global auto_play_next
    if auto_play_next is None:
        time.sleep(15)
        video_info = get_video_info(driver)
        if VIDEO_SRC != video_info['src']:
            print(f"检测到自动播放!")
            auto_play_next = True
        else:
            print(f"未检测到自动播放!")
            auto_play_next = False
    return auto_play_next


#点击按钮
def click_button(driver,button):
    try:
        #driver.execute_script("arguments[0].click();", button)
        button.click()
    except Exception as e:
        print("error:无法点击按钮")


# 主程序
def main():
    driver = setup_browser()
    # 登录
    #login_with_selenium(driver)
    confirm_play_button(driver)
    if BUTTON_PLAY is not None and BUTTON_PAUSE is not None:
        simulate_video_watch(driver)
    # 完成操作后退出
    print(f"===============================")
    print(f"播放结束,共计播放视频个数:{VIDEO_COUNT} 总视频时间:{VIDEO_TIME} 秒")
    #driver.quit()

# 执行主程序
if __name__ == "__main__":
    main()

一条评论

留言

您的邮箱地址不会被公开。 必填项已用 * 标注