python办公自动化之图像对比&模拟鼠标点击

  小爬之前的一系列博文中,提到过基于Pywin32、Uiautomation、SAP Scripting API、Office macro等手段来实现windows桌面应用的自动化。但是,凡事总有例外。有些应用程序使用的一些自定义、自绘制的控件,这类控件无论是使用SPY++还是inspect之类软件都很难成功捕获。主流的RPA厂商,面对这类控件,也只能通过识别某个控件对应的图像在窗口中的位置,再来模拟键盘鼠标实现点击或者输入。如果我们希望用python自己编写代码来实现这一功能,又需要怎么做呢?小爬决定带大家来一探究竟。

  小爬就以 金蝶K3的某桌面客户端举例,通过下图不难看出,Microsoft Spy++拿它无可奈何,只能获取到主窗口句柄,而子窗口完全拿不到,我们如何通过代码实现自动点击下图中【程序列表】处的【金蝶K3 WISE】呢?你们不妨先思考下,我这里短暂卖个关子先。

  

 

   下面全是满满的干货,看完保证您直呼So Easy!具体思路如下:

①、对上图中的【金蝶 K3 WISE】图标区域截图,存储在本地目录,比如就放置于我们脚本的同级目录,姑且命名为【obj.png】:

②利用pywin32中的win32gui函数,也就是FindWindow来获得该客户端主窗口句柄,进而拿到其在系统桌面中的位置和宽高;

③、利用图形库Pillow中的imageGrab.grab方法,对桌面的某个区域,也就是该客户端主窗口区域截图,作为我们的源图,保存在内存中,可以不用存储为本地图片文件;

④、利用aircv的图像对比方法,来实现根据源图定位目标图的位置中心点;

⑤、根据步骤④得到的目标中心点相对座标,计算得到该中心点在整个桌面的绝对位置座标;

⑥、利用win32api中的SetCursorPos、mouse_event等方法来驱动光标位置,模拟鼠标点击等,最终实现自动化。

  其实上面的步骤还可以简化,我们的源图可以直接是我们的桌面截屏,而无需获取应用程序的主窗口位置,那样拿到的就已经是目标图片在桌面的绝对位置,本文中暂且先按照上面的六个步骤来实现。

其中利用步骤④拿到图片在窗口中的位置后,可以借助opencv来显示出来,代码如下:

 1 import cv2
 2 import aircv as ac
 3 import numpy as np
 4 from PIL import ImageGrab,Image
 5 import win32api,win32con,win32gui
 6 def draw_circle(img, center, radius, color,thickness):
 7     cv2.circle(img, center, radius, color,thickness)
 8     cv2.imshow('objDetect', imsrc) 
 9     cv2.waitKey(0)
10     cv2.destroyAllWindows()
11 
12 if __name__ == "__main__":
13     '''通过spy++拿到应用程序主窗口的类名和窗口标题'''
14     mainHnd=win32gui.FindWindow("MainFrameWnd","异速联客户端 7.0.0.0")
15 
16     '''根据GetWindowRect拿到主窗口的左顶点的位置座标(x,y)和窗口的宽高(w*h)'''
17     rect = win32gui.GetWindowRect(mainHnd)
18     x,y=rect[0],rect[1]
19     w,h=rect[2] - x,rect[3] - y
20     img=ImageGrab.grab(bbox=rect) # (left_x, top_y, right_x, bottom_y)
21     # img.save("src.png") # 可以截图保存测试下,是否截图位置准确,非必须步骤
22     imsrc=np.array(img)
23 
24     r, g, b = cv2.split(imsrc)
25     cv2.merge([b,g,r], imsrc) # CV中是以BGR的顺序来表达一个顺序,与传统的RGB标准不一致,所以需要重组ndArray
26     imobj = ac.imread('obj.png')
27     pos = ac.find_template(im_source=imsrc,im_search=imobj,threshold=0.8)
28     circle_center_pos = (int(pos['result'][0]),int(pos['result'][1]))
29     circle_radius = 50
30     color = (255, 0, 0)
31     line_width = 2
32     draw_circle(imsrc, circle_center_pos, circle_radius, color,line_width)

运行该程序后,opencv会帮我们绘制出目标图像在窗口中的位置,效果如下图所示:

  之后就是模拟键盘和鼠标:移动光标到桌面指定位置,点击该区域了,需要注意的是,如果要这块功能生效,需要提前将pycharm或者 visual studio code以管理员权限打开,否则可能看不到实际效果。整个功能完整的代码如下:

 1 import cv2
 2 import aircv as ac
 3 import numpy as np
 4 from PIL import ImageGrab,Image
 5 import win32api,win32con,win32gui
 6 def draw_circle(img, center, radius, color,thickness):
 7     cv2.circle(img, center, radius, color,thickness)
 8     cv2.imshow('objDetect', imsrc) 
 9     cv2.waitKey(0)
10     cv2.destroyAllWindows()
11 
12 if __name__ == "__main__":
13     '''通过spy++拿到应用程序主窗口的类名和窗口标题'''
14     mainHnd=win32gui.FindWindow("MainFrameWnd","异速联客户端 7.0.0.0")
15 
16     '''根据GetWindowRect拿到主窗口的左顶点的位置座标(x,y)和窗口的宽高(w*h)'''
17     rect = win32gui.GetWindowRect(mainHnd)
18     x,y=rect[0],rect[1]
19     w,h=rect[2] - x,rect[3] - y
20     img=ImageGrab.grab(bbox=rect) # (left_x, top_y, right_x, bottom_y)
21     # img.save("src.png") # 可以截图保存测试下,是否截图位置准确,非必须步骤
22     imsrc=np.array(img)
23 
24     r, g, b = cv2.split(imsrc)
25     cv2.merge([b,g,r], imsrc) # CV中是以BGR的顺序来表达一个顺序,与传统的RGB标准不一致,所以需要重组ndArray
26     imobj = ac.imread('obj.png')
27     pos = ac.find_template(im_source=imsrc,im_search=imobj,threshold=0.8)
28     circle_center_pos = (int(pos['result'][0])+x,int(pos['result'][1])+y) # 计算对象图片中心点 在桌面的绝对座标
29     win32api.SetCursorPos((circle_center_pos[0],circle_center_pos[1])) # 设置光标位置
30     win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) # 模拟鼠标左键按下
31     win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) # 模拟鼠标左键弹起

  想必屏幕前的童鞋已经迫不及待了,那就赶紧举一反三,从实际业务中找到这类需求,麻溜地动手试试吧!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章