2020暑期XX银行实习-一个自动化脚本
很多人学习python,不知道从何学起。 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。 很多已经做案例的人,却不知道如何去学习更加高深的知识。 那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码! QQ群:101677771
实习背景
有幸在2020年进行中国某银行金融科技中心进行时长一个月的实习,在金科部的开发工作主要是帮助开发了一款自动化的程序,可以实现自动登录网页,然后进行截图,在此工程中涉及到不少功能和技巧,特此写下记录。可能叙述和代码有错,欢迎大家一起交流。
主功能介绍
整个程序的流程,运行后弹出对话框填写查询的关键字,后续用于网页自动搜索到达指定网页,随后登录网页并自动运行至最后的指定网页,截图指定内容部分保存至文件夹后关闭。
获取用户权限和禁用鼠标键盘
在脚本自动运行的时候,防止用户误输入和误操作,需要禁止使用鼠标和键盘。利用一个函数ShellExecute,具体细节可看ShellExecuteA function。这里贴出一段代码,直接使用即可。
from __future__ import print_function
import ctypes, sys
if 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()。这是一个很有必要改进的点,日后有机会看看能否完善。
参考:https://blog.csdn.net/qq_17550379/article/details/79006655
对话框
通过使用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后,用于后续的搜索,当自动运行过程中出错时,警告框可以提醒用户。
参考:https://www.jb51.net/article/183926.htm
使用谷歌浏览器chrome自动登录网页
使用谷歌浏览器的驱动进行自动化登录网页是一个常用的方法,这也是我自己以前使用的方法,但是这次登录的网页属于银行内部的内网,无法使用webdriver定位网页元素,无论是使用哪种方式都不行,所以只是使用驱动启到一个自动打开网页的功能,后续的用户登录,自动搜索等都是利用pyautogui的模拟鼠标键盘输入。
chromedriver
通常的网页自动登录使用chromedriver即可,可以参考Python实现网站自动登录—傻瓜教程。也非常推荐这种方法,因为模拟鼠标点击实在有些笨,只适合一些简单的自动脚本,但是适用面到可以说是很广了。
模拟鼠标点击以及键盘输入
键盘输入这个功能其实很多模块都有,这是比较需要的是鼠标的点击,这是一个比较愚蠢的方法,但却简单粗暴加实用,当然,很不灵活。主要使用库pyautogui
首先可以使用QQ或者微信的截图功能,获取屏幕上指定点的像素点坐标,记作cur_x, cur_y
import pyautogui
pyautogui.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 pag
import pyperclip
"""输入内容。使用了剪切板,可以忽略输入法问题!"""
char = '用户名或密码'
pyperclip.copy(char)
pag.hotkey('ctrl','v')
其中我还使用该库的locateOnScreen函数进行像素匹配,判断截图是否已经达到了目标区域,和结束位置。有兴趣可以点击链接python 捕捉和模拟鼠标键盘操作,参考仍和第一部分一样。
参考:https://www.jb51.net/article/183926.htm
截图
截图这里有着太多方法,许多库都提供了方法,其实如果是通常可正常爬取的网页,推荐使用导出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 time
import os
from selenium import webdriver
import sys
from ctypes import *
from PIL import Image
import numpy as np
from win32 import win32api, win32gui, win32print
from win32.lib import win32con
from win32.win32api import GetSystemMetrics
import pyperclip
screen_w = GetSystemMetrics (0)
screen_h = GetSystemMetrics (1)
import pyautogui as pag
def 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_id == None:
exit()
#分辨率判断
#提示框,用于截图命名
title = pag.prompt(text='请输入标题,用于结果命名(可不输)', title='标题名称输入', default='')
if title == None:
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(1*time_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(5*time_k)
Mouse_Click(375,145) # 点击个人主页
time.sleep(0.5*time_k)
Mouse_Click(110,269) # 点击我的任务
time.sleep(0.5*time_k)
Mouse_Click(90,335) # 点击已办任务
time.sleep(4.5*time_k)
#输入编码
Mouse_Click(600,351)
All_Del_Pag()
Input_THING(demand_id)
Mouse_Click(918,353) # 点击确定
time.sleep(2.5*time_k)
Mouse_Click(1840,453) # 点击查看
time.sleep(5*time_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(.5*time_k)
flag_img = pag.screenshot(region=(206,817,25,25))
time.sleep(.5*time_k)
pag.scroll(90000)
except:
windll.user32.BlockInput(False)
pag.alert(text='未知错误,请联系开发者', title='警告', button='OK')
driver.close()
time.sleep(.5*time_k)
Mouse_Click(231,299) # 流程查看
time.sleep(.5*time_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 root
def 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, h
def 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_rate == 1:
pag.alert(text='缩放比例已为100%,现在程序尝试自动设置分辨率,\n若没有成功请手动尝试设置960X540,并设置缩放比例为50%', title='说明', button='OK')
resolution_change(1920,1080)
exit()
else:
pag.alert(text='分辨率有误!!\n请点击左下角图标-设置-显示,设置分辨率和缩放比例\n(1920X1080,100% 或 960X540,50%)', title='警告', button='OK')
exit()
def Read_txt():
"""读取文件获得时间参数"""
path = os.path.dirname(sys.argv[0])+'\\time.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 ch
if __name__ == '__main__':
main(screen_w , screen_h)
|