ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python自动化网课截屏转可搜索PDF笔记,告别手动整理

Python自动化网课截屏转可搜索PDF笔记,告别手动整理 说实话我一开始也是个“疯狂截屏党”。200分钟的网课我盯着屏幕看到重点就按一下截图快捷键一节课下来相册里多了一百多张“精华”。回头整理的时候直接崩溃——每张图都是独立的碎片没时间戳、没顺序、没文字还得手工重命名、归档更夸张的是有些图糊得根本看不清板书内容。那感觉就像把一整本书拆成单页往抽屉里一塞要用的时候根本找不到。后来我实在受不了了花了一个周末用 Python 写了套自动化脚本把“手动截屏、手动整理、手动找资料”的整个链路彻底砍掉。只需要设置好视频区域脚本会定时自动截图然后对截图做去重、OCR识别、拼接排版最后直接导出一份带层级、带时间戳、甚至可以做全文检索的 PDF 笔记。整套流程零手工干预200分钟的网课最终产出的 PDF 不到200页但每一页的信息密度都远超我之前那堆“垃圾截图”。这篇文章把整个思路、关键代码、参数调优和踩坑记录都摊开来讲。不管你是学生党、职场培训选手还是喜欢囤课做笔记的“知识整理癖”这套方案都能直接照抄而且完全免费、不需要高端显卡也能跑得动。1. 为什么不能再靠“疯狂截屏”做这个工具之前我认真算过一笔账手动截屏方案到底有多浪费时间。一节 200 分钟的网课按平均每 30 秒手动截一张图来算要按 400 次快捷键。中间还会有漏截、误截、画面拖影、鼠标挡字等各种问题实际有效截图能有一半就不错了。更致命的是这些截图之间没有逻辑关联——哪张在前哪张在后、对应视频的哪个时间点、这页讲的主题是什么全靠大脑记忆。两天后再翻基本等于看一堆陌生图片。1.1 手动截屏的三个致命伤第一个问题是注意力撕裂。听课时你的注意力应该在老师的讲解逻辑上而不是在“这个要不要截”的判断上。手动截屏会不断打断心流一节课听完记忆反而比不截屏时更碎片化。我试过边听课边截图效果是视频看完了脑子一片空白只顾着“操作”了。第二个问题是图像质量不可控。屏幕截图本身不一定会糊但很多播放器在 1.25 倍或 1.5 倍速播放时画面会有轻微动态模糊加上视频本身的码率不高静态截图如果没等画面稳定那一瞬再截拍下来的文字边缘全是锯齿。手动操作时你根本来不及等那一帧稳定咔一下就按下去了。第三个问题是后续整理成本极高。截图只是开始真正的噩梦在后面。你还需要挨个打开图片、裁剪掉无关的播放器边框、判断主题、重命名、按月归档。这种纯体力活干不了几节课就彻底不想动了。所以结论很直接手动截屏这条路效率低、质量差、不可持续必须让代码接管。1.2 先想清楚你要的是“图像版”还是“搜索版”PDF写代码之前先明确需求。市面上把截图合成 PDF 的工具不少但大多只是把图片丢进一个 PDF 文件里本质上是“图片打包”没有文字层。这种 PDF 的好处是生成快、体积小但坏处是没法搜索、没法复制。我最终选了更暴力也更实用的路线先截屏再 OCR 识别最后把识别出的文字层与原始图像一起打包进 PDF。这样做出来的笔记既能看原始截图板书、图表、公式的本来样貌又能直接 CtrlF 搜索文字内容想找某个知识点一搜就定位到那一页。有句话叫“无损压榨”这里的“无损”我理解有两层图像本身不压缩、不缩放保留原始像素同时额外叠加一层 OCR 出来的可检索文本让 PDF 成为真正可用的知识库而不是一张张死图。1.3 方案选型截屏、OCR、PDF三步走围绕这个需求技术栈选得很明确截屏用mss底层调用系统 API速度比 Pillow 自带的 ImageGrab 高一个量级而且跨平台Windows、macOS、Linux 都能用。图像去重用imagehash感知哈希算法配合 OpenCV 转灰度、对比两张截图的相似度把画面几乎没变的帧过滤掉避免同一页PPT在 PDF 里出现几十遍。OCR 识别用 PaddleOCR针对中英文混排场景的识别率明显优于 Tesseract而且开源免费CPU 也能跑。PDF 生成用img2pdf做纯图像合成速度快、体积小如果需要文字层则在此基础上用 PyMuPDFfitz把 OCR 文本按坐标写进去。这套组合拳打下来成本几乎为零效果却比我用过的任何一款商业笔记软件都顺手。2. 开工准备环境、依赖库和你的第一版截屏脚本2.1 工具清单和选择理由先看我最终用的依赖清单列一下版本信息仅供参考库名版本参考用途Python3.9主语言语法层面没有特别高级的特性mss6.1.0高性能截屏多显示器也支持opencv-python4.8.1图像灰度化、去噪、边缘预处理imagehash4.3.1感知哈希去重Pillow10.x图像尺寸校验、格式转换paddleocr2.6.xOCR 文字识别img2pdf0.4.4图片快速合成 PDFPyMuPDF1.23.x给 PDF 叠加文字层这里重点说下为什么不用pyautogui截屏。它虽然也能截屏但速度确实慢大概能到每秒几帧就算不错而且高 DPI 屏幕下坐标容易偏移。mss直接走底层截图接口单帧截取速度可以到毫秒级批量截 1000 张图的差距是“分钟级别”对“小时级别”。安装依赖直接用 pip 就行不赘述建议用虚拟环境别把系统环境搞乱pip install mss opencv-python imagehash Pillow paddleocr img2pdf PyMuPDFPaddleOCR 第一次运行时还会自动下载少量模型文件如果网络不好可能要多等一会儿以后就不用再下了。2.2 核心代码5行先跑通截屏与保存不管计划多宏大先跑通最小闭环再说。我第一步写的代码很简单指定屏幕区域用 mss 截图并保存成 PNG。import mss import mss.tools # 这里写你自己要截取的屏幕区域x, y 是左上角坐标width, height 是宽和高 region {left: 120, top: 80, width: 1200, height: 800} with mss.mss() as sct: # 直接抓取屏幕并保存sct.shot 会把整屏保存成文件 # 如果要截指定区域用 grab 保存 raw sct.grab(region) mss.tools.to_png(raw.rgb, raw.size, outputsnapshot.png) print(截图已保存)就这么几行你已经具备了“自动截屏”的基础能力。这个闭环先跑通后面所有功能都是往里面加模块。一个小提醒region 里的坐标是屏幕物理像素坐标不是逻辑坐标。如果你的 Windows 开启了 125% 或 150% 的缩放这里有个大坑我在后面避坑章节专门讲。2.3 两条容易踩的坐标与DPI坑第一就是高 DPI 缩放问题。Windows 系统在 100% 缩放下一切正常但一旦显示缩放比例不是 100%PyAutoGUI 的pixel、locateOnScreen这类接口经常出现“看到的坐标和实际截图的坐标对不上”的情况。mss相对好用一些但也要以实际像素为准不要用逻辑像素计算区域。保险做法是写一行语句把屏幕实际分辨率打印出来import mss with mss.mss() as sct: # sct.monitors[1] 是主屏幕信息 info sct.monitors[1] print(info) # {left: 0, top: 0, width: 1920, height: 1080}如果这里的 width 是 1920而系统显示设置显示的是 1536 这种值就说明缩放不是 100%你在写 region 时要以 1920 这个物理分辨率来做计算。第二是多显示器坐标偏移。很多玩家用双屏左边放视频、右边写笔记这时候 region 里的 left、top 坐标要特别当心。mss里主屏监视器序号是 1但如果窗口在主屏左边负坐标区域left 就可能出现负值。我的建议是在脚本里直接把视频窗口手动拖到主屏固定位置然后用“窗口位置获取”代码打印出精确的 left、top、width、height 这些值再写死到配置里别用模糊的手动估算。3. 主体逻辑200分钟实战里的三个核心模块3.1 模块一智能去重别让6000张废图进PDF200分钟的视频如果真按固定间隔 1 秒截一张能截出 12000 张图。就算间隔放到 5 秒也有 2400 张。这里面的“重复”远比你想象得多——老师讲一页PPT讲了3分钟5秒一张的截法能截出36张几乎一样的图。全塞进 PDF翻到怀疑人生。所以我加了一道“去重闸门”核心走感知哈希算法。感知哈希的原理是先把图片缩放成固定大小比如 8x8 或 32x32转成灰度做离散余弦变换再取低频信息生成一串“指纹”哈希值。两张图越相似哈希值越接近汉明距离越小。我用 imagehash 实现同时结合 OpenCV 把画面灰度化并缩小减少计算量。比较方式可以很朴素设定一个汉明距离阈值比如 5如果当前截图与上一张保留截图的哈希距离小于阈值就认为画面基本没变化直接丢弃如果大于阈值说明画面发生了有效更新保留这张图。import cv2 import imagehash from PIL import Image # 保存上一次的哈希 last_hash None def is_duplicate(raw_img_path, threshold5): global last_hash image cv2.imread(raw_img_path) # 转成灰度减少计算量 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 缩小到统一尺寸减少取值波动直接用 PIL 处理更简单 gray_pil Image.fromarray(gray).resize((32, 32)) current_hash imagehash.phash(gray_pil) if last_hash is None: last_hash current_hash return False distance last_hash - current_hash last_hash current_hash return distance threshold阈值的选择有讲究。别设太严比如设成 0那只要鼠标光标位置变了、或者视频里出现个弹幕水印就会判定为新画面去重等于失效。也别设太松设成 20 以上老师讲 PPT 速度稍微快一点一页内容讲完俩小结你中间的内容就可能被跳掉。我实测下来一般课程画面的阈值在 4~6 之间最稳如果课上是大量动画演示建议调到 8~10。3.2 模块二OCR识别把屏幕像素变成可搜索文字去重后图片数量可以压到几百张的量级此时可以做 OCR。这里我直接用了 PaddleOCR 的 Python API。为什么不用 Tesseract我在中文网课场景下实测过Tesseract 对中英文混排、亮色背景白字、数学符号和代码块这类网课高频内容识别率确实不尽如人意。PaddleOCR 的 PP-OCRv3 模型在这些场景下明显更稳而且 CPU 也能跑就是稍微慢点一张图大约 0.5 到 1 秒。OCR 环节还有几个提质量的细节去噪增强OCR 之前先灰度化再做一次轻度二值化处理把文字和背景分离放大超采样如果视频分辨率不高文字区域只有百来像素直接把截图丢给 OCR识别率很低。我用 OpenCV 把截图按 2 倍放大后再送识别准确率能肉眼可见地提升过滤乱码PaddleOCR 偶尔会把角标、水印识别成奇怪的字符不需要的可以按置信度过滤掉低于阈值的结果直接丢弃。import numpy as np import cv2 from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch, show_logFalse) def ocr_image(image_path): image cv2.imread(image_path) # 二次放大提升小字识别率 scale 2 resized cv2.resize(image, None, fxscale, fyscale, interpolationcv2.INTER_CUBIC) # 先灰度化再做轻度增强 gray cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY) gray cv2.equalizeHist(gray) result ocr.ocr(gray, clsTrue) texts [] confs [] for line in result: # 识别结果中第二项是 (text, confidence) for box, (text, conf) in line: if conf 0.6: texts.append(text) confs.append(conf) return texts, confsOCR 识别的文字单独存成 JSON 文件不急着生成 PDF因为后面还可能调整排版按需重新生成更灵活。3.3 模块三PDF生成无损图片与可检索文本层先把图片直接合成 PDF这一步用 img2pdf 非常清爽一行搞定import img2pdf import os image_paths sorted([f for f in os.listdir(notes) if f.endswith(.png)]) with open(notes_raw.pdf, wb) as f: f.write(img2pdf.convert([os.path.join(notes, p) for p in image_paths]))img2pdf 不会重新编码图片而是把图片原始流直接写进 PDF 容器所以速度极快而且图片不损失一个像素。这是“无损”的第一层保障。但单纯这样生成的 PDF 还是没法搜索因为只有图像层没有文字层。我接下来的操作是用 PyMuPDF 打开前面生成的 PDF在对应的每一页上把 OCR 出来的文字按坐标写入一个隐藏层。因为文字是隐藏的所以不会遮挡画面但因为 PDF 里有文本对象阅读器就能执行搜索了代码结构很简单import fitz doc fitz.open(notes_raw.pdf) page doc[0] text OCR_TEXTS[0] # 假设这是第一页识别出来的结果 page.insert_textbox( fitz.Rect(0, page.rect.height-100, page.rect.width, page.rect.height), text, fontsize8, fontnamechina-s, overlayFalse, color(0, 0, 1), ) doc.save(notes_searchable.pdf)实际项目中文字应该按 OCR 出来的坐标精细排布而不是堆在底部。坐标对齐的好处是当你复制 PDF 里的文字时顺序和位置能尽量贴近画面上的视觉顺序阅读体验会好很多。4. 完整的自动化流水线从双击脚本到拿到笔记4.1 主流程代码定时截屏判重批处理把上面几个模块串起来形成一个完整的主循环import time import mss import cv2 import imagehash import os from PIL import Image save_dir frames os.makedirs(save_dir, exist_okTrue) region {left: 120, top: 80, width: 1200, height: 800} interval 3 # 每3秒截一次 threshold 5 # 哈希距离阈值小于该值认为是重复画面 last_hash None with mss.mss() as sct: idx 0 while True: raw sct.grab(region) # 转成 PIL 的 Image并保存临时文件 img Image.frombytes(RGB, raw.size, raw.rgb) temp_path temp.png img.save(temp_path) # 去重判断 gray cv2.imread(temp_path, cv2.IMREAD_GRAYSCALE) gray_pil Image.fromarray(gray).resize((32, 32)) current_hash imagehash.phash(gray_pil) if last_hash is not None: if (last_hash - current_hash) threshold: # 画面几乎没变跳过 time.sleep(interval) continue last_hash current_hash idx 1 save_path os.path.join(save_dir, fslide_{idx:04d}_{int(time.time())}.png) img.save(save_path, PNG) print(f已保存: {save_path}) time.sleep(interval)这个脚本运行起来之后你就可以挂机去看视频了。它会按照固定间隔自动截屏重复画面直接丢弃。跑完一轮之后frames 目录下剩下的就是“有效页面序列”。如果你希望脚本在视频结束后自动停止可以在录制前手动定义一个总时长比如200分钟脚本到点自动退出或者用手动 CtrlC 结束。整个过程我用一个简单的状态机控制等待启动、自动循环截屏、用户中断或指定次数结束。配合视频播放器快捷键启动脚本后你需要做的就是专心听课完全不用碰鼠标。4.2 参数计算怎么定间隔、怎么定阈值表格直接给结论参数建议值说明截屏间隔2~5秒课程语速快取2秒常规取3秒哈希距离阈值5画面有大量动画取8~10纯PPT取3~5OCR 放大倍数2倍视频分辨率低于1080p时可以提到3倍OCR 置信度下限0.6想保留更多内容可降到0.5但乱码会变多PDF 图片格式PNG截图直接存PNG不要转JPEG避免二次失真间隔怎么理解200分钟课如果间隔3秒理想极限是4000张但经过判重后一般只剩150~400张算是很合理的笔记厚度。间隔太小比如0.5秒会让判重模块计算量暴涨而且容易把老师讲题过程中动画过渡的中间帧也存下来间隔太大比如30秒又可能漏掉一闪而过的重点。这里给一个粗算过程假设200分钟课12000秒你能忍受的最大处理时间是课后脚本跑30分钟那么 OCR 需要处理的图片张数限制在1800张左右单张约1秒。所以有效去重后的数量控制在1800以内就没问题。实测下来200分钟课程、3秒间隔阈值5的组合最终生成300~500张图片等OCR跑完用不了多少时间。4.3 实操记录一场200分钟网课的产出数据我拿自己一门“网络运维自动化”方向的技术课做了完整实测。原始时长 205 分钟播放倍率 1.5 倍速实际听课时间约 137 分钟。脚本间隔设3秒理论截屏数约 2740 张图像去重阈值过滤后有效截图 311 张。OCR 阶段CPU模式下大约跑了8分钟单张平均1.5秒左右最终生成 PDF 154页文件大小约 86MB因为保留了原图PNG同时带完整文字层。这份 PDF 的好处我感受很深想搜“Ansible”这个词按 CtrlF 输入所有相关页面瞬间定位出来想回顾某个原理图翻到对应页能看到当时老师在白板上手写的推导过程右下角还保留着视频进度条的时间戳点击对应时间可以回看原视频。时间戳那一行其实是我后来加的做法很简单——截屏时把time.time()格式化成可读的HH:MM:SS写到页脚。就是这个小小的附加信息让我回看视频的定位效率翻了倍。5. 常见问题与避坑手册5.1 问题速查表遇到最多的几个问题我整理成了一份速查表现象可能原因解决方案截出来的图全是黑的视频播放器有硬件加速渲染全屏时硬件覆盖层独占了画面关掉播放器硬件加速选项或者使用窗口模式播放截图坐标偏了系统DPI缩放不是100%以物理分辨率为准在脚本里读取 monitor 信息后再算区域去重后漏掉了一整页阈值设太严或者页面是渐变过渡适当放宽阈值建议≥5动画页设置到10OCR 识别率特别低原视频文字太小截图放大2~3倍再送OCR必要时做一次形态学处理PDF 体积过大保留PNG原图用JD无损压缩优化体积或者改存JPEG质量90视觉上几乎不影响图片突然不保存了磁盘空间用完或目录权限不对检查磁盘余量和写权限截屏卡顿区域选得太大把视频窗口尽量调小只截内容区域不截边框5.2 我踩过的几个隐蔽坑第一个坑是播放器截图黑屏。第一次全屏播放时我截下来的图全是黑的查了很久才发现是显卡硬件加速在做“覆盖层合成”系统API截不到播放器的输出内容。解决办法很土但很有效把播放器从全屏改成窗口模式然后把窗口拉到最大确保内容区域尽量大。如果你用的播放工具在窗口模式下也黑屏就去选项里关掉硬件加速。第二个坑是弹幕和水印引发的重复截屏。我一开始的阈值设成了0结果因为鼠标动一下都会造成像素变化去重直接失效截出了几千张几乎一模一样的图。后来又把阈值调成了20结果老师在PPT上快速翻页时第二页还没来得及完全翻出来就被判为“与上一页相似”而丢掉了。最后折中到5才算稳下来。第三个坑是OCR 高置信度的幻觉。PaddleOCR 在识别数学公式符号时经常自信地输出一堆看似通顺但完全不对的文本置信度还很高。所以我在生成PDF时有个原则可搜索文字只是索引不是权威内容真正要引用时永远以原始截图为准。这也算是“无损”策略的一个逆向思维——我把OCR当成搜索辅助而不是内容替代。还有一个值得分享的细节是录制过程中尽量别移动播放器窗口。一旦窗口位置变化region区域会错位截到的内容要么是桌面要么是播放器边框。我的处理方式是在脚本里把视频窗口名称做个检测每10秒检查一次窗口位置是否在预期范围内不在就弹出警告避免录了一堆废图。这个思路其实还可以扩展。比如你想把直播课程、线上会议、本地视频全部变成可检索笔记无缝替换或者想做一个“自动截图语音转文字”的双通道笔记流把这些能力组合起来后基本就是个知识采集系统了。我个人在使用这套工具后最大的变化是终于不用再为“要不要截这一页”分心可以把全部注意力放在理解内容上。笔记质量提升的同时整理笔记的时间几乎降为零。最后再分享一个小技巧截图保存的文件名最好带上时间戳和序列号。刚开始我用纯序号命名结果回看某页想找对应视频位置完全没法定位。后来改成slide_0012_20240514103005.png这种格式文件名本身就印刻了时间信息查回放特别方便。你也可以在 PDF 页脚画一条细分隔线下面写一行小字标页码和时间戳翻笔记时会有一种在用“出版级教材”的错觉——实际上这套方法本来就能把课程录像变成你专属的、可检索的、保留原始细节的知识资产。
返回列表