最稳方式是用 pygetwindow 定位窗口句柄和坐标,再用 mss 精准捕获该区域;它绕过桌面合成器、不依赖焦点、速度快且像素准确。

用 pygetwindow + mss 截指定窗口最稳
直接靠 pyautogui.screenshot() 截全屏再裁剪,既慢又容易因窗口遮挡/缩放失真。真正可靠的方式是:先用 pygetwindow 定位窗口句柄和坐标,再用 mss 精准捕获该区域——它绕过桌面合成器,不依赖鼠标焦点,截图快且像素级准确。
安装两个包即可:pip install pygetwindow mss。注意 pygetwindow 在 Windows 上依赖 pywin32,macOS/Linux 需搭配 pyobjc 或 python-xlib,但跨平台稳定性不如 Windows。
- 务必检查窗口标题是否完全匹配(区分大小写),可用
list(pygetwindow.getAllTitles())先看真实标题 - 如果目标窗口最小化,
win.isActive为False,mss仍能截,但部分 UWP 应用(如 Xbox、邮件)可能返回黑图 - 避免用
win.left/win.top直接拼路径——多显示器下坐标系混乱,应统一用win.topleft和win.size
窗口找不到?试试模糊匹配或 PID 绑定
pygetwindow.getWindowsWithTitle("微信") 找不到,大概率是标题动态变化(比如“微信-张三”“微信-文件传输助手”)。别硬改字符串,改用正则或进程 ID 锁定更可靠。
Windows 下可结合 psutil 拿 PID:import psutil; [p for p in psutil.process_iter() if "WeChat.exe" in p.name()] ,再用 pygetwindow.getWindowByPID(pid)(需 Windows + pywin32 支持)。
立即学习“Python免费学习笔记(深入)”;
- macOS 不支持 PID 查找窗口,只能靠
pygetwindow.getWindowsWithTitle()配合re.search()模糊匹配标题 - Linux 下
xdotool search --name命令可辅助定位,但需提前os.popen()调用,增加依赖 - 如果窗口刚启动,加
time.sleep(0.5)再查,避免getWindowsWithTitle返回空列表
保存文件名带毫秒时间戳,防覆盖
只用 datetime.now().strftime("%Y%m%d_%H%M%S") 会导致同一秒多次截图被覆盖。必须加入毫秒或微秒,且注意 Windows 文件名不支持 : 和 * 等符号。
推荐写法:f"screenshot_{datetime.now().strftime('%Y%m%d_%H%M%S')}{datetime.now().microsecond//1000:03d}.png",例如 screenshot_20240520_142301042.png。
- 不要用
time.time()直接转字符串——浮点数转字符串可能带小数点,Windows 不允许文件名含.(除了扩展名) - macOS/Linux 对文件名限制少,但仍建议统一用下划线分隔,避免后续在 Windows 上同步出错
- 保存前检查目录是否存在:
os.makedirs(os.path.dirname(path), exist_ok=True),尤其路径含子文件夹时
mss 截图区域偏移 1 像素?关掉系统缩放
在 125% 或 150% 缩放的 Windows 上,pygetwindow 返回的 width/height 是逻辑像素,而 mss 按物理像素捕获,导致截图缺边或带黑边。这不是代码 bug,是 DPI 感知问题。
临时解法:在 Python 脚本开头加 import ctypes; ctypes.windll.shcore.SetProcessDpiAwareness(1)(仅 Windows),强制进程高 DPI 感知。但要求 Python >= 3.8 且运行在管理员权限下才 100% 生效。
- 更稳妥的做法是:在程序属性 → 兼容性 → 更改高 DPI 设置 → 勾选“替代高 DPI 缩放行为”,缩放执行人选“应用程序”
- 如果必须支持缩放,得用
win32gui.GetWindowRect()替代pygetwindow获取原始坐标,再手动乘以缩放因子(需调用GetDpiForWindow) - 这个坑藏得深——只有在高分屏+非 100% 缩放+窗口非最大化时才会暴露,本地测试常被忽略


















