实习背景主功能介绍获取用户权限和禁用鼠标键盘对话框使用谷歌浏览器chrome自动登录网页chromedriver模拟鼠标点击以及键盘输入截图所有代码其他
有幸在2020年进行中国某银行金融科技中心进行时长一个月的实习,在金科部的开发工作主要是帮助开发了一款自动化的程序,可以实现自动登录网页,然后进行截图,在此工程中涉及到不少功能和技巧,特此写下记录。可能叙述和代码有错,欢迎大家一起交流。
整个程序的流程,运行后弹出对话框填写查询的关键字,后续用于网页自动搜索到达指定网页,随后登录网页并自动运行至最后的指定网页,截图指定内容部分保存至文件夹后关闭。
在脚本自动运行的时候,防止用户误输入和误操作,需要禁止使用鼠标和键盘。利用一个函数ShellExecute,具体细节可看ShellExecuteA function。这里贴出一段代码,直接使用即可。
from future import print_functionimport ctypes, sysif windll.shell32.IsUserAnAdmin(): //运行的代码else: if sys.version_info[0]==3: ctypes.windll.shell32.ShellExecuteW(None, “runas”, sys.executable, file, None, 1) exit()//因为即使没有权限也会运行,所以添加exit,使得没有权限时会退出程序。 else: //如果是python 2.x ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(file), None, 1) exit()
值得注意的是,如果没有管理员运行,这里其实运行了两次,第一次没有权限,第二次获得权限后运行代码,所以我添加了一个退出的函数exit()。这是一个很有必要改进的点,日后有机会看看能否完善。
通过使用pyautogui库来实现从对话框获取参数和警告对话框功能。
import pyautogui as pag# 显示一个简单的带文字和OK按钮的消息弹窗。用户点击后返回button的文字。pag.alert(text=‘对话框内部内容’, title=‘对话框标题’, button=‘按钮’)//例如:a=pag.alert(text=‘要开始程序么?’, title=‘请求框’, button=‘OK’)print(a) # 输出结果为返回的OK# 显示一个简单的带文字、OK和Cancel按钮的消息弹窗,用户点击后返回被点击button的文字,支持自定义数字、文字的列表。pag.confirm(text=‘对话框内部内容’, title=‘对话框标题’, buttons=[‘OK’, ‘Cancel’]) # OK和Cancel按钮的消息弹窗,其中按钮可以使用数字来设置个数pag.confirm(text=’’, title=’’, buttons=range(10)) # 10个按键0-9的消息弹窗//例如:b=pag.confirm(text=’’, title=’’, buttons=range(10))print(b) # 输出结果为点击的数字# 可以输入的消息弹窗,带OK和Cancel按钮。用户点击OK按钮返回输入的文字,点击Cancel按钮返回None。可以设置一个默认输入。pag.prompt(text=‘对话框内部内容’, title=‘对话框标题’, default=‘默认输入’)# 样式同prompt(),用于输入密码,消息用表示。带OK和Cancel按钮。用户点击OK按钮返回输入的文字,点击Cancel按钮返回None。pag.password(text=‘对话框内部内容’, title=‘对话框标题’, default=’’, mask=’’)
主要使用了返回输入的对话框和警告框,从返回框获得ID后,用于后续的搜索,当自动运行过程中出错时,警告框可以提醒用户。
使用谷歌浏览器的驱动进行自动化登录网页是一个常用的方法,这也是我自己以前使用的方法,但是这次登录的网页属于银行内部的内网,无法使用webdriver定位网页元素,无论是使用哪种方式都不行,所以只是使用驱动启到一个自动打开网页的功能,后续的用户登录,自动搜索等都是利用pyautogui的模拟鼠标键盘输入。
通常的网页自动登录使用chromedriver即可,可以参考Python实现网站自动登录—傻瓜教程。也非常推荐这种方法,因为模拟鼠标点击实在有些笨,只适合一些简单的自动脚本,但是适用面到可以说是很广了。
键盘输入这个功能其实很多模块都有,这是比较需要的是鼠标的点击,这是一个比较愚蠢的方法,但二手却简单粗暴加实用,当然,很不灵活。主要使用库pyautogui
首先可以使用QQ或者微信的截图功能,获取屏幕上指定点的像素点坐标,记作cur_x, cur_y
import pyautoguipyautogui.click(x=cur_x, y=cur_y, button=‘left’)//x,y是要点击的位置,默认是鼠标当前位置//button是要点击的按键,有三个可选值:‘left’, ‘middle’, ‘right’
输入用户名和密码使用模拟键盘输入即可,值得注意的是有几个注意点:
输入前全选加删除,也就是ctrl+a再del不要模拟一个一个字母输入,很有可能受限于输入法的问题,使用字符串复制再粘贴
import pyautogui as pag “”“全选内容并删除”"" pag.hotkey(‘ctrl’,‘a’) pag.press(‘delete’)
import pyautogui as pagimport pyperclip “”“输入内容。使用了剪切板,可以忽略输入法问题!”"" char=‘用户名或密码’ pyperclip.copy(char) pag.hotkey(‘ctrl’,‘v’)
其中我还使用该库的locateOnScreen函数进行像素匹配,判断截图是否已经达到了目标区域,和结束位置。有兴趣可以点击链接python 捕捉和模拟鼠标键盘操作,参考仍和第一部分一样。
截图这里有着太多方法,许多库都提供了方法,其实如果是通常可正常爬取的网页,推荐使用导出PDF,而不是截图,这样可以得到整个网页。截图正常情况下只能得到当前屏幕的内容。但由于银行内网的缘故,这里采用了webdriver的截图函数。
driver.save_screenshot(“保存的地址”)
但是上面的函数只能够截取当前页面的图片,我们需要截取更多的图片,长图,网络上有现成的代码,可以实现分开截取图片最后拼接成长图。因为时间有些久远,忘记参考的链接,这里贴出代码:
“”“截取长图”"" window_height=driver.get_window_size()[‘height’] # 窗口高度 page_height=driver.execute_script(‘return document.documentElement.scrollHeight’) # 页面高度 driver.save_screenshot(‘分图.png’) if page_height > window_height: n=page_height // window_height # 需要滚动的次数 base_mat=np.atleast_2d(Image.open(‘1.png’)) # 打开截图并转为二维矩阵 for i in range(n): driver.execute_script(f’document.documentElement.scrollTop={window_height * (i + 1)};’) time.sleep(.5) driver.save_screenshot(f’分图_{i}.png’) # 保存截图 mat=np.atleast_2d(Image.open(f’分图_{i}.png’)) # 打开截图并转为二维矩阵 base_mat=np.append(base_mat, mat, axis=0) # 拼接图片的二维矩阵 Image.fromarray(base_mat).save(char)
现在所使用的是利用的是,利用标志点是否出现在屏幕里,来截取指定区域的所有内容,具体情况看代码。
“”“自动脚本”""""“登录网站并对流程截图”""""“基本实现功能版”"""""“为解决有些电脑运行过慢,添加一个读取文本文件,加入等待时间的系数time_k”"“import timeimport osfrom selenium import webdriverimport sysfrom ctypes import from PIL import Imageimport numpy as npfrom win32 import win32api, win32gui, win32printfrom win32.lib import win32confrom win32.win32api import GetSystemMetricsimport pyperclipscreen_w=GetSystemMetrics (0)screen_h=GetSystemMetrics (1)import pyautogui as pagdef main(screen_w , screen_h): #读取时间参数 try: time_k=Read_txt() except: time_k=1 # 判断时间参数 if time_k>5 or time_k<0.2: pag.alert(text=‘时间参数设置不正常,请设置在0.2~5之间’, title=‘警告’, button=‘OK’) exit() #权限获取 Inner() #用户可以使用框输入需求ID,例如:GZ20073002 demand_id=pag.prompt(text=‘请输入流程编号(请保证使用谷歌浏览器,且版本为83.0.4103相近)’, title=‘查询业务流程’, default=’’) if demand_idNone: exit() #分辨率判断 #提示框,用于截图命名 title=pag.prompt(text=‘请输入标题,用于结果命名(可不输)’, title=‘标题名称输入’, default=’’) if titleNone: title=" " resolution_judgment(screen_w,screen_h) # 环境配置 chromedriver=“C:\Program Files (x86)\Google\Chrome\Application” os.environ[“webdriver.ie.driver”]=chromedriver driver=webdriver.Chrome() # 选择Chrome浏览器 driver.get(‘网址’) # 打开网站 driver.maximize_window() # 最大化谷歌浏览器 time.sleep(2) try: #禁用鼠标键盘 windll.user32.BlockInput(True) time.sleep(1time_k) #输入用户名和密码 username=”" # 用户名 #password="" # 密码 password=Password() #鼠标移动到用户名 Mouse_Click(1330,290) All_Del_Pag() Input_THING(username) #鼠标移动到密码 Mouse_Click(1340,325) All_Del_Pag() Input_THING(password) Mouse_Click(1305,400)# 点击登录 time.sleep(5time_k) Mouse_Click(375,145) # 点击个人主页 time.sleep(0.5time_k) Mouse_Click(110,269) # 点击我的任务 time.sleep(0.5time_k) Mouse_Click(90,335) # 点击已办任务 time.sleep(4.5time_k) #输入编码 Mouse_Click(600,351) All_Del_Pag() Input_THING(demand_id) Mouse_Click(918,353) # 点击确定 time.sleep(2.5time_k) Mouse_Click(1840,453) # 点击查看 time.sleep(5time_k) try: driver.switch_to.window(driver.window_handles[1]) except: windll.user32.BlockInput(False) pag.alert(text=‘页面不对!可能是网络原因\流程ID不存在\输入法问题,请检查后再尝试’, title=‘警告’, button=‘OK’) driver.close() exit() #抓取一个特征,用于最后截图定位已经到底部 try: pag.scroll(-90000) time.sleep(.5time_k) flag_img=pag.screenshot(region=(206,817,25,25)) time.sleep(.5time_k) pag.scroll(90000) except: windll.user32.BlockInput(False) pag.alert(text=‘未知错误,请联系开发者’, title=‘警告’, button=‘OK’) driver.close() time.sleep(.5time_k) Mouse_Click(231,299) # 流程查看 time.sleep(.5time_k) “”“开始截图,不断往下翻页截图,直到识别标志特征”"" try: t=True i=1 while t: Html_Png(driver,’\%s%d.png’%(title,i)) if pag.locateOnScreen(flag_img): t=False pag.scroll(-1000) i=i+1 except: windll.user32.BlockInput(False) pag.alert(text=‘未知错误’, title=‘警告’, button=‘OK’) driver.close() time.sleep(.5) windll.user32.BlockInput(False) #关闭驱动 driver.close() except: windll.user32.BlockInput(False) driver.close()def All_Del_Pag(): #全选内容并删除 pag.hotkey(‘ctrl’,‘a’) pag.press(‘delete’)def Input_THING(char): “”“输入内容。使用了剪切板,可以忽略输入法问题!”"" pyperclip.copy(char) pag.hotkey(‘ctrl’,‘v’)def Mouse_Click(x,y): #移动并点击 pag.moveTo(x,y) pag.click()def Html_Png(driver,char): “”“截图”"" Png_root=Local_Adr_G(char) try: driver.save_screenshot(Png_root) except: print(‘截图出错’)def Html_LongPng(driver,char): #长截图功能,建行网站无法使用,通常网站可以 window_height=driver.get_window_size()[‘height’] # 窗口高度 page_height=driver.execute_script(‘return document.documentElement.scrollHeight’) # 页面高度 driver.save_screenshot(‘分图.png’) if page_height > window_height: n=page_height // window_height # 需要滚动的次数 base_mat=np.atleast_2d(Image.open(‘1.png’)) # 打开截图并转为二维矩阵 for i in range(n): driver.execute_script(f’document.documentElement.scrollTop={window_height * (i + 1)};’) time.sleep(.5) driver.save_screenshot(f’分图_{i}.png’) # 保存截图 mat=np.atleast_2d(Image.open(f’分图_{i}.png’)) # 打开截图并转为二维矩阵 base_mat=np.append(base_mat, mat, axis=0) # 拼接图片的二维矩阵 Image.fromarray(base_mat).save(char)def Local_Adr(char): “”“获取当前文件路径”"" root=os.path.dirname(sys.argv[0]) root=root + char return rootdef Local_Adr_G(char): “”“获取当前路径下的的流程截图文件路径”"" root=os.path.dirname(sys.argv[0])+’\流程截图’ root=root + char return root def Inner(): #获取管理员权限 if windll.shell32.IsUserAnAdmin(): return else: windll.shell32.ShellExecuteW(None, “runas”, sys.executable, file, None, 0) sys.exit()def get_real_resolution(): “”“获取真实的分辨率”"" hDC=win32gui.GetDC(0) # 横向分辨率 w=win32print.GetDeviceCaps(hDC, win32con.DESKTOPHORZRES) # 纵向分辨率 h=win32print.GetDeviceCaps(hDC, win32con.DESKTOPVERTRES) return w, hdef resolution_change(x,y): “”“修改分辨率”"" dm=win32api.EnumDisplaySettings(None, 0) dm.PelsWidth=x dm.PelsHeight=y dm.BitsPerPel=32 dm.DisplayFixedOutput=0 win32api.ChangeDisplaySettings(dm, 0)def resolution_judgment(w,h): “”“分辨率判断”"" real_resolution=get_real_resolution() screen_size=w,h screen_scale_rate=round(real_resolution[0] / screen_size[0], 2) if screen_size[0]1920: return else: if screen_scale_rate1: pag.alert(text='缩放比例已为100%,现在程序尝试自动设置分辨率,
若没有成功请手动尝试设置960X540,并设置缩放比例为50%’, title=‘说明’, button=‘OK’) resolution_change(1920,1080) exit() else: pag.alert(text='分辨率有误!!
请点击左下角图标-设置-显示,设置分辨率和缩放比例
(1920X1080,100% 或 960X540,50%)’, title=‘警告’, button=‘OK’) exit()def Read_txt(): “”“读取文件获得时间参数”"" path=os.path.dirname(sys.argv[0])+’\ ime.txt’ with open(path,“r”) as f: data=f.readline() return float(data)def Read_Password(): path=os.path.dirname(sys.argv[0])+’\reg.txt’ with open(path,“r”) as f: data=f.readline() return data def Password(): pa=Read_Password() ch="" for i in range(0,len(pa),5): asc=int(str(int(pa[i:i+5])*11)[-3:]) ch=ch+chr(asc) return chif name==‘main’: main(screen_w , screen_h)
共同學習,寫下你的評論
評論加載中...
作者其他優質文章