一文教会你用Python实现最有效的剪切板实时监控
前言
上网浏览网页的时候,看见好的内容免不了要使用复制粘贴,但是我们看到的内容、心里想要的内容和实际粘贴后的内容往往不一致。数据的获取始于复制,终于粘贴,那么问题来了,在这中间系统做了哪些操作,我们怎么能控制它呢?

人生苦短,我用python,查阅相关资料之后发现有很多不一样的实现方式,如利用内置ctypes模块、tk模块,第三方模块如跨平台的pyperclip模块、clipboard模块、pywin.win32clipboard模块等等,大部分都封装好了简洁易用的高级接口,方便我们直接使用。
基于强迫症的心理,本文分析比较了几种主流的方式,对他们逐一进行源码分析、读写性能实测,最后选择了读写速度最快的一种做出一个实时剪切板监控小案例,以供大家参考。
小案例实现的功能如下:
- 实时监测ctrl+c剪切板写入事件,去除剪切板中指定字符或文本,如某些文字的后缀 (¬_¬)瞄。
- 使用正则对某些文本进行智能替换,如将python2格式的代码转换为python3格式。
方式一:调用第三方pyperclip模块
In [1]: import pyperclip
In [2]: data = pyperclip.paste()
In [3]: data
Out[3]: "print 'Hello World'\r\n————————————————\r\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。\r\n原文链接:https://blog.csdn.net/.../article/details/..."
In [4]: data = data[7:12]
In [5]: pyperclip.copy(data)
In [6]: pyperclip.paste()
Out[6]: 'Hello'
源码调用: 内置ctypes模块中的ctypes.windll.user32接口编写,和pandas包的代码一致,代码位置:pandas.io.clipboard.windows,代码引用如下
import ctypes
windll = ctypes.windll
safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData)
safeGetClipboardData.argtypes = [UINT]
safeGetClipboardData.restype = HANDLE
safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData)
safeSetClipboardData.argtypes = [UINT, HANDLE]
safeSetClipboardData.restype = HANDLE
优点: 跨平台,接口调用方便简洁
缺点: 剪切板的数据格式只支持utf-8文本,频繁读写速度较慢
方式二:调用第三方win32clipboard模块
In [1]: import win32clipboard
...:
...: def clipboard_get():
...: """获取剪贴板数据"""
...: win32clipboard.OpenClipboard()
...: data = win32clipboard.GetClipboardData()
...: win32clipboard.CloseClipboard()
...: return data
...:
...: def clipboard_set(data):
...: """设置剪贴板数据"""
...: win32clipboard.OpenClipboard()
...: win32clipboard.SetClipboardData(13, data)
...: win32clipboard.CloseClipboard()
...: return True
...:
In [2]: data = clipboard_get()
In [3]: data
Out[3]: "print 'Hello World'\r\n————————————————\r\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上 原文出处链接及本声明。\r\n原文链接:https://blog.csdn.net/.../article/details/..."
In [4]: clipboard_set(data[7:12])
Out[4]: True
In [5]: clipboard_get()
Out[5]: 'Hello'
源码调用: C源码封装,python接口调用如下
def GetClipboardData(*args, **kwargs): # real signature unknown
pass
def SetClipboardData(*args, **kwargs): # real signature unknown
pass
优点: 原生C封装读写速度最快,支持多种剪切板数据格式
缺点: 只适用于windows平台,高频率读写会报错需要小心处理,utf-8格式之外的数据格式需要熟悉winuser.h库自行设计编写
方法三:调用内置tkinter模块
In [1]: from tkinter import *
...:
...: r = Tk()
In [2]: data = r.clipboard_get()
In [3]: data
Out[3]: "print 'Hello World'\n————————————————\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文 出处链接及本声明。\n原文链接:https://blog.csdn.net/.../article/details/..."
In [4]: r.clipboard_append(data[7:12])
In [5]: r.clipboard_get()
Out[6]: 'Hello'
注意: 在win10系统测试后发现,使用tkinter模块只能获取剪切板数据,不能将数据写入剪切板,如果在外部调用clipboard_append方法将字符串写入剪切板后销毁Tk对象,系统剪切板进程会被tk接管锁死,此时在其他的应用按ctrl+v,粘贴的应用会直接处于卡死的状态,或者粘贴后内容为空。
如果还是通过Tk()对象将数据写入剪切板,只能采取下面的方法,设置延迟销毁Tk对象,系统剪切板数据才会被更新,否则内容还是为空(实测如果设置0.2秒以内的频率读取,剪切板还是为空,这就很鸡肋了):
from tkinter import *
import time
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append('some string')
r.update()
time.sleep(.2)
r.update()
r.destroy()
源码调用: C源码封装,python接口调用如下
# 读取剪切板数据:
_tkinter.tkapp('clipboard', 'get')
# 写入剪切板数据:
_tkinter.tkapp('clipboard', 'append')
剪切板读写速度测试结果
实时监控小案例:
import win32clipboard
import re
import time
def clipboard_get():
"""获取剪贴板数据"""
win32clipboard.OpenClipboard()
data = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT)
win32clipboard.CloseClipboard()
return data
def clipboard_set(data):
"""设置剪贴板数据"""
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT, data)
win32clipboard.CloseClipboard()
# 初始化替换字符列表,相比于正则使用replace函数进行单字符替换更快
char_list = [('(', '('),
(')', ')'),
('“', '"'),
('”', '"'),
('‘', '\''),
('’', '\''),
('print ', 'print '),
('版权声明:本文为CSDN', '版权声明:本文为CSDN'),
]
# 预编译正则替换匹配表达式
# 匹配python2格式的 print函数文本
sub_print = re.compile(r'\bprint\s+(.+)')
# 匹配csdn复制自带的版权声明后缀文本
sub_csdn = re.compile(r'—+\s+版权声明:本文为CSDN.*\s+原文链接.*')
# 指定场景 sub替换函数:python2格式的 print函数 替换为python3格式
def sub_fn(s):
return 'print(' + s.group(1).strip() + ')\r\n'
# 判断如果没有要替换的字符则返回None,有则执行替换操作,先进行字符列表replace,再执行reg.sub(sub_fn, txt)
def char_replace_reg_sub(txt):
new_txt = txt
# 对字符列表中字符 逐一判断,如果字符在文本中 则replace替换,如果都不在 则return None,不用再进行替换操作
i = 0
for old_char, new_char in char_list:
if old_char in new_txt:
i += 1
new_txt = new_txt.replace(old_char, new_char)
if i == 0:
return None
print('-' * 150, '\n【After char replace】:', new_txt)
# 对指定场景替换 使用正则re.sub
new_txt = sub_print.sub(sub_fn, new_txt)
new_txt = sub_csdn.sub('', new_txt)
print('【After sub replace:】', new_txt)
return new_txt
def main():
"""后台脚本:每隔0.2秒,读取剪切板文本,检查有无指定字符或字符串,如果有则执行替换"""
# recent_txt 存放最近一次剪切板文本,初始化值只多执行一次paste函数读取和替换
recent_txt = clipboard_get()
replaced_txt = char_replace_reg_sub(recent_txt)
clipboard_set(recent_txt if replaced_txt is None else replaced_txt)
while True:
# txt 存放当前剪切板文本
txt = clipboard_get()
# 剪切板内容和上一次对比如有变动,再进行内容判断,判断后如果发现有指定字符在其中的话,再执行替换
if txt != recent_txt:
# print(f'txt:{txt}')
new_txt = char_replace_reg_sub(txt) # 没查到要替换的子串,返回None
if new_txt is not None:
clipboard_set(new_txt)
# 更新 recent_txt 为替换之后的文本,便于下次与 txt 剪切板文本对比,判断内容有无更新
recent_txt = new_txt
# 检测间隔(延迟0.2秒)
time.sleep(0.2)
if __name__ == '__main__':
main()
运行效果:
-----------------------------------------------------------------------------------------
【Copy text】:
print 'Hello World' \r\n————————————————\r\n版权声明:本文为CSDN博主「...」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上 原文出处链接及本声明。\r\n原文链接:https://blog.csdn.net/.../article/details/...)
-----------------------------------------------------------------------------------------
【After replace:】:
print('Hello World')
-----------------------------------------------------------------------------------------
参考链接:
Stack Overflow:https://stackoverflow.com/questions/579687/how-do-i-copy-a-string-to-the-clipboard-on-windows-using-python

一文教会你用Python实现最有效的剪切板实时监控的更多相关文章
- Python源码文件中带有中文时,输出乱码
Python源码文件中带有中文时,文件头应加注释: #!/usr/bin/env python # -*- coding: utf-8 -*- 第一行注释是为了告诉Linux/OS X系统,这是一个P ...
- Python 开发者在迁移到 Go(lang) 时需要知道哪些事?
[编者按]本文最早由 Repustate 发布,主要介绍将代码迁移至 Go(lang) 时的注意事项.文章系国内 ITOM 管理平台 OneAPM 编译呈现,以下为正文. 这是一篇讲述将大块 Pyth ...
- 解决Python向MySQL数据库插入中文数据时出现乱码
解决Python向MySQL数据库插入中文数据时出现乱码 先在MySQL命令行中输入如下语句查看结果: 只要character_set_client character_set_database ch ...
- python自带编译器在写入文件时闪退,或者一步步执行到写入时提示8170。解决办法:
用python黑框运行程序写入文件时闪退,或一行行运行到写入时提示8170数字. 经试验,为文件路径错误导致. with open("1.doc", "wb") ...
- python踩坑系列之导入包时下划红线及报错“No module named”问题
python踩坑系列之导入包时下划红线及报错“No module named”问题 使用pycharm编写Python时,自己写了一个包(commontool),在同级另一个路径下(fileshand ...
- 我大概知道他在说什么了,是对内存单元的竞争访问吧。Python有GIL,在执行伪码时是原子的。但是伪码之间不保证原子性。 UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情况。你先看看发送速率,还有是否带有拥塞控制。
我大概知道他在说什么了,是对内存单元的竞争访问吧.Python有GIL,在执行伪码时是原子的.但是伪码之间不保证原子性. UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情 ...
- 用python调试Appium和雷电模拟器连接时出现Original error: Could not find 'adb.exe' in PATH
用python调试Appium和雷电模拟器连接时出现Original error: Could not find 'adb.exe' in PATH 确定环境变量没错,用管理员启动Appium就不会出 ...
- 一文教你读懂Python中的异常信息
正文共:11813 字 2 图 预计阅读时间: 30 分钟 原文:https://realpython.com/python-traceback/ 译者:陈祥安 原文有所改动. 在写 Python 代 ...
- vim中编写python代码使用python-mode和syntastic插件时警告(Warning)的消除
问题: 在Vim使用了syntastic后,编写代码时,可以对代码错误和警告进行相对实时的了解,对编写代码有很大的帮助.同时这个插件和python-mode一起工作时,可以对python代码的编写提供 ...
随机推荐
- Q:简单实现URL只能页面跳转,禁止直接访问
sessionStorage 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据,且不同标签页的session不能共享,通过此特性来控制某个页面只能通过上级页面同标签页跳转 ...
- leetcode-158周赛-5224-掷筛子模拟
题目描述: 方法:动态规划O(6∗6∗n∗15) 递归: from functools import lru_cache class Solution: def dieSimulator(self, ...
- thinkphp 数据库缓存
默认的数据库驱动位于Think\Db\Driver命名空间下面,驱动类必须继承Think\Db类,每个数据库驱动必须要实现的接口方法包括(具体参数可以参考现有的数据库驱动类库): 驱动方法 方法说明 ...
- 「题解」:x
问题 A: x 时间限制: 1 Sec 内存限制: 256 MB 题面 题面谢绝公开. 题解 赛时想到了正解并且对拍了很久.对拍没挂,但是评测姬表示我w0了……一脸懵逼. 不难证明,如果对于两个数字 ...
- 欧拉函数+反演——2019hdu多校6588
\[ 求\sum_{i=1}^{n}(\sqrt[3]i,i)\\ 首先转化一下这个式子,考虑对于i\in[j^3,(j+1)^3-1],\sqrt[3]i=j\\ 所以可以枚举所有j,然后对i\in ...
- NOIp2018集训test-9-18
T1.Conjugate 只能选没选过的点,看成如果选了选过的堆的点就不管它继续选.如果第一次选到某一堆的点在第一次选到第一堆的点之前,这一堆对答案就会有1的贡献.那么a[i]有贡献的概率是a[i]和 ...
- CF1265B Beautiful Numbers
题意 给一个长度为\(n\)的排列\(P\),求对于\(1\) 到 \(n\)中的每个数\(m\),是否能找到一段长度为\(m\)的区间使得区间内的数是一个\(1\)到\(m\)的排列. 输出一个\( ...
- python round, ceil, flooor
round(num, n) 保留n位小数 round(80.23456, 2) : 80.23 round(100.000056, 3) : 100.0 round(-100.000056, 3) : ...
- Greenplum(PostgreSql)中函数内游标的使用实例
直接上代码,具体整体函数定义就不上了,只写关键部分: --定义两个变量 DECLARE CCUR REFCURSOR; -- 游标变量 RECORD1 RECORD; -- 记录变量,用来存储游标遍历 ...
- centos7.4安装高可用(haproxy+keepalived实现)kubernetes1.6.0集群(开启TLS认证)
目录 目录 前言 集群详情 环境说明 安装前准备 提醒 一.创建TLS证书和秘钥 安装CFSSL 创建 CA (Certificate Authority) 创建 CA 配置文件 创建 CA 证书签名 ...