python自动从粘贴板里复制保存图片

由于经常写作,需要为文章准备一些配图,通常这些配图是从百度图片搜索里准备的。在准备图片时,我遇到了两个小麻烦:

  1. 有些图片的格式是webp,需要在另存为时指定格式
  2. 有些图片需要剪切,剪切后不能直接复制到桌面,我总是先复制到qq聊天窗口,然后另存为,步骤繁琐

有没有什么办法能让我轻松的获得复制的图片呢?经过一番思索,我研究出一个方案:
让程序每隔一秒钟监听一次剪贴板,如果剪贴板里是图片,就保存到指定文件夹里,这样,在复制图片1秒钟后程序就自动的帮我把图片保存下来。

说干就干。

1. 验证可行性

首先,我需要验证这个思路的可行性,经过调研,得知PIL库的ImageGrab模块提供的grabclipboard函数可以获得剪贴板里的内容并且可以判断是否为图片,这就好办了

from PIL import Image, ImageGrab

def paste_pic():
    im = ImageGrab.grabclipboard()
    if isinstance(im, Image.Image):
        im.save(r'C:\Users\zhangdongsheng\Desktop\图片\pic.png')


if __name__ == '__main__':
    paste_pic()

代码准备好以后,我在百度图片里随意复制了一张图片,然后运行程序,图片果然被保存到了指定目录里,哈哈,完全可行。

2. 设置文件名称

我不能从剪贴板里获得图片的文件名,因此需要为它设置文件名称,就用时间这种最简单的方式,2021-07-16-10-25-39,用时间命名在查找图片时也提供了一定的检索能力

import os
from PIL import Image, ImageGrab
from datetime import datetime

def get_file_name():
    return datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + '.jpg'

def paste_pic():
    im = ImageGrab.grabclipboard()
    if isinstance(im, Image.Image):
        filename = os.path.join(r'C:\Users\zhangdongsheng\Desktop\图片', get_file_name())
        im.save(filename)

if __name__ == '__main__':
    paste_pic()

顺便将图片的格式改为jpg,不损失太多图片质量的情况下比png格式图片要小很多。

3. 以天为单位存储

图片存储的多了,自然会变得杂乱无章,尽管文件名称可以排序但还是应当按天来存储,这样便于查找

import os, time
from PIL import Image, ImageGrab
from datetime import datetime

root_dir = r'C:\Users\zhangdongsheng\Desktop\图片'

def get_file_name():
    return datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + '.jpg'

def make_dir_by_day():
    day_dir = datetime.now().strftime("%y-%m-%d")
    day_dir = os.path.join(root_dir, day_dir)
    if not os.path.exists(day_dir):
        os.mkdir(day_dir)

    return day_dir

def paste_pic():
    im = ImageGrab.grabclipboard()
    if isinstance(im, Image.Image):
        day_dir = make_dir_by_day()
        filename = os.path.join(day_dir, get_file_name())
        im.save(filename)


if __name__ == '__main__':
    paste_pic()

4. 清空剪贴板

程序最终要循环执行,每隔一秒钟去剪贴板里检查一下是否有数据,数据是否是图片,在第一次保存图片后,图片仍然在剪贴板里,1秒钟后再次执行paste_pic时又会复制保存一次,这是万万不行的。

在图片被保存后,直接清空剪贴板

def paste_pic():
    im = ImageGrab.grabclipboard()
    if isinstance(im, Image.Image):
        day_dir = make_dir_by_day()
        filename = os.path.join(day_dir, get_file_name())
        im.save(filename)
        clear_clipboard()

def clear_clipboard():
    from ctypes import windll
    if windll.user32.OpenClipboard(None):  # 打开剪切板
        windll.user32.EmptyClipboard()  # 清空剪切板
        windll.user32.CloseClipboard()  # 关闭剪切板

5. 运行起来

def main():
    while True:
        paste_pic()
        time.sleep(1)


if __name__ == '__main__':
    main()

每隔一秒钟,运行一次paste_pic函数,实际测试,效果非常好,包括屏幕截取也能保存下来,因为截屏后的图片也保存在剪贴板里。

6. 剪贴板保留图片30秒

在前面的设计中,图片被保存后清空剪贴板,想了想,这样做有不妥之处。复制图片并不都是为了保存成文件,有时仅仅是想粘贴在某处,比如发送给微信的朋友或者直接粘贴在有道云笔记里。针对这种情况,我修改了删除策略,图片在剪贴板里存储超过39秒以后再执行删除

old_info = {
    'size': 0,
    'time': datetime.now()
}


def is_old_pic(im):
    im_size = len(im.tobytes())
    if old_info['size'] == im_size:     # 是旧图片,已经保存过
        return True

    # 新图片,记录新图片的信息
    old_info['size'] = im_size
    old_info['time'] = datetime.now()
    return False


def paste_pic():
    im = ImageGrab.grabclipboard()
    if isinstance(im, Image.Image):
        if is_old_pic(im):
            # 如果是旧图片,存在时间超过30秒以后再清空
            if (datetime.now() - old_info['time']).seconds > 30:
                clear_clipboard()
        else:
            day_dir = make_dir_by_day()
            filename = os.path.join(day_dir, get_file_name())
            im.save(filename)

使用old_info字典记录图片的信息,size保存图片大小,用于比较剪贴板里的图片是否为旧图片,time保存新图片的保存时间,用于计算图片被保存在剪贴板里的时间。

经过改造后,图片可以正常保存,但不会在保存后立即删除,而是在剪贴板里保存30秒,这30秒的时间里,你可以将图片粘贴到你想粘贴的位置上。

import os, time
from PIL import Image, ImageGrab
from datetime import datetime

root_dir = r'C:\Users\zhangdongsheng\Desktop\图片'

def get_file_name():
    return datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + '.jpg'

def make_dir_by_day():
    day_dir = datetime.now().strftime("%y-%m-%d")
    day_dir = os.path.join(root_dir, day_dir)
    if not os.path.exists(day_dir):
        os.mkdir(day_dir)

    return day_dir

old_info = {
    'size': 0,
    'time': datetime.now()
}


def is_old_pic(im):
    im_size = len(im.tobytes())
    if old_info['size'] == im_size:     # 是旧图片,已经保存过
        return True

    # 新图片,记录新图片的信息
    old_info['size'] = im_size
    old_info['time'] = datetime.now()
    return False


def paste_pic():
    try:
        im = ImageGrab.grabclipboard()
    except:
        print('打开剪贴板失败')
        return
    
    if isinstance(im, Image.Image):
        if is_old_pic(im):
            # 如果是旧图片,存在时间超过30秒以后再清空
            if (datetime.now() - old_info['time']).seconds > 30:
                clear_clipboard()
        else:
            day_dir = make_dir_by_day()
            filename = os.path.join(day_dir, get_file_name())
            im.save(filename)


def clear_clipboard():
    from ctypes import windll
    if windll.user32.OpenClipboard(None):  # 打开剪切板
        windll.user32.EmptyClipboard()  # 清空剪切板
        windll.user32.CloseClipboard()  # 关闭剪切板


def main():
    while True:
        paste_pic()
        time.sleep(1)


if __name__ == '__main__':
    main()

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案