python屏幕截图并保存

您所在的位置:网站首页 使用python截取屏幕截图的3种方法 python屏幕截图并保存

python屏幕截图并保存

2023-08-17 08:23| 来源: 网络整理| 查看: 265

起因

在极客学院讲授《使用Python编写远程控制程序》的课程中,涉及到查看被控制电脑屏幕截图的功能。

如果使用PIL,这个需求只需要三行代码:

from PIL import ImageGrab

pic = ImageGrab.grab()

pic.save('1.jpg')

但是考虑到被控端应该尽量的精简,对其他模块尽量少的依赖,这样才能比较方便的部署,因此我考虑能否有一种方法,不依赖PIL来实现截图的功能。

思路

由于被控端使用了win32api, 因此有一个方法:

win32api.keybd_event

这个方法可以模拟键盘的按键动作。因此,解决方法就比较的明显了:

模拟键盘上面的“Print Screen” 键按下

从剪贴板中读取出截图

将截图保存到本地

第一步非常的简单,实用win32api 和 win32con,两行代码就能实现:

import win32api

import win32con

win32api.keybd_event(win32con.VK_SNAPSHOT, 0)

其中win32con这个库里面包含了很多定义好的和Windows相关的常量,而VK_SNAPSHOT就是Print Screen键的键位码。后面的数字0表示截取整个屏幕。如果改成数字1,表示截取当前窗口。

那么现在问题来了,在不实用PIL的情况下,如何将剪贴板你们的图片保存到本地?

win32api有一个模块 win32clipboard 是负责剪贴板相关的操作。它有一个方法:

win32clipboard.GetClipboardData(formats)

这个方法可以从剪贴板里面读取数据。但是需要指定数据的格式。从这里可以查看到更多的标准剪贴板格式(Standard Clipboard Formats).

一开始我使用的formats是CF_BITMAP,程序返回的是一串整数,怀疑应该是一个内存地址。这也和这个format的描述:

A handle to a bitmap (HBITMAP).

是一致的,它是一个handle。

我也尝试过CF_TIFF, 不过程序直接报错了,可见我使用Print Screen截图以后,剪贴板里面的图片格式并不是TIFF。

经过查阅其他资料,我最后确定使用了CF_DIB。

A memory object containing a BITMAPINFO structure followed by the bitmap bits.

这个描述说明,CF_DIB返回的是一个内存对象,包含了BIT格式图片的信息。经过测试使用:

win32clipboard.GetClipboardData(win32con.CF_DIB)

以后,可以得到一个很大的字符串。显然这个字符串就是图片的内容了。但是当我把这个字符串写入到bmp格式的文件后,却发现图片无法打开。

解决办法

在StackOverflow上,我遇到了一个非常好的老先生: Mr. martineau他为了解答了问题,并给我提供了解决办法。以下内容翻译自martineau先生的回答,原文请戳->http://stackoverflow.com/a/35885108/3922976

你的方法的主要问题在于,你写入文件的字符串缺少了.bmp 文件头,这个文件头是BITMAPFILEHEADER结构。

为了创建这个文件头,使用GetClipboardData()返回的字符串必须要进行解码(decoded)。对于CF_DIB格式来说,返回的字符串的前面一部分就是BOTMAPINFOHEADER。

对于各种各样有不同种类压缩的DIB来说,这种文件头结构是非常的普遍的。不过幸好对截图来说,只需要简单的无压缩的RGBA像素。

由于BOTMAPFILEHEADER被放在了bf0ffBits的区域里,所以事情就变得很容易了。而其他的情况,例如大尺度的颜色表跟在BITMAPINFOHEADER 和像素数组的开头。

(这一段我看不太懂,还请如果有能正确解释这段话的朋友指正。原文是:

That fact makes things much easier because otherwise determining the value to put in the bfOffBits field of the BITMAPFILEHEADER would be complicated by the fact that in most other cases there's also a variably-sized color table following the BITMAPINFOHEADER and the start of the pixel array.)

下面的代码是一个简单的例子(仅仅针对这个需求):

import ctypes

from ctypes.wintypes import *

import win32clipboard

from win32con import *

import sys

class BITMAPFILEHEADER(ctypes.Structure):

_pack_ = 1 # structure field byte alignment

_fields_ = [

('bfType', WORD), # file type ("BM")

('bfSize', DWORD), # file size in bytes

('bfReserved1', WORD), # must be zero

('bfReserved2', WORD), # must be zero

('bfOffBits', DWORD), # byte offset to the pixel array

]

SIZEOF_BITMAPFILEHEADER = ctypes.sizeof(BITMAPFILEHEADER)

class BITMAPINFOHEADER(ctypes.Structure):

_pack_ = 1 # structure field byte alignment

_fields_ = [

('biSize', DWORD),

('biWidth', LONG),

('biHeight', LONG),

('biPLanes', WORD),

('biBitCount', WORD),

('biCompression', DWORD),

('biSizeImage', DWORD),

('biXPelsPerMeter', LONG),

('biYPelsPerMeter', LONG),

('biClrUsed', DWORD),

('biClrImportant', DWORD)

]

SIZEOF_BITMAPINFOHEADER = ctypes.sizeof(BITMAPINFOHEADER)

win32clipboard.OpenClipboard()

try:

if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_DIB):

data = win32clipboard.GetClipboardData(win32clipboard.CF_DIB)

else:

print('clipboard does not contain an image in DIB format')

sys.exit(1)

finally:

win32clipboard.CloseClipboard()

bmih = BITMAPINFOHEADER()

ctypes.memmove(ctypes.pointer(bmih), data, SIZEOF_BITMAPINFOHEADER)

if bmih.biCompression != BI_BITFIELDS: # RGBA?

print('insupported compression type {}'.format(bmih.biCompression))

sys.exit(1)

bmfh = BITMAPFILEHEADER()

ctypes.memset(ctypes.pointer(bmfh), 0, SIZEOF_BITMAPFILEHEADER) # zero structure

bmfh.bfType = ord('B') | (ord('M')



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3