基于Selenium WebDriver检测播放列表中第n个HTML5视频结束的通用方案
可靠捕获播放列表中第n个视频结束事件的Selenium方案(适配JW8、VideoJS等HTML5播放器)
我之前处理过不少跨播放器的视频自动化监控需求,结合你描述的播放列表流程——广告→短视频→广告→下一个视频循环,而且所有视频最终都是通过HTML5 <video>元素加载、切换时仅更新src属性的情况,这里给你一套通用的、不需要对接JW8/VideoJS等播放器专属API的方案,完全基于原生HTML5视频事件来实现可靠捕获第n个视频的结束事件:
核心思路
不管上层封装的是哪种播放器,最终的视频渲染都依赖原生的<video>元素,所以我们可以绕开播放器API,直接监听<video>的原生ended事件;同时通过跟踪src属性的变化(或元素替换)来区分不同的视频实例,再配合一个计数机制,就能精准定位到第n个目标视频的结束时刻。
具体实现步骤
- 第一步:等待页面中的
<video>元素加载完成,确保后续能正常绑定事件。 - 第二步:注入自定义JavaScript脚本到浏览器中,实现三个核心功能:
- 维护一个视频播放结束的计数器
- 监听
<video>的ended事件,每次触发时更新计数器 - 监听
<video>的src属性变化(或元素替换),确保事件监听始终绑定在当前活跃的视频元素上
- 第三步:通过Selenium的显式等待,等待计数器达到目标n值,即第n个视频结束事件触发。
代码示例(Python + Selenium)
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 # 初始化浏览器 driver = webdriver.Chrome() driver.get("你的播放列表页面URL") # 等待原生video元素出现 video_element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.TAG_NAME, "video")) ) # 注入视频跟踪脚本,传入要监听的第n个视频索引(从1开始计数) target_video_index = 2 # 这里设置你要捕获的第n个视频 tracker_script = """ window.videoEndCount = 0; window.isTargetVideoEnded = false; const targetIndex = arguments[0]; // 绑定ended事件的通用函数 function bindEndEvent(videoEl) { // 先移除旧的监听,避免重复触发 videoEl.removeEventListener('ended', onVideoEnd); videoEl.addEventListener('ended', onVideoEnd); } // 视频结束时的回调 function onVideoEnd() { window.videoEndCount += 1; console.log(`第${window.videoEndCount}个视频播放结束`); if (window.videoEndCount === targetIndex) { window.isTargetVideoEnded = true; console.log(`目标视频(第${targetIndex}个)已播放完毕`); } } // 给初始video绑定事件 bindEndEvent(document.querySelector('video')); // 监听video元素的src变化或元素替换,确保事件始终有效 const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { // 处理src属性变化的情况 if (mutation.type === 'attributes' && mutation.attributeName === 'src') { bindEndEvent(mutation.target); } // 处理video元素被替换的情况(部分播放器会这么做) if (mutation.type === 'childList') { const newVideo = document.querySelector('video'); if (newVideo) bindEndEvent(newVideo); } }); }); // 监听video元素本身的属性变化,以及其父元素的子节点变化 observer.observe(document.querySelector('video'), { attributes: true }); observer.observe(document.querySelector('video').parentNode, { childList: true }); """ # 执行注入脚本,传入目标视频索引 driver.execute_script(tracker_script, target_video_index) # 等待目标视频结束事件触发,超时时间根据视频最长时长设置(这里设为5分钟) WebDriverWait(driver, 300).until( lambda d: d.execute_script("return window.isTargetVideoEnded;") ) print(f"第{target_video_index}个视频已成功捕获结束事件!") # 后续操作:比如继续处理下一个视频、关闭浏览器等 # driver.quit()
进阶优化:区分广告和目标短视频
如果你的需求是仅计数“短视频”而非广告,可以在onVideoEnd回调里添加过滤逻辑,比如通过视频时长或src特征来判断:
function onVideoEnd() { // 示例:过滤时长小于30秒的广告视频 if (this.duration > 30) { window.videoEndCount += 1; console.log(`第${window.videoEndCount}个目标短视频播放结束`); if (window.videoEndCount === targetIndex) { window.isTargetVideoEnded = true; console.log(`目标短视频(第${targetIndex}个)已播放完毕`); } } else { console.log("广告播放结束,不计数"); } }
关键注意事项
- 超时时间设置:根据你的视频最长时长调整显式等待的超时时间,避免因视频未播放完就触发超时。
- 元素替换兼容:部分播放器切换视频时会直接替换
<video>元素而非仅更新src,所以脚本里同时监听了父元素的子节点变化,确保能重新绑定事件。 - 跨浏览器支持:这套方案基于HTML5原生API,在Chrome、Firefox、Edge等现代浏览器都能稳定运行。
内容的提问来源于stack exchange,提问作者Nick Slavsky




