Python使用Pillow和pyautogui实现全屏截图代码
详细讲解在Python中使用Pillow的ImageGrab与PyAutoGUI实现全屏截图的完整代码、环境依赖配置、底层调用原理、多显示器捕获及Retina高分屏DPI坐标对齐方案。
ImageGrab 模块或 PyAutoGUI 库的 screenshot() 函数。Pillow 的调用路径短、执行性能高,适合纯粹的图像获取、保存与后端处理;PyAutoGUI 则将截图能力与键鼠自动化控制无缝打通,适合构建桌面自动化机器人与 UI 自动化测试。本文完整展示两套方案的基础代码、进阶扩展(如多显示器抓取与内存流中转),并深入剖析高分屏(Retina / DPI)缩放机制与平台权限注意事项。
一、准备工作:依赖安装与系统环境核验
在编写代码前,需要确保运行环境中已正确安装对应的第三方库。在命令行终端中执行以下命令进行安装:
pip install pillow pyautogui
安装完成后,可以在 Python 交互环境中快速核对模块是否能够正常导入:
python -c "import PIL; import pyautogui; print('环境准备就绪')"
不同操作系统在底层图形捕获上的支持机制不同,在正式运行前应特别注意以下系统特有配置:
- Windows 系统:开箱即用。Windows 底层基于标准 GDI 接口捕获桌面位图,普通用户权限即可正常运行,无额外弹窗权限阻拦。
- macOS 系统:自 macOS 10.15 (Catalina) 起,系统引入了 TCC 隐私保护机制。首次运行截图脚本的终端程序(如 Terminal、iTerm2)或集成开发环境(如 VS Code、PyCharm)必须在“系统设置 -> 隐私与安全性 -> 屏幕录制”中勾选授权。如果未授权,调用截图接口不会直接抛出异常,而是会截出一张仅有系统桌面壁纸或全黑的空图片。
- Linux 系统:若运行在 X11 桌面环境,PyAutoGUI 通常依赖
scrot命令行工具,可以通过sudo apt-get install scrot(Debian/Ubuntu)补齐;若运行在无图形界面的云服务器(Headless),则需要借助Xvfb提供虚拟显示缓冲区。
二、使用 Pillow (ImageGrab) 实现全屏截图
Pillow 是 Python 中最常用的图像处理基础库。其内置的 ImageGrab 模块提供了原生的屏幕位图捕获能力,无需额外安装庞大的自动化套件。
1. 基础全屏截图代码
以下为使用 Pillow 抓取当前屏幕并保存到磁盘的完整代码:
from PIL import ImageGrab
def capture_fullscreen_pillow(save_path="fullscreen_pillow.png"):
# 截取主屏幕全屏,返回 PIL.Image.Image 对象
screenshot = ImageGrab.grab()
# 将图像保存至指定文件路径
screenshot.save(save_path)
# 打印截图基本信息进行校验
print(f"Pillow 截图完成!尺寸: {screenshot.size},色彩模式: {screenshot.mode}")
return screenshot
if __name__ == "__main__":
capture_fullscreen_pillow()
ImageGrab.grab() 方法在不传任何参数时,默认抓取主显示器的全屏区域,返回一个 PIL.Image.Image 对象。该对象可以直接调用 save() 保存为常见的图像格式(如 PNG、JPEG、WEBP 等)。如果保存为 JPEG 格式,还可以通过传递 quality=85 参数来兼顾清晰度与文件体积。
2. 多显示器(多屏幕)全景截图
在双屏或多屏开发环境中,很多读者希望一次性截取所有显示器的完整拼合画面。在 Pillow 中,只需向 grab() 传递 all_screens=True 参数即可实现:
from PIL import ImageGrab
# 启用 all_screens 参数抓取所有连接的显示器
multiscreen_img = ImageGrab.grab(all_screens=True)
multiscreen_img.save("multiscreen_pillow.png")
print(f"多显示器拼接全景分辨率: {multiscreen_img.size}")
all_screens=True 目前在 Windows 和 macOS 系统下均受支持。当两个显示器的物理分辨率或缩放比例不同时,Pillow 会以包容所有屏幕的最小外接矩形构建图像画布,未被屏幕覆盖的留白区域默认填充为黑色。
三、使用 PyAutoGUI 实现全屏截图
PyAutoGUI 是一个跨平台的 GUI 自动化控制库。如果你的截图需求是“截取屏幕 -> 寻找目标按钮 -> 控制鼠标点击”自动化工作流中的一环,使用 PyAutoGUI 可以让代码结构更为简练。
1. 基础全屏截图代码
PyAutoGUI 的 screenshot() 函数支持直接传入文件名进行保存,也可以像 Pillow 一样返回 Image 对象:
import pyautogui
def capture_fullscreen_pyautogui(save_path="fullscreen_pyautogui.png"):
# 一行代码完成全屏截图并写入文件
# 若不传入参数,则只在内存中返回 Image 对象而不写入硬盘
screenshot = pyautogui.screenshot(save_path)
# 获取逻辑屏幕尺寸供自动化定位对比
screen_width, screen_height = pyautogui.size()
print(f"PyAutoGUI 截图保存成功!逻辑屏幕分辨率: {screen_width}x{screen_height}")
return screenshot
if __name__ == "__main__":
capture_fullscreen_pyautogui()
2. 配合延迟倒计时截图
在实际自动化测试中,常常需要在用户打开特定窗口或弹出菜单后再触发截图。可以结合 Python 标准库的 time.sleep() 实现倒计时截屏:
import time
import pyautogui
def delayed_capture(countdown=3, save_path="delayed_screenshot.png"):
print(f"请切换到目标窗口,将在 {countdown} 秒后执行全屏截图...")
for i in range(countdown, 0, -1):
print(f"倒计时: {i} 秒")
time.sleep(1)
img = pyautogui.screenshot(save_path)
print(f"已截屏并保存至: {save_path}")
if __name__ == "__main__":
delayed_capture(3)
四、底层调用机制与架构关系剖析
许多初学者会好奇:Pillow 与 PyAutoGUI 在全屏截图上是否存在本质差异?
答案是:在 Windows 和 macOS 系统中,PyAutoGUI 的 screenshot() 函数底层直接委托调用了 Pillow 的 ImageGrab.grab()。PyAutoGUI 内部在导入阶段会检测系统中是否安装了 Pillow;如果存在 Pillow,它便将抓取屏幕的底层工作交由 Pillow 执行。
通过架构图可以清晰看出两者的层级差异:
- Pillow (ImageGrab):属于轻量级纯图像库。它编译了原生 C 扩展,直接对接 Windows GDI(通过
BitBlt复制设备上下文 DC)或 macOS Quartz CoreGraphics 框架(通过CGWindowListCreateImage提取显存图像缓存)。 - PyAutoGUI:属于高阶综合库。它在 Pillow 基础之上包装了统一的跨平台调用外观,并补充了键鼠消息模拟、屏幕模板匹对(
locateOnScreen)等自动化生态支持。在缺少 Pillow 的 Linux 环境下,它会降级通过调用scrot命令行工具来完成截图。
五、高分屏 (Retina / Windows DPI) 缩放与像素对齐
在搭载 2K/4K 屏幕的高分屏笔记本或启用了 Retina 视网膜屏的 Mac 电脑上,读者常常会遇到一个令人困惑的问题:
运行 pyautogui.size() 显示屏幕尺寸为 (1920, 1080),但保存出来的截图文件查看详细属性却发现尺寸是 (3840, 2160)。如果在截图上测量了一个按钮的像素坐标 (800, 600) 并直接调用 pyautogui.click(800, 600),鼠标往往会点击在完全错误的位置。
这一现象的根源在于逻辑视口坐标(Logical Points)与物理像素矩阵(Physical Pixels)的分离:
- 逻辑坐标(UI 坐标):操作系统为了防止在高分屏下文字和图标过小,设置了 150%、200%(2.0x)甚至更高的 DPI 缩放比率。
pyautogui.size()和pyautogui.click()使用的就是这种经过缩放的逻辑度量单位。 - 物理位图:
ImageGrab.grab()截取的是显卡渲染帧缓冲区中的真实物理像素点,因此图像尺寸会被完整放大 2 倍。
坐标换算的正确做法:如果通过图像识别找到了截图中的目标中心点 (img_x, img_y),需要先除以缩放系数,再传递给 PyAutoGUI 进行点击:
import pyautogui
from PIL import ImageGrab
# 1. 抓取物理像素图像
shot = ImageGrab.grab()
physical_w, physical_h = shot.size
# 2. 获取逻辑分辨率
logical_w, logical_h = pyautogui.size()
# 3. 计算缩放倍率(例如 Retina 屏通常为 2.0)
scale_x = physical_w / logical_w
scale_y = physical_h / logical_h
# 假设在物理截图中识别出目标位于 (800, 600)
target_img_x, target_img_y = 800, 600
# 4. 换算为逻辑坐标后点击
real_click_x = target_img_x / scale_x
real_click_y = target_img_y / scale_y
print(f"缩放倍率: {scale_x},实际应点击坐标: ({real_click_x}, {real_click_y})")
# pyautogui.click(real_click_x, real_click_y)
六、进阶技巧:无磁盘 IO 的内存流中转
在网络爬虫、远程监控或 Web API 接口开发中,频繁将截图写入本地固态硬盘再读取会带来不必要的磁盘磨损与 I/O 延迟。可以通过 Python 标准库中的 io.BytesIO 将截图保存在内存中:
import io
from PIL import ImageGrab
def get_screenshot_bytes(image_format="PNG"):
# 截取全屏
img = ImageGrab.grab()
# 创建内存二进制流缓冲区
buffer = io.BytesIO()
# 写入内存缓冲区而非硬盘
img.save(buffer, format=image_format)
# 获取字节数据(可直接用于网络传输或 Base64 编码)
img_bytes = buffer.getvalue()
buffer.close()
print(f"已在内存中生成 {image_format} 截图流,总大小: {len(img_bytes)} 字节")
return img_bytes
if __name__ == "__main__":
raw_data = get_screenshot_bytes("PNG")
七、常见故障排查与运行避坑
| 故障现象 | 根本原因 | 排查与解决方法 |
|---|---|---|
| macOS 下截图全黑或只有桌面背景 | 当前运行终端或 IDE 未获得系统的“屏幕录制”授权 | 前往“系统设置 -> 隐私与安全性 -> 屏幕录制”,找到当前运行的 Python、VS Code 或 Terminal,打开授权开关并重启应用。 |
| Linux 无桌面服务器下报找不到显示设备 | 无 X11 图形环境(Headless 环境未指定 DISPLAY 变量) | 使用虚拟帧缓冲:sudo apt install xvfb,使用 xvfb-run python your_script.py 运行。 |
Linux 报错 scrot not found |
未预装底层截图工具 | 在终端执行 sudo apt-get install scrot 完成系统级命令安装。 |
| 截图文件体积过大(单张 10MB+) | 高分屏多屏幕全景采用无损 PNG 编码保存 | 使用 JPEG 或 WEBP 格式并设置合理画质参数:img.save('output.jpg', quality=85, optimize=True)。 |
八、方案选型与特性总结
综合来看,Pillow 与 PyAutoGUI 各有侧重。选择哪一个取决于你的核心业务逻辑:
简明选型结论:
- 如果你只需要定时抓取屏幕、做图像分析、多屏巡检或将截图通过 API 发送,优先选用 Pillow (ImageGrab)。它的依赖极小、速度更快,且对多屏拼接支持更为原生态。
- 如果截图是为了后续进行自动化按钮识别、鼠标连点、按键模拟或 RPA 脚本开发,优先选用 PyAutoGUI,一套套件即可串联整个自动化执行链路。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









