ARTICLE DETAIL

资讯详情

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

Python本地行为记录器:自动追踪窗口、剪贴板与键鼠活动

Python本地行为记录器:自动追踪窗口、剪贴板与键鼠活动 1. 先说说我为什么要折腾这个记录神器先交代一个很现实的场景我白天写代码、看文档、回消息、开各种网页查资料忙到下午六点领导问我今天干了啥我脑子里只剩一片空白。要说做了三个功能、修了两个 bug但是具体几点做的、中间被什么打断、查了哪些资料完全回忆不起来。刚开始我以为是记性问题后来发现真正的问题是——信息过载。一天下来电脑上经过的信息量实在太大人的短期记忆根本存不住。后来我试着用现成的时间管理软件像什么 RescueTime、ManicTime也都用过。它们能记录你花了多少时间在某个软件上但有个共同毛病数据归人家平台管导出格式受限而且只告诉你“你在 Chrome 上待了 3 小时”没法告诉你这 3 小时具体看了哪些页面、复制了哪些内容、打开了哪些文件。我要的是能回放当天工作场景的“数字足迹”不是一张干巴巴的饼图。于是我自己动手写了一套本地运行的程序文件操作记录工具把程序使用、文件操作、网页浏览、剪切板内容、键鼠活跃度这五类信息全部无感记录下来存在本地 SQLite 里。用了一个月以后效果出乎意料地好周报不用硬憋了写技术复盘能找到原始素材甚至还能分析出自己上午效率高还是下午效率高。这篇文章就把这套工具的思路、代码和踩过的坑完整分享出来适合有 Python 基础、想在 Windows 上做本地行为记录的人参考。2. 整体方案设计本地存储、事件驱动、可检索2.1 技术选型与理由选型这件事我一开始就定了三个原则本地优先、轻量、可扩展。数据必须存在自己电脑上不上传云端这是底线。程序要轻量不能像某些监控软件一样挂着 300 兆内存。可扩展的意思是今天记录五种数据明天想加一种不能推倒重来。技术栈最终定为 Python 3.10 加 pywin32配合 psutil 和 SQLite。选 Python 不是因为它性能好而是因为 Windows API 封装齐全写起来快。窗口信息用 win32gui 拿剪贴板监听用 ctypes 调系统 API进程信息交给 psutil存储直接用一个 SQLite 文件搞定。整个项目核心代码不到 600 行没有任何第三方重型框架后续维护非常省心。为什么不选 C 或者 C#老实说C# 写 Windows 桌面工具也很舒服但 Python 的优势在于数据分析生态。我后期想把记录数据导出来做统计用 pandas 直接读 SQLite 就行不用来回转换格式。如果你更熟悉 C#完全可以照着同样思路用 WinForms 重写一版核心逻辑不受语言限制。2.2 数据链路采集、去重、入库、查询这套系统的数据流可以分成四层每一层职责非常清楚采集层负责从系统拿原始事件。窗口切换用轮询剪贴板变化用系统监听键鼠活跃度用系统 API 查询。采集层只负责“拿到数据”不关心数据怎么存。清洗层把原始数据里的噪声去掉。比如同一个窗口标题在五分钟内反复出现就合并成一条记录剪贴板连续两次复制相同内容只保留最新一条。存储层清洗后的数据写入 SQLite。每条记录都带精确到秒的时间戳这是后面所有统计的基础。查询层提供一组查询命令按天输出时间线、按关键词搜剪贴板内容、按进程名统计使用时长。这个分层最大的好处是每一层都能单独替换。比如你不想用轮询采集窗口改成监听事件只需要动采集层存储结构完全不用变。2.3 数据库表结构设计数据库我设计了三张核心表没有过度设计够用就好events表记录程序和窗口活动字段包括 id、时间戳、进程名、窗口标题、窗口类名。这表是整个系统的地基按时间倒序查就能还原你一天的工作时间线。clipboard_history表记录剪贴板内容字段包括 id、时间戳、内容类型文本、图片、文件列表、内容预览和完整内容。图片这类二进制数据我建议单独存文件数据库里只留文件路径和缩略图否则数据库会膨胀得很快。activity_log表记录键鼠活跃度字段包括 id、开始时间、结束时间、活跃状态和活跃级别。这张表不存具体按键内容只存“这段时间人是否在电脑前操作”用于生成专注度曲线。三张表之间没有外键关系统一靠时间戳关联。这个设计是故意的因为查询场景基本都是一天一天地看跨表关联需求很少加外键反而拖慢写入速度。3. 五大核心模块的实现细节3.1 程序与窗口活动记录程序使用记录是整个工具的灵魂也是最容易实现的部分。Windows 系统提供了一个取巧的办法任何时候只有一个前台窗口拿到这个窗口的句柄就能反查出它属于哪个进程、窗口标题是什么。核心逻辑有三步。第一步调用GetForegroundWindow拿到前台窗口句柄第二步用GetWindowText拿标题用GetWindowThreadProcessId拿进程 ID第三步通过进程 ID 查到进程名和执行路径。窗口标题是信息量最大的字段——Chrome 的标题通常是“网页标题 - Google Chrome”记事本的标题就是文件名Word 的标题是文档名。把这堆标题按时间排起来就是一份天然的工作日志。轮询间隔我设为 3 秒一次。太快会白白消耗 CPU太慢会漏掉窗口切换。实测 3 秒间隔下CPU 占用不到百分之一而且能捕捉到绝大多数窗口切换。这里有个重要优化如果前后两次拿到的窗口句柄没变就不写入数据库只有句柄变了才算一次新事件。这样能避免海量重复数据。3.2 剪切板历史记录剪切板监听比窗口记录麻烦一点。Windows 从 Vista 开始提供AddClipboardFormatListener接口窗口注册后剪贴板一有变化就给这个窗口发WM_CLIPBOARDUPDATE消息。Python 里用 ctypes 调这两个 API 就能实现。我记录的剪贴板内容分三类文本直接存全文适合搜代码片段、搜网址图片先把 PNG 数据落盘数据库里记路径文件列表记录复制的文件路径因为日常操作里“复制了一个文件”也是重要行为线索。必须提醒一句剪贴板里可能会出现密码、验证码、身份证号这类敏感信息。我自己的方案是加了一个排除规则当窗口标题包含“密码”“登录”“验证码”等关键词时跳过剪贴板记录。这个规则不完美但对于个人使用来说能挡住九成敏感场景。另外所有剪贴板数据严格只存本地绝不联网这个底线从设计第一天就定死了。3.3 文件操作记录文件操作记录是最难做的一块。理想方案是挂文件系统筛选驱动可以拦截所有进程的打开、修改、删除操作但对个人项目来说太重了驱动签名、兼容性都是坑。我用了两条轻量路线组合。第一条是从剪贴板文件列表里抓。用户在某文件夹里复制文件、剪切文件剪贴板会变成文件列表格式直接解析路径就能知道用户最近碰了哪些文件。第二条是解析常用软件的窗口标题和命令行参数。比如 VS Code 的标题通常带当前打开的文件名浏览器标题带页面标题记事本标题带文件名。这些信息配合进程名能还原出“哪个时间点在用编辑器打开什么项目”。这条路线不完整但胜在零侵入、稳定可靠。如果想补全文件操作细节可以定期扫描候选目录下的最近修改时间把文件变动时间线并进记录里。3.4 网页浏览记录网页浏览记录我建议直接读浏览器自己的历史库不要自己去 Hook 网络请求。Chrome 和 Edge 的历史数据都存成一个 SQLite 数据库路径固定里面有一张urls表记录所有访问过的网址一张visits表记录访问时间。读这两张表就能还原浏览器产生过的每一次访问。但有个前提浏览器历史库有时间延迟通常关闭浏览器时才落盘而且浏览器运行时数据库文件会被锁住。解决方法是拷贝一份历史文件到临时目录再读避免和浏览器抢占文件锁。另外一个坑是Chrome 历史库只保留最近 90 天的数据太早的记录查不到。窗口记录和浏览器历史配合起来效果最好。窗口标题告诉用户“当前正在看什么”历史库告诉用户“具体是哪个 URL、几点几分打开的”。把这两个数据源合并网页浏览轨迹就完整了。3.5 键鼠活跃度追踪键鼠活跃度这个模块我先说清楚边界它只统计“人有没有在操作”不记录按键的具体内容。我不会去做键盘记录器那类东西那是另一个性质的工具。我这里的实现方式是调用GetLastInputInfo这个 API 能告诉你上一次用户产生键盘或鼠标输入的时间。定期调用就能算出用户已经空闲多久。逻辑是这样的每 30 秒检查一次距离上次输入不超过 60 秒就标记为“活跃”否则标记为“空闲”。连续活跃超过 15 分钟记成一段高专注时段活跃两分钟、空闲五分钟记成一段低效碎片时段。这套数据价值非常大它能告诉你一天里真正专注工作的时间其实不到一半也能帮你发现自己总在哪个时间段摸鱼。还有一个衍生指标按星期几统计平均专注时长用来对比哪几天状态最好。很多人以为自己对工作节奏很了解真把数据拉出来看完全不是自己以为的那样。4. 实操记录从零搭一套能用的本地记录器4.1 环境准备与踩坑开发环境是 Windows 10 加 Python 3.10。装依赖就两个包pywin32用于 Windows APIpsutil用于进程信息。命令很简单pip install pywin32 psutil这里我踩过一个大坑而且是很多新手都会遇到的PowerShell 执行策略禁止运行脚本导致 npm、claude 这类命令一执行就报“无法加载文件因为在此系统上禁止运行脚本”。这个问题的根源是 PowerShell 默认执行策略是 Restricted不是命令本身坏了。解决办法是在管理员 PowerShell 里执行一条命令Set-ExecutionPolicy -Scope CurrentUser RemoteSigned设置成 RemoteSigned 意思是本地脚本可以运行从网上下载的脚本必须有签名。这个策略比全部放开安全得多。如果你用的是其他终端也有类似的提示先查执行策略再动手装环境别急着重装软件。还有一个高频问题是conda或nvcc提示“不是内部或外部命令”。这跟当前这个项目本身关系不大但是用 Python 的人迟早会碰到。原因通常是安装时没有勾选把程序加入 PATH或者多个 Python 版本打架。个人项目里我直接把 Python 装在默认路径环境变量保持干净避免后面排查问题时分心。4.2 核心代码事件采集层窗口活动采集的关键函数不长核心逻辑如下import psutil import win32gui import win32process def get_foreground_app(): hwnd win32gui.GetForegroundWindow() if hwnd 0: return None, None, None win_title win32gui.GetWindowText(hwnd) _, pid win32process.GetWindowThreadProcessId(hwnd) try: proc psutil.Process(pid) proc_name proc.name() proc_path proc.exe() except psutil.Error: proc_name, proc_path None, None return proc_name, proc_path, win_title这个函数的返回值是进程名、执行路径、窗口标题三元组。实际运行时每 3 秒调用一次如果窗口句柄没变化直接丢弃结果。有一点要注意窗口标题可能为空尤其是纯画界面的窗口进程。空标题的记录并不是垃圾它至少能告诉你“某个无标题窗口在前台待了多久”。剪贴板监听稍微复杂一点需要创建一个消息窗口并注册剪贴板监听器import ctypes from ctypes import wintypes WM_CLIPBOARDUPDATE 0x031D user32 ctypes.windll.user32 def create_clipboard_listener(): hwnd user32.CreateWindowExW( 0, STATIC, clipboardListener, 0, 0, 0, 0, 0, None, None, None, None ) user32.AddClipboardFormatListener(hwnd) return hwnd, user32窗口创建好以后程序进入消息循环每次收到WM_CLIPBOARDUPDATE就触发一次剪贴板读取。这个模式是 Windows GUI 程序的标准写法。剪贴板内容用win32clipboard读取文本直接取图片走GetClipboardData拿位图转存 PNG文件列表用CF_HDROP格式解析。4.3 核心代码存储与去重存储层我用 Python 自带的sqlite3写日志数据时开事务批量提交能显著提升性能import sqlite3 class ActivityStore: def __init__(self, db_path): self.conn sqlite3.connect(db_path) self._init_schema() def _init_schema(self): self.conn.executescript( CREATE TABLE IF NOT EXISTS events ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts TEXT NOT NULL, proc_name TEXT, proc_path TEXT, win_title TEXT ); CREATE TABLE IF NOT EXISTS clipboard_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, ts TEXT NOT NULL, content_type TEXT, content TEXT, file_path TEXT ); CREATE TABLE IF NOT EXISTS activity_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, start_ts TEXT, end_ts TEXT, active INTEGER ); ) self.conn.commit()写入去重的策略是事件表里对比上一条记录的窗口句柄剪贴板表里对比上一次内容活动表里每 30 秒生成一条状态记录连续状态相同的做合并更新。合并更新的语句用“先查再更新”的方式避免插入大量重复行数据库体积能控制在每天几兆以内。4.4 查询与导出命令行小工具数据记录得再多查不出来就是废数据。我写了一个简单查询脚本支持三种常见场景python query.py --day 2025-01-15 --timeline python query.py --search clipboard --keyword ERROR python query.py --day 2025-01-15 --stats--timeline按时间顺序输出当天的窗口切换记录每一行是“时间 进程名 窗口标题”。--search clipboard在剪贴板历史里搜关键词这功能写代码时尤其好用——你昨天复制过一段 SQL 模板今天忘存了搜一下就能找回来。--stats统计各进程当天占比生成一个简单的使用时长排行。导出功能我是直接生成 Markdown 格式的日报告方便贴进工作日志或者发给同事。这个报告由“时间线”“剪贴板摘录”“专注时段”“使用时长排行”四部分组成每周末手动跑一次周报素材就齐了。4.5 开机自启与稳定性保障记录工具最怕的就是忘记开。我把程序注册成开机自启在 Windows 任务计划程序里建了一个任务登录时启动、失败后每五分钟重试一次。选任务计划程序而不是启动文件夹是因为任务计划可以设置“仅当用户登录时运行”“异常退出后重新启动”这些规则可靠性高得多。程序自身的稳定性也要考虑。Windows API 偶尔会抛异常比如某些进程句柄失效。我在主循环外面包了 try-except异常只记日志不退出。另外加了简单的看门狗主程序每隔十分钟往心跳文件写一次时间戳外部脚本检查心跳超过五分钟没更新就拉起新的进程。5. 常见问题与排查实录这套工具跑起来以后我陆续遇到不少问题挑几个最典型的列出来很多人可能会遇到一模一样的坑。现象根本原因解决办法窗口标题频繁为空部分 UWP 应用和系统进程不提供标题记录进程名兜底不强行依赖标题剪贴板内容读不到目标进程关闭了剪贴板访问或内容格式特殊OpenClipboard失败时延时重试最多重试 3 次Windows 提示剪贴板不可用剪贴板被某进程锁定常见于远程控制软件重启 explorer.exe或注销重登SQLite 报 database is locked多线程同时写库所有写入集中到单线程加写锁sqlite3 导入数据中文乱码终端编码不是 UTF-8查询脚本开头执行chcp 65001CPU 占用过高轮询间隔太短或循环里做了多余操作窗口轮询间隔调到 3 秒以上这里展开说两个最坑的问题。第一个是剪贴板不可用。有段时间远程控制电脑发现本地剪贴板时不时抽风复制了粘贴不出来或者提示剪贴板不可用。排查了一圈发现不是程序的问题是远程控制软件的剪贴板服务抢占了系统剪贴板锁。处理办法也很粗暴任务管理器杀掉 explorer.exe 再重启剪贴板功能就恢复了。如果你的场景里经常要远程操作建议把远程软件的剪贴板同步关掉本地记录工具就能正常工作。第二个是运行脚本时被 PowerShell 拦截。这个在前面讲过但我想再强调一次。很多程序安装完以后文档里给的命令都是让你在终端直接输结果你自己环境里执行策略卡着导致各种“无法加载文件”的报错。这种问题跟软件本身一点关系都没有先把 PowerShell 执行策略换成 RemoteSigned再重新试一次九成问题能解决。还有一个细节有时候改了策略后旧终端窗口不会生效必须新开一个终端再跑命令。6. 数据用起来的经验与体会工具写完以后很多人以为重点在“记录”但用了一个月我才意识到重点在“回看”。每天早上花三分钟看昨天的数据工作效率会有非常明显的变化。因为记录不会骗人你以为自己在认真写代码数据显示你可能三分钟切一次窗口经常刷购物网站剪贴板里大量复制的是聊天内容。这些直观的数字比任何自我感觉都真实。我个人的体会是这套工具最适合三类用途。第一类是时间管理。每周导出一次统计看时间都花在了哪里然后针对性地调整工作安排。第二类是资料找回。剪贴板历史就是你的私人碎片知识库什么时候复制过什么搜出来直接用这个价值远超我的预期。第三类是工作复盘。写周报、写年终总结的时候往前翻每天的窗口切换记录所有素材都在那儿写起来特别快。第三大块也是我自己最受益的数据驱动改进。我试过连续三天启用“写代码时段不打开购物网站”的自我约束效果立竿见影因为每天的数据就是最诚实的反馈。后来我又加了一个小功能把每天的专注时间和 git 提交数量、代码行数关联起来形成一套自己的效率指标。这个项目后续最值得扩展的方向我觉得有两个。一个是自动生成周报把一周的窗口记录、剪贴板摘录、专注时段汇总成结构化文档基本不用人再改。另一个是做一个本地网页展示界面用浏览器打开就能看时间线不用在终端里敲命令。这些扩展都不难核心的地基已经打好了剩下的只是锦上添花。最后再说一个使用时要注意的底线这类记录工具本质上是把个人行为数字化数据一旦泄露风险比别人看你聊天记录还大。所以数据库文件、图片缓存目录我都放在系统盘的加密目录里并且定期清理超过 30 天的剪贴板历史。你自己用的时候一定要先想清楚数据安全边界再决定记录哪些内容、保留多长时间。在这件事上谨慎永远不会多余。
返回列表