基于Python实现剪切板实时监控方法解析


Posted in Python onSeptember 11, 2019

前言

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

人生苦短,我用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_board方法时,系统剪切板进程会被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')

剪切板读写速度测试结果

基于Python实现剪切板实时监控方法解析

实时监控小案例:

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 相关文章推荐
python生成指定长度的随机数密码
Jan 23 Python
Python的Flask框架中web表单的教程
Apr 20 Python
Python使用ntplib库同步校准当地时间的方法
Jul 02 Python
Python实现在线暴力破解邮箱账号密码功能示例【测试可用】
Sep 06 Python
python 调用c语言函数的方法
Sep 29 Python
django中的HTML控件及参数传递方法
Mar 20 Python
python多线程共享变量的使用和效率方法
Jul 16 Python
Python3 Tkinkter + SQLite实现登录和注册界面
Nov 19 Python
Python迷宫生成和迷宫破解算法实例
Dec 24 Python
解决Django no such table: django_session的问题
Apr 07 Python
python实现一个简单RPC框架的示例
Oct 28 Python
Python卷积神经网络图片分类框架详解分析
Nov 07 Python
python连接、操作mongodb数据库的方法实例详解
Sep 11 #Python
面向对象学习之pygame坦克大战
Sep 11 #Python
Python整数与Numpy数据溢出问题解决
Sep 11 #Python
python中通过selenium简单操作及元素定位知识点总结
Sep 10 #Python
用Python画一个LinkinPark的logo代码实例
Sep 10 #Python
Pytorch修改ResNet模型全连接层进行直接训练实例
Sep 10 #Python
django drf框架自带的路由及最简化的视图
Sep 10 #Python
You might like
PHP图片处理类 phpThumb参数用法介绍
2012/03/11 PHP
解析php中curl_multi的应用
2013/07/17 PHP
PHP使用CURL获取302跳转后的地址实例
2014/05/04 PHP
浅析PHP编程中10个最常见的错误
2014/08/08 PHP
10个php函数实用却不常见
2015/10/13 PHP
学习YUI.Ext 第七天--关于View&JSONView
2007/03/10 Javascript
Json字符串转换为JS对象的高效方法实例
2013/05/01 Javascript
IE 下Enter提交表单存在重复提交问题的解决方法
2014/05/04 Javascript
JavaScript中Null与Undefined的区别解析
2015/06/30 Javascript
javascript新闻跑马灯实例代码
2020/07/29 Javascript
利用Angular.js限制textarea输入的字数
2016/10/20 Javascript
Vuejs 用$emit与$on来进行兄弟组件之间的数据传输通信
2017/02/23 Javascript
Javascript前端经典的面试题及答案
2017/03/14 Javascript
微信小程序 action-sheet 反馈上拉菜单简单实例
2017/05/11 Javascript
angular4 共享服务在多个组件中数据通信的示例
2018/03/30 Javascript
微信小程序scroll-view实现滚动穿透和阻止滚动的方法
2018/08/20 Javascript
layui添加动态菜单与选项卡 AJAX请求的例子
2019/09/25 Javascript
JavaScript之Blob对象类型的具体使用方法
2019/11/29 Javascript
React组件设计模式之组合组件应用实例分析
2020/04/29 Javascript
vue element ui validate 主动触发错误提示操作
2020/09/21 Javascript
Python编程之event对象的用法实例分析
2017/03/23 Python
Python双向循环链表实现方法分析
2018/07/30 Python
Python中模块(Module)和包(Package)的区别详解
2019/08/07 Python
IronPython连接MySQL的方法步骤
2019/12/27 Python
Python如何使用字符打印照片
2020/01/03 Python
一款纯css3实现的非常实用的鼠标悬停特效演示
2014/11/05 HTML / CSS
空指针到底是什么
2012/08/07 面试题
函授生自我鉴定
2014/03/25 职场文书
市场营销调查计划书
2014/05/02 职场文书
环境卫生标语
2014/06/09 职场文书
师德培训心得体会2016
2016/01/09 职场文书
Html5生成验证码的示例代码
2021/05/10 Javascript
Redis 彻底禁用RDB持久化操作
2021/07/09 Redis
php实例化对象的实例方法
2021/11/17 PHP
CSS实现五种常用的2D转换
2021/12/06 HTML / CSS
Python实现批量自动整理文件
2022/03/16 Python