实现挂视频的三种方法

前言

需要在某学习课堂挂视频。该网站视频播放期间没有干扰,所以只要实现将课程下所有的视频片段从头走到尾即可。

  • 之前也做过一样的挂视频py脚本,使用python+selenium实现。所以起初就想使用同样的方式来实现,这是第一种方法。缺点:需要python,需要selenium webdriver。相对其它两种方法优点是啥,目前还真想不出。
  • 由于视频的播放动作和点击下一片段都是使用selenium的webdriver.execute_script()函数把js代码发送至浏览器中执行实现的,就是向页面中添加一些js代码。突然想起这样的实现方式使用油猴脚本不就完了吗?还不需要python和webdriver,只要在安装油猴的浏览器上打开该视频页面就能添加自动播放功能。这是第二种方法。缺点:需要能安装油猴的浏览器(firefox/chreom/edge)、并且安装油猴扩展、还得在油猴中新增脚本,对计算机小白确实还有点不够友好。优点:直接就在浏览器中实现功能,而且打开视频页面就自动起作用,不需要其它什么操作。
  • 受Reabble的Sent to Kindle第三种使用方式的启发,把js代码做成一个书签按钮,然后进入视频页面点击一下该书签按钮就可以了。缺点:得知道怎么设置书签按钮,并把浏览器中设置“显示收藏夹栏”。优点:只要把js代码事先做成一个书签(和IE收藏夹中网页快捷方式一样的东西,不同的叫法),然后你带到任意一个浏览器,只要点击收藏夹栏中该书签就能启用自动播放功能,使用起来方便简单。

三种方法,我比较倾向于使用第三种。因为只要记下那段js代码,不管在哪、使用什么浏览器,它都能用,并不需要安装其它软件或者扩展。

第一种方法:phthon+selenium

V0.1版实现了基本功能,然后V0.2版做了面向对象的重构。因为想到更方便的实现方法,也就没再仔细去继续优化代码。

# 通服云课堂 自动播放视频
# V0.2  :功能重构
import time

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

LOGIN_URL = 'https://www.ccspx.com.cn/login'
USER = '*******'
PWD = '*******'

driver = webdriver.Edge(executable_path='msedgedriver.exe')


class Player:
    def __init__(self, parallel_num, proceed, task_center_title='任务中心'):
        self.parallel_num = parallel_num
        self.proceed = proceed
        self.task_center_title = task_center_title
        self.tasks = []

        self.logined = False

    def run(self):
        self._login()
        self._create_tasks()
        for task in self.tasks:
            task.start()
            time.sleep(3)

        for task in self.tasks:
            print(f'{task.name}\n{task.command}\n{task.video_fragment_commands}\n----------------------')

        while self.tasks:
            time.sleep(10)
            for i in range(len(self.tasks)):
                self.tasks[i].check_video_ended()
                if self.tasks[i].completed:
                    self.tasks.remove(self.tasks[i])

    def _login(self):
        driver.get(LOGIN_URL)
        time.sleep(1)
        driver.find_element_by_id('username').send_keys(USER)
        driver.find_element_by_id('password').send_keys(PWD)
        driver.find_element_by_id('validateCode').click()

        while True:
            try:
                driver.find_element_by_id('username')
            except NoSuchElementException:
                self.logined = True
                break
            time.sleep(2)

    def _create_tasks(self):
        window = None
        while window is None:
            time.sleep(10)
            for w in driver.window_handles:
                driver.switch_to.window(w)
                if driver.title == self.task_center_title:
                    window = w

        driver.switch_to.window(window)
        time.sleep(2)
        task_commands = [a.get_attribute('onclick') for a in
                         driver.find_elements_by_xpath('//div[@id="dataList"]//li/a')]
        progresses = [span.text for span in driver.find_elements_by_xpath('//div[@id="dataList"]//li//span')]
        for c, p in zip(task_commands, progresses):
            if self.proceed:
                if p != '100%':
                    self.tasks.append(Task(command=c, parent_window=window, proceed=self.proceed))
            else:
                self.tasks.append(Task(command=c, parent_window=window, proceed=self.proceed))


class Task:
    def __init__(self, command, parent_window, proceed):
        self.command = command
        self.parent_window = parent_window
        self.proceed = proceed

        self.running = False
        self.completed = False
        self.name, self.window = None, None

        self.video_fragment_commands = []

    def start(self):
        driver.switch_to.window(self.parent_window)
        driver.execute_script(self.command)
        time.sleep(3)
        self.window = driver.window_handles[-1]
        driver.switch_to.window(self.window)
        self.name = driver.title

        self._get_video_fragments()
        self._play()

    def _get_video_fragments(self):
        driver.switch_to.window(self.window)
        fragment_commands = [li.get_attribute('onclick') for li in
                             driver.find_elements_by_xpath('//li[contains(@class, "task-item")]')]
        progresses = [sp.text for sp in driver.find_elements_by_xpath('//li[contains(@class, "task-item")]//span')][
                     2::3]
        for c, p in zip(fragment_commands, progresses):
            if self.proceed:
                if p != '100%':
                    self.video_fragment_commands.append(c)
            else:
                self.video_fragment_commands.append(c)

    def _play(self):
        if len(self.video_fragment_commands) != 0:
            driver.switch_to.window(self.window)
            driver.execute_script(self.video_fragment_commands[0])
            time.sleep(2)
            driver.execute_script('document.getElementsByTagName("video")[0].play();')
        else:
            self.completed = True
            driver.execute_script('alert("页面无视频或采集错误");')

    def check_video_ended(self):
        driver.switch_to.window(self.window)
        if driver.find_element_by_tag_name('video').get_property('ended'):
            self.video_fragment_commands.pop(0)
            if len(self.video_fragment_commands) == 0:
                self.completed = True
                driver.execute_script('alert("视频播放完毕");')
                # driver.close()
            else:
                self._play()
        else:
            driver.execute_script('document.getElementsByTagName("video")[0].play();')

    def __repr__(self):
        return f'{self.__class__.__name__}(name={self.name}, command={self.command}, ' \
               f'video_fragments={self.video_fragment_commands})'


def main():
    player = Player(parallel_num=5, proceed=False)
    player.run()


if __name__ == '__main__':
    main()

第二种方法:使用油猴扩展

思路:添加一个定时调用器 setInterval 。每隔4秒执行一次,检查视频播放是否结束(判断player.ended),以及播放下一个片段。

// ==UserScript==
// @name         通服云课堂 - 自动播放/静音
// @namespace    *
// @version      0.1
// @description  自动播放“通服云课堂”视频
// @author       czbuyi
// @match        https://www.ccspx.com.cn/course/detail*
// @grant        none
// ==/UserScript==

(function() {
    
    
    if(confirm("开始视频自动播放?")){
    
    
        setInterval(function(){
    
    
            let player = document.getElementsByTagName('video')[0];
            if(player.ended){
    
    
                let all = [].slice.call(document.getElementsByClassName('task-item'));
                let active = document.getElementsByClassName('task-item active')[0];
                let next_index = all.indexOf(active)+1;
                console.log(all.indexOf(active));
                if (next_index <= all.length-1){
    
    
                    all[next_index].click();
                }else{
    
    
                    alert("视频播放结束");
                    window.close();
                }
            };
            player.play();
            player.muted=true;
        }, 4000)
    }
    // Your code here...
})();

第三种方法:使用标签按钮

js代码与第二种方法是一样的。为了能做成标签,js代码前面需加入"javascript:"并且去掉回车,整理成一行。

javascript:(function(){
    
    if(confirm("开始视频自动播放?")){
    
    setInterval(function(){
    
    let player=document.getElementsByTagName('video')[0];if(player.ended){
    
    let all=[].slice.call(document.getElementsByClassName('task-item'));let active=document.getElementsByClassName('task-item active')[0];let next_index=all.indexOf(active)+1;if(next_index<=all.length-1){
    
    all[next_index].click();}else{
    
    alert("视频播放结束");window.close();}};player.play();player.muted=true;},4000)}})();

记录几个坑

  • 在网页中查找操作某元素或者测试js代码,之前我一直都是使用运行代码(通过python,或者html、js脚本)来检测,每次可能要涉及登录、点击至目标页面、刷新等等,浪费了非常多的时间精力。后来才发现可以在浏览器“开发者调试工具”(按F12调出来)的控制台下直接输入测试,马上就看到结果,非常直接方便。
  • 这个网站的视频播放页面里 video 元素会变化,所以每次操作都需要重新获取一下 video 元素。也因同样的问题,不能为 video 元素添加 ended 事件来处理视频片段播放完毕后的操作。
  • 为了实现连续播放,需要找到下一个片段的il元素。但是各个页面结构有些差异,只好将页面内所有带“task-item”类的il全部找出来转成数组,再查找“task-item active”类的il元素,通过检查其在数组中的位置来确定下一个片段的il。
  • js的document.getElementsBy***返回的是一个HTMLCollection对象,它类似数组,可以用for遍历,但没有indexOf的函数,不能确定某元素在对象中的位置。因为在实现播放下一下片段,我需要找到当前“task-item active”类的il,并click下一个il元素,所以只能把HTMLCollection对象转换成数组:all = [].slice.call(document.getElementsByClassName(‘task-item’))
  • html元素在<>内定义的属性可以使用getAttribute(“属性名”)来获取,比如 video元素.getAttribute(“scr”) 。但video元素的状态,比如播放结束"ended"、静音状态"muted"等,直接使用 video元素.ended 来判断。

猜你喜欢

转载自blog.csdn.net/qq_41090453/article/details/114163656