前言

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

  人生苦短,我用python,查阅相关资料之后发现有很多不一样的实现方式,如利用内置ctypes模块、tk模块,第三方模块如跨平台的pyperclip模块、clipboard模块、pywin.win32clipboard模块等等,大部分都封装好了简洁易用的高级接口,方便我们直接使用。

  基于强迫症的心理,本文分析比较了几种主流的方式,对他们逐一进行源码分析、读写性能实测,最后选择了读写速度最快的一种做出一个实时剪切板监控小案例,以供大家参考。

小案例实现的功能如下:

  1. 实时监测ctrl+c剪切板写入事件,去除剪切板中指定字符或文本,如某些文字的后缀 (¬_¬)瞄。
  2. 使用正则对某些文本进行智能替换,如将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')
-----------------------------------------------------------------------------------------

参考链接:

微软开发文档:https://docs.microsoft.com/zh-cn/windows/win32/dataxchg/using-the-clipboard?redirectedfrom=MSDN#_win32_Copying_Information_to_the_Clipboard

Stack Overflow:https://stackoverflow.com/questions/579687/how-do-i-copy-a-string-to-the-clipboard-on-windows-using-python

一文教会你用Python实现最有效的剪切板实时监控的更多相关文章

  1. Python源码文件中带有中文时,输出乱码

    Python源码文件中带有中文时,文件头应加注释: #!/usr/bin/env python # -*- coding: utf-8 -*- 第一行注释是为了告诉Linux/OS X系统,这是一个P ...

  2. Python 开发者在迁移到 Go(lang) 时需要知道哪些事?

    [编者按]本文最早由 Repustate 发布,主要介绍将代码迁移至 Go(lang) 时的注意事项.文章系国内 ITOM 管理平台 OneAPM 编译呈现,以下为正文. 这是一篇讲述将大块 Pyth ...

  3. 解决Python向MySQL数据库插入中文数据时出现乱码

    解决Python向MySQL数据库插入中文数据时出现乱码 先在MySQL命令行中输入如下语句查看结果: 只要character_set_client character_set_database ch ...

  4. python自带编译器在写入文件时闪退,或者一步步执行到写入时提示8170。解决办法:

    用python黑框运行程序写入文件时闪退,或一行行运行到写入时提示8170数字. 经试验,为文件路径错误导致. with open("1.doc", "wb") ...

  5. python踩坑系列之导入包时下划红线及报错“No module named”问题

    python踩坑系列之导入包时下划红线及报错“No module named”问题 使用pycharm编写Python时,自己写了一个包(commontool),在同级另一个路径下(fileshand ...

  6. 我大概知道他在说什么了,是对内存单元的竞争访问吧。Python有GIL,在执行伪码时是原子的。但是伪码之间不保证原子性。 UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情况。你先看看发送速率,还有是否带有拥塞控制。

    我大概知道他在说什么了,是对内存单元的竞争访问吧.Python有GIL,在执行伪码时是原子的.但是伪码之间不保证原子性.   UDP丢包,你是不是做了盲发?没有拥塞控制的情况下,确实会出现丢包严重的情 ...

  7. 用python调试Appium和雷电模拟器连接时出现Original error: Could not find 'adb.exe' in PATH

    用python调试Appium和雷电模拟器连接时出现Original error: Could not find 'adb.exe' in PATH 确定环境变量没错,用管理员启动Appium就不会出 ...

  8. 一文教你读懂Python中的异常信息

    正文共:11813 字 2 图 预计阅读时间: 30 分钟 原文:https://realpython.com/python-traceback/ 译者:陈祥安 原文有所改动. 在写 Python 代 ...

  9. vim中编写python代码使用python-mode和syntastic插件时警告(Warning)的消除

    问题: 在Vim使用了syntastic后,编写代码时,可以对代码错误和警告进行相对实时的了解,对编写代码有很大的帮助.同时这个插件和python-mode一起工作时,可以对python代码的编写提供 ...

随机推荐

  1. Hbase和Hive在大数据架构中处在不同位置

    先放结论:Hbase和Hive在大数据架构中处在不同位置,Hbase主要解决实时数据查询问题,Hive主要解决数据处理和计算问题,一般是配合使用.一.区别:Hbase: Hadoop database ...

  2. Cucumber:启动类配置

    Cucumber:启动类配置 package com.steps; import cucumber.api.CucumberOptions; import cucumber.api.junit.Cuc ...

  3. 通过jquery获取页面信息

    获取浏览器显示区域(可视区域)的高度 : $(window).height(); 获取浏览器显示区域(可视区域)的宽度 :$(window).width(); 获取页面的文档高度 $(document ...

  4. DataWorks2.0的“业务流程”与1.0的“工作流”的对比

    DatwWorks终于升级2.0了,心情万分激动之余,又有一丝担忧.因为,没法再创建新的旧版工作流了...新版抛弃了“工作流”这个概念,引入了“业务流程”和“解决方案”两个新的概念.于是,作为团队Le ...

  5. thinkphp session驱动

    默认的session驱动的命名空间是Think\Session\Driver,并实现下面的驱动接口:大理石构件哪家好 方法说明 接口方法 打开Session open($savePath, $sess ...

  6. mac系统下通过安装包的形式安装mongdb

    下载地址 mongoDB mongoDB 图形化管理软件 MongoDB Compass 下载完成后,进行解压,解压后的文件进行重命名 mongodb 然后前往/usr/local,将mongodb文 ...

  7. NX二次开发-比较两个string是否相等

    NX11+VS2013 #include <uf.h> #include <uf_ui.h> UF_initialize(); string A = "ABC&quo ...

  8. PaperWeekly 第五期------从Word2Vec到FastText

    PaperWeekly 第五期------从Word2Vec到FastText 张俊 10 个月前 引 Word2Vec从提出至今,已经成为了深度学习在自然语言处理中的基础部件,大大小小.形形色色的D ...

  9. How to Add Swap on CentOS

    About Linux Swapping Linux RAM is composed of chunks of memory called pages. To free up pages of RAM ...

  10. GPIO, AFIO

    o read/write the AFIO_EVCR, AFIO_MAPR and AFIO_EXTICRX registers, the AFIO clock should first be ena ...