Python自動化指令碼——涉及彈窗,自動登入,截圖,獲取使用者許可權,打包程式等
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')
- 1
- 2
- 3
- 4
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("儲存的地址")
- 1
但是上面的函式只能夠擷取當前頁面的圖片,我們需要擷取更多的圖片,長圖,網路上有現成的程式碼,可以實現分開擷取圖片最後拼接成長圖。因為時間有些久遠,忘記參考的連結,這裡貼出程式碼:
"""擷取長圖"""
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)