
在实际的自动化项目里browser-use 和 video-use 常常被看成两个不相关的工具前者控制浏览器后者处理视频。但在需要沉淀操作证据、生成演示视频或自动分析网页操作结果的场景里两者会出现在同一条数据链路上。browser-use 负责让浏览器按脚本完成点击、输入、跳转和截图video-use 负责把录制下来的屏幕操作、页面截图和文字结果变成可审查、可归档、可检索的视频资料。这篇文章会从一个最小可运行的实践项目出发先分别说明 browser-use 和 video-use 各自解决什么问题再给出环境搭建、关键代码、运行验证和排错清单。最后的成品是一个“浏览器自动操作 操作录屏 视频内容摘要”的小链路。虽然示例以 Python 为主但核心思路同样适用于 Node.js、Java 等语言。1. 先理解 browser-use 和 video-use 在同一个项目里如何分工很多人看到 browser-use / video-use 这个标题第一反应是“一个控制浏览器一个处理视频”。这个方向没错但如果只是把两个工具放在一个仓库里各写各的项目的价值会大打折扣。更合理的做法是先把它们放在同一条处理流水线里明确谁产出素材、谁消费素材、中间用什么样的文件格式交换。1.1 browser-use 解决的是浏览器端的“行为生成”浏览器自动化的传统做法是写一段 Selenium 或 Playwright 脚本通过选择器定位页面元素然后执行点击、输入、断言。browser-use 的出现解决了两个更复杂的问题一是让同一个任务可以描述成“我要做什么”而不是每一步“怎么找元素”二是任务执行过程中能够感知页面变化生成一张页面状态的快照包括截图、DOM 摘要和操作结果。在实际项目里browser-use 通常承担三个阶段的工作任务解析把用户输入的自然语言或配置项拆解成浏览器操作步骤。页面交互在真实浏览器里执行跳转、点击、输入、滚动、等待等动作。结果采集把每个关键步骤的截图、URL、文本、页面标题和操作记录统一输出。这个阶段的产品是“操作日志”和“页面快照”。它还没有形成视频但这些日志和截图正好是后续 video-use 的输入。1.2 video-use 解决的是视频域的“内容提取与加工”video-use 不是一个像 FFmpeg 那样单一的库在多数项目里它是一组视频处理能力的组合用 OpenCV 读取视频帧用 FFmpeg 做转码和封装用 OCR 识别画面文字再用结构化逻辑生成摘要。你可以把 video-use 理解成“对视频素材做二次加工的工具链”。在 browser-use 的上下游场景里video-use 主要消费三类输入浏览器录屏文件例如 MP4、WebM。页面截图序列一组按时间或步骤编号的 PNG/JPEG。操作日志包含时间戳、行为描述、URL 变化的结构化文本。video-use 的输出通常是带字幕的合成视频、视频抽帧目录、文本摘要 JSON以及用于检索的视频元信息。1.3 两者结合后的典型场景以下三类场景最容易用到 browser-use / video-use 的组合。第一类是线上问题复现留证。测试环境里发现一个页面交互异常但问题不是每次都能复现。用 browser-use 按固定路径操作浏览器同时录屏再用 video-use 对画面做 OCR 识别就能把“异常时的页面文案”和“操作序列”对应起来。第二类是操作演示视频生成。运营或产品同学需要一份新功能使用演示不需要真人录制。browser-use 自动操作页面video-use 在一个小时之内把录屏剪成带字幕的短视频并自动生成操作说明。第三类是网页数据资产归档。定期打开一组数据页面记录页面变化把截图和关键文字合成视频存档方便事后回溯。不管哪种场景核心思路是一致的browser-use 负责产生有过程的素材video-use 负责把这些素材加工成有结果的内容。2. 环境准备与最小项目结构在实际动手前先把环境对齐。很多自动化脚本跑不起来不是代码写错而是 Python 版本、浏览器驱动、FFmpeg 环境三者不匹配。下面这套环境组合适合大多数 Linux 服务器和 macOS 开发机Windows 上需要留意路径分隔符和字体文件位置。2.1 运行环境要求建议使用 Python 3.10 或更高版本。browser-use 依赖异步能力Python 版本过旧容易出现 event loop 兼容问题。浏览器方面建议安装 Chromium 或 Chrome并用 Playwright 自带的浏览器管理命令安装一份独立 Chromium避免影响系统全局浏览器。视频处理部分需要 FFmpeg。FFmpeg 负责视频转码、抽帧、加字幕OpenCV 负责读取视频帧和图像预处理。系统里没有 FFmpeg 时后续录屏处理和画字字幕都会失败。2.2 安装 browser-use 以及浏览器驱动Windows 下建议先创建虚拟环境避免依赖写入全局路径。cd browser-video-demo python -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install browser-use playwright playwright install chromium这里有两个细节需要说明。browser-use这个包名在不同版本中 API 差异较大有的版本使用Agent作为入口有的版本需要自己传入Browser实例。下面示例会采用一种简化封装写法目的是讲清楚链路实际项目里要以你安装版本的官方文档为准。playwright install chromium会把 Chromium 下载到用户缓存目录。如果下载失败可以检查网络环境或者设置镜像源后重试。这一步不是可选项browser-use 底层需要真实浏览器。2.3 安装 video-use 相关的视频处理依赖视频处理部分需要的依赖包括 OpenCV、pytesseract、Pillow 和它的 OCR 引擎 Tesseract。pip install opencv-python-headless pytesseract pillowmacOS 上安装 Tesseractbrew install tesseractUbuntu / Debian 上安装 Tesseractsudo apt-get install tesseract-ocr如果只需要处理中文页面还需要安装中文语言包sudo apt-get install tesseract-ocr-chi-sim安装完成后用下面的命令确认 OCR 引擎可用tesseract --version2.4 推荐的项目目录结构建议把浏览器自动化、视频处理、中间产物分成三个目录。这样 review 和排错都会方便很多。browser-video-demo/ ├── .venv/ ├── config/ │ └── settings.yaml ├── outputs/ │ ├── logs/ │ ├── screens/ │ ├── videos/ │ └── summaries/ ├── scripts/ │ ├── browser_job.py │ ├── build_video.py │ └── video_summary.py └── run_pipeline.pyoutputs/logs保存 browser-use 的操作日志outputs/screens保存页面截图outputs/videos保存录屏和合成后的视频outputs/summaries保存 video-use 生成的 JSON 摘要。scripts下按职责拆分了三个入口脚本run_pipeline.py负责串联整条链路。3. 用 browser-use 驱动浏览器完成一次可复现任务这一节的目标不是写一个完整商业级自动化引擎而是用一个最小任务跑通 browser-use 的完整流程打开目标页面、执行一次搜索、等待结果加载、保存截图和操作日志。这里的关键不在代码量而在于理解每个阶段出现的输入输出。3.1 初始化浏览器实例以常见封装为参考先写一个browser_session.py负责创建和关闭浏览器实例。# scripts/browser_session.py from browser_use import Browser, BrowserConfig def create_browser(headless: bool True, video_dir: str outputs/videos): config BrowserConfig( headlessheadless, record_video_dirvideo_dir, ) return Browser(configconfig)这段代码里需要注意record_video_dir。有些项目把它放在BrowserConfig里有些版本则需要在 Playwright 的BrowserContext上配置。代码封装的用意是隔离开底层 API 的版本差异。如果只是做快速验证可以先把headless设为False。这样能看到浏览器窗口执行动作也更容易判断选择器是否定位正确。等确认逻辑稳定后再切回True放到服务器上运行。3.2 核心配置参数说明在学习环境里配置越少越容易跑通。但进入生产环境之前至少要关注以下参数。参数常见值作用错误配置的表现headlessFalse / True是否无头运行True 时不弹窗但排错困难timeout30000 毫秒单步操作超时上限过短时页面尚未加载完成就抛超时record_video_diroutputs/videos录屏文件输出目录目录不存在时录屏失败viewport_size1280x800模拟浏览器窗口大小过小时页面内容会响应式折叠user_agent默认标识客户端身份部分站点对陌生 UA 有风控对大多数页面timeout不要低于 15 秒。浏览器自动化最常见的失败是页面元素已经渲染完成但脚本在等待某个异步接口导致出现“元素能找到但页面逻辑还没准备好”的问题。3.3 执行自动化任务并导出操作日志下面用一个示例任务说明执行过程打开一个搜索页面输入关键词点击搜索按钮等待结果。# scripts/browser_job.py import asyncio from browser_session import create_browser async def run_job(): browser create_browser(headlessFalse) page await browser.new_page() steps [] try: await page.goto(https://example.com/search) await page.fill(input#keyword, browser automation) steps.append({action: fill_keyword, text: browser automation}) await page.click(button#search) await page.wait_for_timeout(3000) steps.append({action: click_search}) title await page.title() text await page.content() screenshot_path outputs/screens/result.png await page.screenshot(pathscreenshot_path) result { title: title, text_length: len(text), screenshot: screenshot_path, steps: steps, } return result finally: await browser.close() if __name__ __main__: result asyncio.run(run_job()) print(result)这段代码里有一个重要习惯每执行一步操作就把动作名称和关键参数追加到steps列表。这不仅是日志还是后续 video-use 生成字幕和摘要的重要依据。如果只在程序里打印日志视频合成阶段还需要重新解析控制台成本会高很多。3.4 这一步最容易踩的三个坑第一个坑是选择器不唯一。页面里可能有多个buttonbutton#search使用 id 选择器能降低误点概率。如果 id 是动态生成的就要改用CSS或XPath中更稳定的属性。第二个坑是页面等待时间写死。timeout3000在本地网络环境可能足够但在生产环境请求变慢时就会出现结果未加载就截图的情况。建议优先等待目标元素出现await page.wait_for_selector(div.result-item, timeout15000)第三个坑是浏览器没有正常关闭。如果browser.close()放在异常分支外部脚本崩溃后浏览器进程会残留。使用finally块可以保证最终关闭。在服务器上长期跑任务时还要考虑用with上下文管理器或进程守护工具兜底。4. 将浏览器操作过程转成视频素材browser-use 跑完任务之后我们拿到了截图、文本和操作日志。但这份材料还不能算视频。要生成真正可播放的视频需要把浏览器操作过程录下来再用 FFmpeg 或者 video-use 链路做合成。4.1 录制方案选择浏览器录屏通常有三种思路。第一种是操作系统级录屏比如 macOS 的 screenrecord 或 Windows 的录屏工具。这种方式能录到任意软件界面但依赖桌面环境不适合服务器。第二种是浏览器内录屏通过 Playwright 的record_video_dir自动录制页面内容。这种方式适合纯 Web 页面在无头浏览器中也可以工作。第三种是先用 browser-use 产生截图序列再用 FFmpeg 把截图合成视频。这种方式最稳定因为每张截图都有明确语义而且不受录屏文件格式影响。三种方式对比如下方式优点缺点适用场景系统级录屏画面完整依赖 GUI难自动化本地演示、会议记录浏览器内录屏服务器可用文件较小部分弹窗、文件选择框无法录制页面自动化、回归测试截图序列合成可控性强能生成字幕帧率不稳定教程视频、过程留档4.2 使用 Playwright 内录屏生成操作视频如果使用 Playwright 的录屏能力可以在创建页面上下文时开启录屏。录屏结束时需要关闭页面上下文视频文件才会真正写入磁盘。from playwright.async_api import async_playwright async def record_capture(): async with async_playwright() as p: browser await p.chromium.launch() context await browser.new_context( record_video_diroutputs/videos/, record_video_size{width: 1280, height: 720}, viewport{width: 1280, height: 800} ) page await context.new_page() await page.goto(https://example.com) await page.fill(input#keyword, browser automation) await page.click(button#search) await page.wait_for_timeout(5000) # 关闭上下文后视频才保存 await context.close() video await page.video.path() print(video path:, video) await browser.close() import asyncio asyncio.run(record_capture())代码里record_video_size和viewport最好保持一致否则录出的视频画面会被拉伸或裁剪。page.video.path()只在上下文关闭之后才能拿到有效路径。4.3 用 FFmpeg 给视频加字幕和时间戳浏览器录屏完成后最直接的增强是给视频加上操作步骤字幕。假设操作日志已经转换成subtitles.srt用 FFmpeg 一条命令就可以把字幕烧进画面ffmpeg -i outputs/videos/raw.webm -vf subtitlesoutputs/logs/subtitles.srt outputs/videos/final.mp4如果不想维护单独的 SRT 文件也可以用drawtext一次性插入简单文字ffmpeg -i raw.webm -vf drawtexttextStep 1: fill keyword:x20:y20:fontsize24:fontcolorwhite final.mp4但drawtext对中文字体支持不够好在 Linux 服务器上经常遇到乱码。更推荐的做法是先生成 SRT 文件再用subtitles滤镜。SRT 里可以指定字幕文字和显示时间video-use 链路也可以读取同一种文件。5. 用 video-use 链路完成视频内容摘要视频生成之后工作还没结束。真正有价值的反而是“视频里到底发生了什么”。video-use 在这一步负责把视频转成一串可查询的结构化结果。5.1 抽取关键帧和音频信息先用 OpenCV 读取视频按固定间隔抽帧。抽帧频率不宜过高否则后续每张图都要走 OCR性能消耗很大。# scripts/video_frames.py import cv2 import os def extract_frames(video_path: str, output_dir: str, interval: float 2.0): cap cv2.VideoCapture(video_path) if not cap.isOpened(): raise RuntimeError(cannot open video: video_path) fps cap.get(cv2.CAP_PROP_FPS) os.makedirs(output_dir, exist_okTrue) frame_no 0 saved_no 0 while True: ret, frame cap.read() if not ret: break if frame_no % int(fps * interval) 0: path os.path.join(output_dir, fframe_{saved_no:04d}.jpg) cv2.imwrite(path, frame) saved_no 1 frame_no 1 cap.release() return saved_no这里用fps * interval计算需要跳过的帧数。比如视频是 30fpsinterval2表示每 60 帧保存一帧也就是每 2 秒抽取一帧。抽帧数量不是越多越好先抽出来看覆盖率再决定是否需要加密。5.2 用 OCR 识别浏览器画面中的文字浏览器录屏画面里会出现按钮文字、输入框内容、标题等。OCR 能把这些视觉信息转成可检索文本。# scripts/video_ocr.py import pytesseract from PIL import Image def ocr_image(image_path: str, lang: str chi_simeng): image Image.open(image_path) data pytesseract.image_to_data(image, langlang, output_typepytesseract.Output.DICT) lines [] blocks {} for i, block_num in enumerate(data[block_num]): text data[text][i].strip() if not text: continue item { block: block_num, left: data[left][i], top: data[top][i], width: data[width][i], height: data[height][i], text: text, } blocks.setdefault(block_num, []).append(item) lines.append(item) return blocks, linesOCR 结果不要直接拼成字符串建议保留坐标信息。后续如果需要在视频里高亮某个区域坐标就是直接可用的数据。pytesseract的识别结果对清晰的小字号文字效果较好但页面如果有复杂背景可以先对图像做灰度化和二值化import cv2 def preprocess_image(image_path: str): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) _, thresh cv2.threshold(img, 180, 255, cv2.THRESH_BINARY) return thresh5.3 生成结构化摘要 JSON 和入口函数OCR 结束后可以把所有帧的文本和时间信息合并成摘要。下面是一个 video-use 模块的主要入口# scripts/video_use.py import json import os from video_frames import extract_frames from video_ocr import ocr_image def build_summary(video_path: str, output_dir: str) - str: frame_dir os.path.join(output_dir, frames) count extract_frames(video_path, frame_dir, interval2.0) items [] for i in range(count): frame_path os.path.join(frame_dir, fframe_{i:04d}.jpg) _, lines ocr_image(frame_path) if not lines: continue # 去重保留同一 block 内出现多次的文本 unique_texts [] seen set() for line in lines: if line[text] not in seen: seen.add(line[text]) unique_texts.append(line[text]) items.append({ frame: i, time_sec: round(i * 2.0, 2), text: | .join(unique_texts), ocr_lines: lines, }) summary { video_path: video_path, frame_count: count, items: items, } summary_path os.path.join(output_dir, summary.json) with open(summary_path, w, encodingutf-8) as f: json.dump(summary, f, ensure_asciiFalse, indent2) return summary_pathtime_sec是根据帧序号乘以抽帧间隔计算的。如果抽帧间隔改变这个时间也需要同步调整。真正生产级实现应该从视频元信息中读取每帧的真实时间戳这里为了示例清晰做简化。6. 串联完整管线从 browser-use 到 video-use前面的步骤各自独立但实际价值发生在把它们连起来的时候。完整流程是“浏览器自动化”产生操作过程和视频然后“视频处理”把视频变成文字摘要最后把摘要再反馈给调用方或归档系统。6.1 管线设计思路设计管线时建议把每个中间产物落到磁盘而不是全部扔在内存里。browser_job.py写操作日志和截图record_capture.py写录屏视频build_summary.py写 JSON 摘要。每个文件都是独立的检查点。这样任何一个环节出错都能从上一个检查点重新开始而不需要重新跑完整流程。还有一个容易被忽略的点操作日志和视频摘要的时间基准必须一致。browser-use 在点击按钮时记录的是浏览器时间轴上的动作video-use 抽帧时用的是视频时间轴。如果两者相差很大字幕和画面会错位。6.2 一键执行脚本下面用一个run_pipeline.py把整条链路串起来。# run_pipeline.py import asyncio import subprocess import json from scripts.browser_job import run_job from scripts.video_use import build_summary def main(): # 第一步运行 browser-use 任务 job_result asyncio.run(run_job()) # 第二步调用录屏脚本此处用 subprocess 便于单独观察失败 subprocess.run( [python, -m, scripts.record_capture], checkTrue, ) # 第三步生成视频摘要 video_path outputs/videos/raw.webm summary_path build_summary(video_path, outputs/summaries) print(summary:, summary_path) # 第四步打印关键结果 print(title:, job_result[title]) with open(summary_path, r, encodingutf-8) as f: summary json.load(f) print(frames:, summary[frame_count]) if __name__ __main__: main()实际项目里更规范的做法是用apscheduler做定时触发或者用消息队列把任务拆成异步流程。但最小示例里顺序执行已经足够展示数据流。6.3 运行后的验证点与预期输出运行完整链路后可以按以下顺序检查输出outputs/screens/result.png是否存在且内容完整。outputs/videos/raw.webm是否能正常播放。outputs/videos/final.mp4是否包含中文字幕。outputs/summaries/summary.json是否包含items数组。summary.json中的文本是否与录屏画面内容匹配。如果summary.json里出现大量空文本说明 OCR 没有识别到有效文字。这时要回看抽帧图片确认画面是否清晰、背景是否干扰、语言包是否安装。6.4 学习环境与生产环境的差异学习环境里可以在有界面的开发机上跑使用headlessFalse方便观察浏览器动作。生产环境通常是无头服务器需要注意下面几点。使用headlessTrue并确保服务器安装了 Chromium 的所有系统依赖。配置日志落盘而不是只输出到终端。视频文件完成处理后及时转存避免占用磁盘。为每个任务生成唯一的 run_id所有中间产物都带上 run_id 前缀。对 OCR 和视频转码增加资源限制防止内存和 CPU 被占满。保留原始录屏文件摘要只是索引不是原片。7. 常见问题排查清单browser-use 和 video-use 组合起来后问题可能出现在浏览器层、视频层或系统依赖层。排查时应从输入是否正确开始一层层往下查。7.1 浏览器启动失败现象是运行脚本时提示找不到浏览器可执行文件或playwright报Executable doesnt exist。可能原因是没有执行playwright install chromium或者安装的浏览器版本与依赖不兼容。检查方式playwright install --dry-run chromium处理方案重新安装 Chromium并确认安装目录存在。使用无头模式时需要额外确认系统库。7.2 录屏没有声音或画面空白浏览器内录屏默认只录页面内容不会采集麦克风或系统音频。这属于预期行为不是 bug。如果画面空白最可能是record_video_dir目录不存在或页面在录屏结束时没有正常关闭。处理方案先创建outputs/videos目录再确认代码中最后执行了context.close()。音频需要单独设计采集方案不要指望浏览器录屏自带。7.3 OCR 识别结果为空现象是summary.json中text字段为空字符串。可能原因是 Tesseract 语言包未安装、画面太模糊、或者页面背景复杂。检查方式用 OCR 工具单独跑一张抽帧图片tesseract outputs/summaries/frames/frame_0000.jpg stdout -l chi_simeng处理方案先输入中文语言包然后对图片做灰度化、二值化、放大分辨率等预处理。问题现象常见原因检查方式处理建议浏览器启动失败驱动未安装或系统依赖缺失playwright install --dry-run chromium重新安装浏览器及相关依赖录屏为空视频目录不存在或未关闭 context检查目录、上下文关闭逻辑创建目录确保执行context.close()OCR 为空语言包缺失、图片模糊tesseract命令行单独测试安装语言包、灰度化、放大图片视频转码失败FFmpeg 未安装或字体缺失ffmpeg -version安装 FFmpeg配置中文字体时间戳错位抽帧间隔与实际写入时间不一致比对summary.json与字幕文件时间使用真实视频时间戳或同一配置内存暴涨抽帧间隔过小图片队列堆积查看任务 CPU/内存曲线拉大抽帧间隔批量处理7.4 video-use 处理长时间视频时内存暴涨现象是视频文件不到几百 MB但处理脚本占用内存超过 4 GB。可能原因是extract_frames把所有帧放到列表里或者 OCR 时一次性加载大量图片。解决方式是把“抽帧-OCR-写入”改成流式处理读一帧处理完立即把结果写入 JSON 文件释放图像对象。# 推荐逐帧处理避免长时间占用内存 while True: ret, frame cap.read() if not ret: break if should_keep(frame_no): process_one_frame(frame, frame_no) frame None8. 最佳实践与扩展方向到这里最小链路已经跑通。真正把 browser-use 和 video-use 用到生产环境还需要在工程化上多做几件事。8.1 把任务改成可重放、可回滚的流水线浏览器自动化最怕“这次能跑下次不能跑”。所以不要把所有逻辑写在一个脚本里。建议把每一步拆成独立任务记录每一步的输入和输出摘要。当某个页面元素发生了变化只需要检查日志就能定位到哪一步开始失败。实际操作时可以先给中间文件加校验。例如截图生成后立即记录文件大小OCR 完成后立即写入 JSON。任何一步结果为空都要在管线入口抛出明确错误。8.2 视频文件应走对象存储不要直接塞进代码库本地开发时把视频放在outputs/videos没问题但不要提交到 Git 仓库。视频文件体积大且每次运行结果都会变化。更好的做法是把视频上传到公司的对象存储或内部文件服务器代码库里只保留视频 URL 和摘要 JSON。对于需要长期留痕的场景要设置好文件的清理周期。录屏文件只保留一个月摘要 JSON 保留一年是常见的策略。具体保留时间需要根据业务要求确定。8.3 值得继续深挖的方向browser-use 和 video-use 的组合不仅能做演示视频还能继续扩展。引入大模型让 browser-use 的任务描述直接来自 LLM从“操作搜索”扩展到“读页面-判断-操作-总结”。做页面回归看板每次运行后生成视频摘要把不同日期的摘要进行 diff快速发现页面内容变化。支持多浏览器多账号在 browser-use 层增加账号隔离在 video-use 层为每个账号生成独立的视频报告。对接告警系统当 OCR 识别到“系统错误”或“服务器异常”等关键词时自动标记任务结果并触发告警。对于刚开始接触这套链路的开发者建议先不追求算法复杂度。先把一条最小流程跑通再把真实业务中的页面加进来逐步增加异常分支。视频处理脚本因为依赖 OpenCV、Tesseract 和 FFmpeg很容易因为系统环境差异出问题。每完成一个环节先验证中间产物再继续下一个环节是整个项目里最重要的习惯。