如何用Python Selenium获取浏览器获取YouTube媒体包时的调用栈
可以通过Selenium实现,需结合Chrome DevTools Protocol(CDP)
Selenium原生日志API无法直接获取网络请求对应的JavaScript调用栈,需要借助Chrome DevTools Protocol(CDP)来实现。以下是具体方案:
核心思路
- 用Selenium 4+内置的CDP接口,启用浏览器的
Network和Runtime调试域 - 监听
Network.requestWillBeSent事件,捕获YouTube媒体包请求(通常是.m4s、.webm格式的分片文件) - 在媒体请求触发时,调用CDP的
Runtime.getStackTrace获取当前JavaScript调用栈
示例代码
from selenium import webdriver from selenium.webdriver.chrome.service import Service import time # 初始化Chrome浏览器 service = Service("/usr/local/bin/chromedriver") driver = webdriver.Chrome(service=service) # 启用Network和Runtime调试域 driver.execute_cdp_cmd("Network.enable", {}) driver.execute_cdp_cmd("Runtime.enable", {}) # 存储捕获到的调用栈 media_call_stacks = [] def capture_call_stack(request_data): # 过滤YouTube媒体分片请求 if ".m4s" in request_data["request"]["url"] or ".webm" in request_data["request"]["url"]: # 获取当前调用栈 stack_trace = driver.execute_cdp_cmd("Runtime.getStackTrace", {}) media_call_stacks.append({ "request_url": request_data["request"]["url"], "call_stack": stack_trace["callFrames"] }) # 打印当前请求的调用栈 print(f"捕获媒体请求: {request_data['request']['url']}") print("调用栈:") for frame in stack_trace["callFrames"]: func_name = frame["functionName"] if frame["functionName"] else "匿名函数" print(f" - {func_name} (文件: {frame['url'].split('/')[-1]} 行号: {frame['lineNumber']})") # 监听网络请求发送事件 driver.add_cdp_listener("Network.requestWillBeSent", capture_call_stack) # 打开目标YouTube视频页面 driver.get("https://www.youtube.com/watch?v=vlzCi0x9uJw") # 等待媒体请求触发(可根据实际情况调整时长) time.sleep(10) # 输出所有捕获的调用栈汇总 print("\n=== 所有媒体请求调用栈汇总 ===") for item in media_call_stacks: print(f"\n请求URL: {item['request_url']}") print("调用栈详情:") for frame in item["call_stack"]: func_name = frame["functionName"] if frame["functionName"] else "匿名函数" print(f" {func_name} | 文件: {frame['url'].split('/')[-1]} | 行号: {frame['lineNumber']}") driver.quit()
关键说明
- 请求过滤:通过URL后缀
.m4s/.webm识别媒体分片,可根据实际需求调整过滤规则 - 调用栈解析:
Runtime.getStackTrace返回的callFrames数组包含每个调用帧的函数名、文件路径、行号等信息 - 版本要求:必须使用Selenium 4及以上版本,Selenium 3对CDP支持有限
- 等待优化:示例中用
time.sleep等待请求触发,实际场景可结合显式等待(如等待视频播放按钮加载完成)提升效率
精准调试替代方案:断点触发
如果需要在特定媒体加载函数触发时捕获调用栈,可通过CDP设置断点:
# 在指定脚本的特定行设置断点 driver.execute_cdp_cmd("Debugger.setBreakpointByUrl", { "url": "https://www.youtube.com/youtubei/v1/player/streamingData?key=...", "lineNumber": 456 }) # 监听断点命中事件 def on_breakpoint_hit(breakpoint_data): print("\n断点命中,调用栈:") for frame in breakpoint_data["callFrames"]: func_name = frame["functionName"] if frame["functionName"] else "匿名函数" print(f" - {func_name}") driver.add_cdp_listener("Debugger.paused", on_breakpoint_hit)
该方法需要提前定位媒体加载相关的JS文件和行号,适合深度调试场景。
内容的提问来源于stack exchange,提问作者Satvik
相关产品推荐
相关产品推荐

