YOLO813

python控制电脑的鼠标+键盘

    我们知道,python是有很多实用的模块可以帮助我们处理表格,文档等,举一个简单的例子,例如,需要按照日期格式重命名几百个文件夹的名字,我们可以使用shutil模块来处理,这里其实有两个先决条件,存在shutil模块和我们知道shutil模块的作用,假如不存在shutil类似的模块,我们是否也能在电脑上实现自动化处理呢?

    答案是肯定的,利用模块pyautogui。这个模块的作用在于控制电脑的鼠标和键盘,你可以把它想象成一个机械臂,我们敲击键盘,点击鼠标能处理的事情它都能做,像这种类人程序与计算机的应用交互,称之为“图形用户界面自动化”或“GUI自动化”。

    摘录官网的几个问题:

  •     问:PyAutoGUI是否可以在Android,iOS或平板电脑/智能手机应用程序上工作。
  •     答:PyAutoGUI仅在Windows,macOS和Linux上运行。
  •     问:PyAutoGUI是否可以在多显示器设置上使用。
  •     答:不,现在PyAutoGUI仅处理主监视器。
  •     问:PyAutoGUI是否执行OCR?
  •     答:不可以,但这在计划中。
  •     问:PyAutoGUI可以进行键盘记录或检测当前是否按下了按键?
  •     答:否,PyAutoGUI目前无法进行记录检测。


    以下的测试内容在苹果电脑上进行,python版本为3.7.3。


    注意事项:由于GUI自动化控制了鼠标和键盘,一旦出错我们可能连点击关闭程序的操作都无法完成,所以如何停止失去控制的GUI自动化程序是必修课(当然,关机是最简单粗暴的办法,但是运行的程序就失去了意义),pyautogui模块默认启用了故障安全功能(fail-safe feature),其作用是在调用PyAutoGUI的函数时,我们将鼠标移动到电脑显示器四个角落任意一个顶点,则程序会抛出一个FailSafeException异常,从而终止程序的运行,而每一次调用PyAutoGUI函数之后会存在1/10秒的延迟以方便用户可以猛拽鼠标滑动到指定的地点。如果想要关闭故障安全功能只需配置如下代码即可:

pyautogui.FAILSAFE = False

但是强烈不推荐关闭该功能!当开启了故障安全模式,我们也可以使用如下代码修改延迟时间

>>> pyautogui.PAUSE = 2.5

    安装,借助镜像安装快一些,安装成功后

#检查安装是否成功
>>> import pyautogui

    坐标系参考,原点为屏幕左上角(0,0),向右x坐标增加,向下y坐标增加,所有的坐标均为正整数,而且鼠标的顶点位置会比实际尺寸的数值少1个像素

# 以下代码返回我的电脑尺寸,为元组
>>> pyautogui.size()
Size(width=1366, height=768)
# 获取宽度、高度
width,height = pyautogui.size()
# 鼠标移至电脑右下角,获取位置
>>> pyautogui.position()
Point(x=1365, y=767)

    移动鼠标,两种方式,一种是moveTo,移动到指定位置;一种是moveRel,相较现在的位置进行移动。如果没有指定持续时间,则默认为0,瞬间到达。

pyautogui.moveTo(0,0, duration=5)
pyautogui.moveRel(200,0, duration=5)

    如何实时获取鼠标位置?利用循环不断调用position函数即可,当用户输入了control+c,会触发KeyboardInterrupt,打印用户退出

>>> import pyautogui
>>> try:
...     while True:
...         print(pyautogui.position())
... except KeyboardInterrupt:
...     print("User quit!\n")

如果将打印的格式变好看一点,以下代码将只会输出一行字符串,即鼠标当时所在的坐标,其中rjust函数接受两个参数,返回长度为4的右对齐的字符串,如果不足4个长度,默认以空格填充,如果超过4个字符串,则返回原字符串;print函数指定end不为换行符这样下方输入的n个\b(backspace)可以将前面固定长度的坐标字符串擦除;而设置flush=True是为了及时更新屏幕上的内容(print的内容是写入到内存当中,不一定能及时刷新到屏幕,尤其是在while循环和利用open打开文件时),实测在苹果电脑上是否设置该参数并没有区别;另外如果不设置rjust,那么每次字符串长度不一样,会导致字符串刷新会出现问题。

try:
while True:
  x,y=pyautogui.position()
  current_position=f"X: {str(x).rjust(4)}, Y: {str(y).rjust(4)}"
  print(current_position,end='')
  print('\b'*len(current_position),end='',flush=True)
except KeyboardInterrupt:
  print("User quit!\n")


    如何点击、拖动、滚动鼠标?

    点击。完整的点击意味着按下鼠标左键,并松开。以下代码,其中button接受三个参数'left', 'middle','right',分别控制鼠标的左中右键。

pyautogui.click(x=1167,y=404, clicks=2, interval=0.1,button='left')

click函数其实是mouseDown(鼠标按下不松开)和mouseUp(松开鼠标)两个函数的封装。同样的还有一些可读性比较高的函数,例如rightClick,middleClick,doubleClick和tripleClick(就是字面意思)。

    拖动。移动鼠标且按住一个按键不放。类似移动鼠标,也存在两个函数,分别是移动到指定位置和移动相对距离。需要注意的是,如果duration设置为0,苹果电脑下无法立即拖动,会出现问题。

pyautogui.dragTo(x, y, duration=num_seconds)  # drag mouse to XY
pyautogui.dragRel(xOffset, yOffset, duration=num_seconds)  # drag mouse relative to its current position

例如两秒时间将我电脑上的某个文件夹往左下拉100个像素

pyautogui.moveTo(x=1167,y=404)
pyautogui.dragRel(xOffset=-100,yOffset=100,duration=2,button='left')

又或者让电脑自动绘一幅图片,如下图所示

pyautogui.click()
distance=200
while distance > 0:
  pyautogui.dragRel(distance, 0, duration=0.2, button='left')
  distance -=15
  pyautogui.dragRel(0, distance, duration=0.2, button='left')
  pyautogui.dragRel(-distance, 0, duration=0.2, button='left')
  distance -=15
  pyautogui.dragRel(0, -distance, duration=0.2, button='left')

    滚动鼠标。需要注意的参数如果为正,则向上滑动,反之亦然。具体的像素需要自己测试,官网的第一个参数为clicks,如下代码scroll down 10 "clicks",在我的理解里,应该是一种点击滚动操作,例如,某个网页右侧有滑块,将鼠标放在上面,点击10次。

pyautogui.scroll(-10)


    如何处理屏幕?

    pyautogui需要借用Pillow/PIL来处理相关的数据,我的电脑已经安装了Pillow。

pip show pillow
Name: Pillow
Version: 6.1.0
Summary: Python Imaging Library (Fork)
Home-page: http://python-pillow.org
Author: Alex Clark (Fork Author)
Author-email: aclark@aclark.net
License: UNKNOWN
Location: /usr/local/lib/python3.7/site-packages
Requires:
Required-by: pytesseract, PyScreeze, MouseInfo

测试保存图片,随后可以在照片中找到名为foo.png的全屏截图,或者想要截取指定范围的图片,指定region即可,传入一个4整数元组(左,上,宽度,高度)。

>>> im=pyautogui.screenshot('foo.png')
<PIL.PngImagePlugin.PngImageFile image mode=RGBA size=1366x768 at 0x107D4D9E8>
pyautogui.screenshot(imageFilename='fo.png',region=(0,0,1,1))

获取屏幕中某个像素点的RGB颜色值,如下代码返回一个RGBA数值,pyautogui的文档介绍中关于Pixel Matching函数示例有些问题,另外需要注意的是getpixel是pillow中的函数,而pixel是pyautogui中的函数:

>>> im.getpixel((136,767))
(75, 70, 140, 255)
# 如果只要rgb数值
>>> pix=pyautogui.pixel(0,0)
>>> pix
RGB(red=255, green=228, blue=233)

如果需要在知道坐标位置的像素点RGB数值时,可以使用pixelMatchesColor函数来判断是否目标位置是否是正确的位置

>>> pyautogui.pixelMatchesColor(0,0,(255,228,233))
True
>>> pyautogui.pixelMatchesColor(0,0,(255,228,223))
False


    现在可以扩展一下前面实时获取鼠标位置的功能了,加上鼠标所在位置的RGB数值,上面介绍了官方文档的Pixel Matching函数有些问题,所以如果我们按照解包的方式去获取getpixel的RGB,会出现ValueError: too many values to unpack的错误,所以利用索引下标值来解决这个问题。

try:
while True:
  x,y=pyautogui.position()
  current_position=f"X: {str(x).rjust(4)}, Y: {str(y).rjust(4)}. RGB:("
  my_rgb = pyautogui.screenshot().getpixel((x,y))
  current_position += f"{str(my_rgb[0]).rjust(3)}, "
  current_position += f"{str(my_rgb[1]).rjust(3)}, "
  current_position += f"{str(my_rgb[2]).rjust(3)} )"
  print(current_position,end='')
  print('\b'*len(current_position),end='')
except KeyboardInterrupt:
  print("User quit!\n")


    每次去获取目标的坐标位置来进行点击太过麻烦,而且不能保证目标位置不发生改变,如果能使用图片来进行校对那操作性就高了很多,例如,我需要使用计算器来计算,我无法保证每次计算器打开的位置都一样,所以可以先截取图片进行校对,获取需要点击的图片所在坐标,如果在屏幕中存在相同的图片,那么就判定命中,进行点击。截图需要注意的是千万不要使用QQ截图(始终返回NONE),苹果电脑下快捷截图按键command+shift+4,图片会保存在桌面。

im_location=pyautogui.locateOnScreen('8.png')
>>>Box(left=296, top=163, width=36, height=30)
im_center=pyautogui.center(im_location)
>>>Point(x=314, y=178)
x,y=im_center
pyautogui.click(x,y)

如上代码,我先是截了一张数字为8的图片,命名为8.png,再使用locateOnScreen函数获取其图片坐标,center函数接受一个4整数元组,并返回一个点(Point)对象的xy坐标,再使用click方法进行点击。需要注意的是计算器界面需要放在最上层,不能进行遮挡。

    也可以使用更简便的locateCenterOnScreen函数,其整合了locateOnScreen函数和center函数,一样返回中心点xy的坐标:

x,y = pyautogui.locateCenterOnScreen('8.png')
pyautogui.click(x,y)

    如果图像可以找到多处,可以使用locateAllOnScreen函数,其返回一个生成器(generator),可以自行用next函数一个个打印出来看下

im=pyautogui.locateAllOnScreen('1.png')
im
<generator object _locateAll_python at 0x10a564b10>
list(im)
[Box(left=371, top=162, width=106, height=16), Box(left=371, top=194, width=106, height=16), Box(left=371, top=226, width=106, height=16)]

 

如何利用python控制电脑的键盘

环境:win10,python3.8.4,pyautogui 0.9.52。

例如:写一个自动化程序控制英雄打人机局获取英雄联盟游戏里面的首胜奖励。

测试:输入文字

    typewrite函数用于对消息message的每个字符按下键盘键,然后释放,输入时以键盘光标所在位置为基准。

上方的文字内容为程序输入,代码如下:

pyautogui.click(300,400)
#如果message是字符串
pyautogui.typewrite(message='this is the first keyboard test, by zhangxiaofei', interval=0.1)
#如果message是列表
#pyautogui.typewrite(message=['a','b','left','left','x','y'], interval=0.1)
# pyautogui.write('hello world')

首先click获取光标,随后使用typewrite函数输入message,每次按键间隔时间interval为0.1秒(默认为0),message还可以接受一个列表,列表的每个值须为KEYBOARD_KEYS中定义的键盘按键,具体的按键可以用以下代码导入后转到定义查看,如果列表中某个值非定义值,那么该值会被输出为空。

from pyautogui import KEYBOARD_KEYS

    如果需要单独的运用键盘按下和松开,可以使用如下代码,其中press函数是keyDown和keyUp的组合封装:

pyautogui.keyDown('shift')
pyautogui.press('4')
pyautogui.keyUp('shift')
>>>$


    以上代码其实就是shift+4的结合按键,但是太过复杂,所以pyautogui为我们提供了热键(hotkey),方便我们组合按键,这也是一样的效果:

pyautogui.hotkey('shift','4')

 

参考文章:

#文档
https://pyautogui.readthedocs.io/en/latest/
https://pypi.org/project/PyAutoGUI/