原文:https://automatetheboringstuff.com/2e/chapter20/
了解用于編輯電子表格、下載文件和啟動程序的各種 Python 模塊是很有用的,但有時您需要使用的應(yīng)用沒有任何模塊。在計算機(jī)上實現(xiàn)任務(wù)自動化的終極工具是你編寫的直接控制鍵盤和鼠標(biāo)的程序。這些程序可以通過發(fā)送虛擬擊鍵和鼠標(biāo)點擊來控制其他應(yīng)用,就像你坐在電腦前親自與應(yīng)用進(jìn)行交互一樣。
這種技術(shù)被稱為圖形用戶界面自動化,或者簡稱為 GUI 自動化。有了 GUI 自動化,你的程序可以做坐在電腦前的人類用戶能做的任何事情,除了把咖啡灑在鍵盤上。把 GUI 自動化想象成給一個機(jī)器人手臂編程。你可以給機(jī)械臂編程,讓它在你的鍵盤上打字,并為你移動鼠標(biāo)。這種技術(shù)對于涉及大量無意識點擊或填寫表格的任務(wù)特別有用。
一些公司出售創(chuàng)新的(且昂貴的)“自動化解決方案”,通常以 RPA 的名義銷售。這些產(chǎn)品實際上與您可以用pyautogui
模塊自己制作的 Python 腳本沒有什么不同,該模塊具有模擬鼠標(biāo)移動、按鈕點擊和鼠標(biāo)滾輪滾動的函數(shù)。本章僅涵蓋 PyAutoGUI 特性的一個子集;你可以在pyautogui.readthedocs.io
找到完整的文檔。
安裝pyautogui模塊
pyautogui
模塊可以向 Windows、MacOS 和 Linux 發(fā)送虛擬按鍵和鼠標(biāo)點擊。Windows 和 MacOS 用戶可以簡單地使用 PIP 來安裝 PyAutoGUI。然而,Linux 用戶首先必須安裝一些 PyAutoGUI 所依賴的軟件。打開終端窗口,輸入以下命令:
sudo apt-get install scrot
sudo apt-get install python3-tk
sudo apt-get install python3-dev
要安裝 PyAutoGUI,運行pip install --user pyautogui
。不要用sudo
搭配pip
;您可能會將模塊安裝到操作系統(tǒng)使用的 Python 安裝中,從而導(dǎo)致與依賴于其原始配置的任何腳本發(fā)生沖突。然而,當(dāng)使用apt-get
安裝應(yīng)用時,您應(yīng)該使用sudo
命令。
附錄 A 有關(guān)于安裝第三方模塊的完整信息。要測試 PyAutoGUI 是否已正確安裝,請從交互式 Shell 中運行import pyautogui
并檢查任何錯誤消息。
警告
不要將你的程序保存為
pyautogui.py
。當(dāng)你運行import pyautogui
時,Python 會導(dǎo)入你的程序而不是pyautogui
,你會得到類似attribute error: module 'PyAutoGUI' has not property' click'
。
在 MacOS 上設(shè)置輔助功能應(yīng)用
作為一種安全措施,MacOS 通常不讓程序控制鼠標(biāo)或鍵盤。要讓 PyAutoGUI 在 MacOS 上工作,您必須將運行 Python 腳本的程序設(shè)置為可訪問性應(yīng)用。如果沒有這一步,您的 PyAutoGUI 函數(shù)調(diào)用將沒有任何效果。
無論您是從 Mu、IDLE 還是終端運行 Python 程序,都要打開該應(yīng)用。然后打開“系統(tǒng)偏好設(shè)置”并轉(zhuǎn)到“輔助功能”標(biāo)簽。當(dāng)前打開的應(yīng)用將出現(xiàn)在“允許下面的應(yīng)用控制您的電腦”標(biāo)簽下。檢查 Mu、IDLE、終端或您用來運行 Python 腳本的任何應(yīng)用。系統(tǒng)會提示您輸入密碼以確認(rèn)這些更改。
保持在軌道上
在您進(jìn)入 GUI 自動化之前,您應(yīng)該知道如何避免可能出現(xiàn)的問題。Python 可以以令人難以置信的速度移動鼠標(biāo)和擊鍵。事實上,它可能太快了,其他程序跟不上。此外,如果出了問題,但你的程序不停地移動鼠標(biāo),這將很難告訴確切的程序在做什么或如何從問題中恢復(fù)。就像迪士尼電影《魔術(shù)師的學(xué)徒》中的魔術(shù)掃帚一樣,它不停地往米奇的浴缸里注水,然后又注滿,即使你的程序完全按照你的指令運行,它也可能會失去控制。如果鼠標(biāo)自己四處移動,停止程序可能會很困難,使您無法單擊 Mu 編輯器窗口來關(guān)閉它。幸運的是,有幾種方法可以防止 GUI 自動化問題或從中恢復(fù)。
暫停和自動防故障
如果你的程序有一個 bug,而你不能使用鍵盤和鼠標(biāo)來關(guān)閉它,你可以使用 PyAutoGUI 的自動防故障功能。將鼠標(biāo)快速滑動到屏幕的四個角之一。每個 PyAutoGUI 函數(shù)調(diào)用在執(zhí)行其動作后都有十分之一秒的延遲,以便有足夠的時間將鼠標(biāo)移到角落。如果 PyAutoGUI 隨后發(fā)現(xiàn)鼠標(biāo)光標(biāo)在一個角落,它會引發(fā)pyautogui.FailSafeException
異常。非 PyAutoGUI 指令沒有這種十分之一秒的延遲。
如果您發(fā)現(xiàn)自己處于需要停止 PyAutoGUI 程序的情況,只需朝某個角落猛按鼠標(biāo)即可停止。
注銷關(guān)閉一切
也許停止失控的 GUI 自動化程序的最簡單方法是注銷,這將關(guān)閉所有正在運行的程序。在 Windows 和 Linux 上,注銷熱鍵是CTRL+ALT+DEL
。在 MacOS 上,是Cmd+SHIFT+OPTION+Q
。通過注銷,你會丟失任何未保存的工作,但至少你不用等待電腦完全重啟。
控制鼠標(biāo)移動
在本節(jié)中,您將學(xué)習(xí)如何使用 PyAutoGUI 移動鼠標(biāo)并跟蹤其在屏幕上的位置,但首先您需要理解 PyAutoGUI 如何處理坐標(biāo)。
PyAutoGUI 的鼠標(biāo)函數(shù)使用 x 和 y 坐標(biāo)。圖 20-1 顯示了計算機(jī)屏幕的坐標(biāo)系;這類似于用于圖像的坐標(biāo)系統(tǒng),在第 19 章中討論過。原點,其中x
和y
均為零,位于屏幕左上角。x 坐標(biāo)向右增加,y 坐標(biāo)向下增加。所有坐標(biāo)都是正整數(shù);沒有負(fù)坐標(biāo)。
圖 20-1:分辨率為1920×1080
的電腦屏幕坐標(biāo)
你的分辨率是你的屏幕有多少像素寬和高。如果您的屏幕分辨率設(shè)置為 1920×1080,那么左上角的坐標(biāo)將是(0, 0)
,右下角的坐標(biāo)將是(1919, 1079)
。
函數(shù)返回屏幕寬度和高度的兩個整數(shù)元組,以像素為單位。在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> wh = pyautogui.size() # Obtain the screen resolution.
>>> wh
Size(width=1920, height=1080)
>>> wh[0]
1920
>>> wh.width
1920
在分辨率為1920×1080
的計算機(jī)上,pyautogui.size()
函數(shù)返回(1920, 1080)
;根據(jù)屏幕分辨率的不同,返回值可能會有所不同。size()
返回的Size
對象是一個命名元組。命名元組有數(shù)字索引,像常規(guī)元組,還有屬性名,像對象:wh[0]
和wh.width
都計算屏幕的寬度。(命名元組不在本書討論范圍之內(nèi)。請記住,您可以像使用元組一樣使用它們。)
移動鼠標(biāo)
現(xiàn)在你明白了屏幕坐標(biāo),讓我們移動鼠標(biāo)。pyautogui.moveTo()
函數(shù)會立即將鼠標(biāo)光標(biāo)移動到屏幕上的指定位置。x 坐標(biāo)和 y 坐標(biāo)的整數(shù)值分別構(gòu)成函數(shù)的第一個和第二個參數(shù)??蛇x的duration
整數(shù)或浮點關(guān)鍵字參數(shù)指定將鼠標(biāo)移動到目的地所需的秒數(shù)。如果你不考慮它,默認(rèn)是0
用于瞬時移動。(PyAutoGUI 函數(shù)中所有的duration
關(guān)鍵字參數(shù)都是可選的。)在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> for i in range(10): # Move mouse in a square.
... pyautogui.moveTo(100, 100, duration=0.25)
... pyautogui.moveTo(200, 100, duration=0.25)
... pyautogui.moveTo(200, 200, duration=0.25)
... pyautogui.moveTo(100, 200, duration=0.25)
本示例在提供的四個坐標(biāo)中以正方形模式順時針移動鼠標(biāo)光標(biāo)共 10 次。每個動作需要四分之一秒,由關(guān)鍵字參數(shù)指定。如果您沒有向任何一個pyautogui.moveTo()
調(diào)用傳遞第三個參數(shù),鼠標(biāo)光標(biāo)會立即從一個點傳送到另一個點。
pyautogui.move()
函數(shù)將鼠標(biāo)光標(biāo)相對于其當(dāng)前位置移動。下面的示例以相同的方塊模式移動鼠標(biāo),只是它從代碼開始運行時鼠標(biāo)恰好出現(xiàn)在屏幕上的位置開始方塊:
>>> import pyautogui
>>> for i in range(10):
... pyautogui.move(100, 0, duration=0.25) # right
... pyautogui.move(0, 100, duration=0.25) # down
... pyautogui.move(-100, 0, duration=0.25) # left
... pyautogui.move(0, -100, duration=0.25) # up
pyautogui.move()
函數(shù)也有三個參數(shù):向右水平移動多少像素,向下垂直移動多少像素,以及(可選)完成移動需要多長時間。第一個或第二個參數(shù)的負(fù)整數(shù)將分別導(dǎo)致鼠標(biāo)向左或向上移動。
獲取鼠標(biāo)位置
您可以通過調(diào)用pyautogui.position()
函數(shù)來確定鼠標(biāo)的當(dāng)前位置,該函數(shù)將在函數(shù)調(diào)用時返回鼠標(biāo)光標(biāo)的x
和y
位置的一個名為Point
的元組。在交互式 Shell 中輸入以下內(nèi)容,每次調(diào)用后移動鼠標(biāo):
>>> pyautogui.position() # Get current mouse position.
Point(x=311, y=622)
>>> pyautogui.position() # Get current mouse position again.
Point(x=377, y=481)
>>> p = pyautogui.position() # And again.
>>> p
Point(x=1536, y=637)
>>> p[0] # The x-coordinate is at index 0.
1536
>>> p.x # The x-coordinate is also in the x attribute.
1536
當(dāng)然,您的返回值將根據(jù)鼠標(biāo)光標(biāo)的位置而變化。
控制鼠標(biāo)交互
現(xiàn)在你知道了如何移動鼠標(biāo),并且知道了它在屏幕上的位置,你就可以開始點擊、拖動和滾動了。
點擊鼠標(biāo)
要向您的計算機(jī)發(fā)送虛擬鼠標(biāo)點擊,請調(diào)用pyautogui.click()
方法。默認(rèn)情況下,這種單擊使用鼠標(biāo)左鍵,并且發(fā)生在鼠標(biāo)光標(biāo)當(dāng)前所在的任何位置。如果您希望單擊發(fā)生在鼠標(biāo)當(dāng)前位置之外的某個位置,可以將單擊的 x 和 y 坐標(biāo)作為可選的第一個和第二個參數(shù)傳遞。
如果您想要指定使用哪個鼠標(biāo)按鈕,請包含關(guān)鍵字參數(shù)button
,其值為'left'
、'middle'
或'right'
。例如,pyautogui.click(100, 150, button='left')
將在坐標(biāo)(100, 150)
處單擊鼠標(biāo)左鍵,而pyautogui.click(200, 250, button='right')
將在坐標(biāo)(200, 250)
處執(zhí)行右鍵單擊。
在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> pyautogui.click(10, 5) # Move mouse to (10, 5) and click.
您應(yīng)該看到鼠標(biāo)指針移動到屏幕左上角附近,并單擊一次。完整的“點擊”定義為按下鼠標(biāo)按鈕,然后在不移動光標(biāo)的情況下釋放鼠標(biāo)按鈕。也可以通過調(diào)用只按下鼠標(biāo)按鈕的pyautogui.mouseDown()
和只釋放按鈕的pyautogui.mouseUp()
來執(zhí)行點擊。這些函數(shù)與click()
有相同的參數(shù),事實上,click()
函數(shù)只是這兩個函數(shù)調(diào)用的方便包裝器。
為了進(jìn)一步方便起見,pyautogui.doubleClick()
函數(shù)將用鼠標(biāo)左鍵執(zhí)行兩次點擊,而pyautogui.rightClick()
和pyautogui.middleClick()
函數(shù)將分別用鼠標(biāo)右鍵和鼠標(biāo)中鍵執(zhí)行一次點擊。
拖動鼠標(biāo)
拖動是指按住一個鼠標(biāo)鍵的同時移動鼠標(biāo)。例如,您可以通過拖移文件夾圖標(biāo)在文件夾之間移動文件,或者您可以在日歷應(yīng)用中四處移動約會。
PyAutoGUI 提供了pyautogui.dragTo()
和pyautogui.drag()
函數(shù)來將鼠標(biāo)光標(biāo)拖動到一個新位置或相對于當(dāng)前位置的位置。dragTo()
和drag()
的參數(shù)與moveTo()
和move()
相同:x 坐標(biāo)/水平移動,y 坐標(biāo)/垂直移動,以及可選的持續(xù)時間。(鼠標(biāo)移動過快時 MacOS 無法正確拖動,建議傳遞一個duration
關(guān)鍵字參數(shù)。)
要嘗試這些函數(shù),請打開一個圖形繪制應(yīng)用,例如 Windows 上的微軟畫圖、MacOS 上的 Paintbrush 或 Linux 上的 GNU 畫圖。(如果您沒有繪圖應(yīng)用,可以在使用在線應(yīng)用。)我將使用 PyAutoGUI 在這些應(yīng)用中繪圖。
將鼠標(biāo)光標(biāo)放在繪圖應(yīng)用的畫布上,選擇鉛筆或畫筆工具,在新的文件編輯器窗口中輸入以下內(nèi)容,并將其另存為spiralDraw.py
:
import pyautogui, time
? time.sleep(5)
? pyautogui.click() # Click to make the window active.
distance = 300
change = 20
while distance > 0:
? pyautogui.drag(distance, 0, duration=0.2) # Move right.
? distance = distance – change
? pyautogui.drag(0, distance, duration=0.2) # Move down.
? pyautogui.drag(-distance, 0, duration=0.2) # Move left.
distance = distance – change
pyautogui.drag(0, -distance, duration=0.2) # Move up.
運行這個程序的時候會有五秒鐘的延遲?用于在選擇鉛筆或畫筆工具的情況下,將鼠標(biāo)光標(biāo)移動到繪圖程序的窗口上。然后spiralDraw.py
會控制鼠標(biāo)點擊使繪圖程序的窗口活躍?。活動窗口是當(dāng)前接受鍵盤輸入的窗口,你所采取的行動——比如打字,或者在這種情況下,拖動鼠標(biāo)——將影響那個窗口?;顒哟翱谝卜Q為聚焦窗口或前臺窗口。一旦繪圖程序激活,spiralDraw.py
繪制一個類似于圖 20-2 左側(cè)的方形螺旋圖案。雖然你也可以使用第 19 章中的模塊創(chuàng)建一個方形螺旋圖像,但是通過控制鼠標(biāo)在微軟畫圖中繪制來創(chuàng)建圖像可以讓你利用這個程序的各種筆刷風(fēng)格,就像右邊的圖 20-2 一樣,以及其他高級功能,比如漸變或填充桶。你可以自己預(yù)先選擇筆刷設(shè)置(或者讓你的 Python 代碼選擇這些設(shè)置),然后運行螺旋繪制程序。
圖 20-2:來自pyautogui.drag()
示例的結(jié)果,用微軟畫圖的不同筆刷繪制
distance
變量從200
開始,所以在while
循環(huán)的第一次迭代中,第一個drag()
調(diào)用將光標(biāo)向右拖動 200 個像素,耗時 0.2 秒。distance
然后減少到 195,第二個drag()
調(diào)用將光標(biāo)向下拖動 195 像素。第三個drag()
調(diào)用水平將光標(biāo)拖動 –195(向左 195),distance
減小到 190,最后一個drag()
調(diào)用將光標(biāo)向上拖動 190 像素。在每一次迭代中,鼠標(biāo)被向右、向下、向左和向上拖動,并且distance
比前一次迭代中的略小。通過循環(huán)這段代碼,您可以移動鼠標(biāo)光標(biāo)來繪制一個方形螺旋。
你可以用手(或者更確切地說,用鼠標(biāo))畫出這個螺旋,但是你必須慢慢地工作才能如此精確。PyAutoGUI 幾秒鐘就能搞定!
注
在撰寫本文時,PyAutoGUI 無法向某些程序發(fā)送鼠標(biāo)點擊或按鍵,如防病毒軟件(防止病毒禁用該軟件)或 Windows 上的視頻游戲(使用不同的方法接收鼠標(biāo)和鍵盤輸入)。您可以在
pyautogui.readthedocs.io
查看在線文檔,看看這些功能是否已經(jīng)添加。
滾動鼠標(biāo)
最后一個 PyAutoGUI 鼠標(biāo)函數(shù)是scroll()
,它傳遞了一個整數(shù)參數(shù),表示您希望鼠標(biāo)上下滾動多少個單位。一個單元的大小因操作系統(tǒng)和應(yīng)用的不同而不同,所以您必須進(jìn)行實驗,看看在您的特定情況下它到底能滾動多遠(yuǎn)。滾動發(fā)生在鼠標(biāo)光標(biāo)的當(dāng)前位置。傳遞正整數(shù)向上滾動,傳遞負(fù)整數(shù)向下滾動。當(dāng)鼠標(biāo)光標(biāo)位于Mu編輯器窗口上時,在Mu編輯器的交互式 Shell 中運行以下內(nèi)容:
>>> pyautogui.scroll(200)
如果鼠標(biāo)光標(biāo)在可以向上滾動的文本字段上,您將看到 Mu 向上滾動。
編程您的鼠標(biāo)移動
編寫自動點擊屏幕的程序的困難之一是找到你想要點擊的東西的 x 和 y 坐標(biāo)。pyautogui.mouseInfo()
函數(shù)可以幫你做到這一點。
pyautogui.mouseInfo()
函數(shù)應(yīng)該從交互式 Shell 中調(diào)用,而不是作為程序的一部分。它啟動 PyAutoGUI 中包含的一個名為 MouseInfo 的小應(yīng)用。應(yīng)用的窗口看起來像圖 20-3 。
圖 20-3:MouseInfo 應(yīng)用的窗口
在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> pyautogui.mouseInfo()
這將顯示 MouseInfo 窗口。此窗口以三個整數(shù)的 RGB 元組和十六進(jìn)制值的形式提供鼠標(biāo)光標(biāo)當(dāng)前位置的信息,以及鼠標(biāo)光標(biāo)下像素的顏色。顏色本身出現(xiàn)在窗口的顏色框中。
為了幫助您記錄坐標(biāo)或像素信息,您可以點擊八個復(fù)制或記錄按鈕中的一個。復(fù)制全部、復(fù)制 XY、復(fù)制 RGB 和復(fù)制 RGB 十六進(jìn)制按鈕會將它們各自的信息復(fù)制到剪貼板?!叭坑涗洝?、“XY 記錄”、“RGB 記錄”和“RGB 十六進(jìn)制記錄”按鈕會將各自的信息寫入窗口中的大文本字段。您可以通過單擊保存日志按鈕來保存日志文本字段中的文本。
默認(rèn)情況下,3 秒。“按鈕延遲”復(fù)選框被選中,導(dǎo)致在點按“拷貝”或“記錄”按鈕和拷貝或記錄發(fā)生之間有三秒鐘的延遲。這給了你很短的時間點擊按鈕,然后移動鼠標(biāo)到你想要的位置。取消選中此框,將鼠標(biāo)移動到位,并按F1
到F8
鍵來復(fù)制或記錄鼠標(biāo)位置可能更容易。您可以查看 MouseInfo 窗口頂部的“復(fù)制”和“日志”菜單,找出哪個鍵映射到哪個按鈕。
例如,取消選中 3 秒。按鈕延遲,然后在按下F6
按鈕的同時在屏幕上移動鼠標(biāo),注意鼠標(biāo)的 x 和 y 坐標(biāo)是如何記錄在窗口中間的大文本字段中的。您可以稍后在 PyAutoGUI 腳本中使用這些坐標(biāo)。
有關(guān) MouseInfo 的更多信息,請查看位于mouseinfo.readthedocs.io
的完整文檔。
使用屏幕
你的 GUI 自動化程序不必盲目地點擊和輸入。PyAutoGUI 具有屏幕截圖函數(shù),可以根據(jù)屏幕的當(dāng)前內(nèi)容創(chuàng)建一個圖像文件。這些函數(shù)還可以返回當(dāng)前屏幕外觀的 PillowImage
對象。如果你已經(jīng)跳過了這本書,你會想在繼續(xù)本節(jié)之前閱讀第 17 章并安裝pillow
模塊。
在 Linux 電腦上,需要安裝scrot
程序才能使用 PyAutoGUI 中的截圖函數(shù)。在終端窗口中,運行sudo apt-get install scrot
來安裝這個程序。如果您使用的是 Windows 或 MacOS,請?zhí)^這一步,繼續(xù)本節(jié)。
獲取截圖
用 Python 截圖,調(diào)用pyautogui.screenshot()
函數(shù)。在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> im = pyautogui.screenshot()
im
變量將包含截圖的Image
對象。您現(xiàn)在可以調(diào)用im
變量中的Image
對象的方法,就像任何其他的Image
對象一樣。第 19 章有更多關(guān)于Image
物體的信息。
解析截圖
假設(shè)您的 GUI 自動化程序中的一個步驟是單擊一個灰色按鈕。在調(diào)用click()
方法之前,您可以截取一個屏幕截圖,查看腳本將要點擊的像素。如果它和灰色按鈕的灰色不一樣,那么你的程序就知道有問題??赡艽翱谝馔庖苿樱蛘呖赡軓棾龅膶υ捒蜃柚沽税粹o。在這一點上,你的程序可以“看到”它沒有點擊正確的東西并自行停止,而不是繼續(xù)——并且可能因為點擊錯誤的東西而造成混亂。
您可以使用pixel()
函數(shù)獲得屏幕上特定像素的 RGB 顏色值。在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> pyautogui.pixel((0, 0))
(176, 176, 175)
>>> pyautogui.pixel((50, 200))
(130, 135, 144)
傳遞一組坐標(biāo),比如(0, 0)
或(50, 200)
,它會告訴你圖像中這些坐標(biāo)處像素的顏色。從pixel()
返回的值是一個 RGB 元組,包含像素中紅色、綠色和藍(lán)色的數(shù)量。(alpha 沒有第四個值,因為截圖圖像是完全不透明的。)
如果屏幕上給定的 x 和 y 坐標(biāo)處的像素與給定的顏色匹配,PyAutoGUI 的pixelMatchesColor()
函數(shù)將返回True
。第一個和第二個參數(shù)是 x 和 y 坐標(biāo)的整數(shù),第三個參數(shù)是屏幕像素必須匹配的 RGB 顏色的三個整數(shù)的元組。在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
? >>> pyautogui.pixel((50, 200))
(130, 135, 144)
? >>> pyautogui.pixelMatchesColor(50, 200, (130, 135, 144))
True
? >>> pyautogui.pixelMatchesColor(50, 200, (255, 135, 144))
False
在使用pixel()
獲得特定坐標(biāo)處像素顏色的 RGB 元組后?,將相同的坐標(biāo)和 RGB 元組傳遞給pixelMatchesColor()
?,該返回True
。然后改變 RGB 元組中的一個值,對同一個坐標(biāo)再次調(diào)用pixelMatchesColor()
?。這應(yīng)該歸false
。每當(dāng)您的 GUI 自動化程序?qū)⒁{(diào)用click()
時,調(diào)用這個方法會很有用。注意,給定坐標(biāo)處的顏色必須完全匹配。如果稍有不同,例如,(255, 255, 254)
而不是(255, 255, 255)
,那么pixelMatchesColor()
將返回False
。
圖像識別
但是如果您事先不知道 PyAutoGUI 應(yīng)該點擊哪里呢?您可以改用圖像識別。給 PyAutoGUI 一個你想點擊的圖像,讓它算出坐標(biāo)。
例如,如果您之前已經(jīng)截屏捕獲了submit.png
中提交按鈕的圖像,locateOnScreen()
函數(shù)將返回找到該圖像的坐標(biāo)。要了解locateOnScreen()
的工作原理,可以試著在你的屏幕上截取一個小區(qū)域的截圖;然后保存圖像,在交互式 Shell 中輸入以下內(nèi)容,用截圖的文件名替換'submit.png'
:
>>> import pyautogui
>>> b = pyautogui.locateOnScreen('submit.png')
>>> b
Box(left=643, top=745, width=70, height=29)
>>> b[0]
643
>>> b.left
643
Box
對象是一個命名的元組,它由locateOnScreen()
返回,具有左邊的 x 坐標(biāo)、上邊的 y 坐標(biāo)、圖像在屏幕上第一次出現(xiàn)時的寬度和高度。如果你用你自己的屏幕截圖在你的計算機(jī)上嘗試這個,你的返回值將與這里顯示的不同。
如果在屏幕上找不到圖像,locateOnScreen()
ReturnNone
。請注意,為了被識別,屏幕上的圖像必須與提供的圖像完全匹配。如果圖像有一個像素的偏差,locateOnScreen()
會引發(fā)一個ImageNotFoundException
異常。如果您更改了屏幕分辨率,以前屏幕截圖中的圖像可能與當(dāng)前屏幕上的圖像不匹配。您可以在操作系統(tǒng)的顯示設(shè)置中更改縮放比例,如圖圖 20-4 所示。
圖 20-4:Windows 10(左)和 MacOS(右)中的標(biāo)尺顯示設(shè)置
如果可以在屏幕的幾個地方找到圖像,locateAllOnScreen()
將返回一個Generator
對象。生成器超出了本書的范圍,但是你可以把它們傳遞給list()
來返回一個四整數(shù)元組的列表。對于在屏幕上找到圖像的每個位置,將有一個四整數(shù)元組。繼續(xù)交互式 Shell 示例,輸入以下內(nèi)容(并用您自己的圖像文件名替換'submit.png'
):
>>> list(pyautogui.locateAllOnScreen('submit.png'))
[(643, 745, 70, 29), (1007, 801, 70, 29)]
每個四整數(shù)元組代表屏幕上的一個區(qū)域。在上面的例子中,圖像出現(xiàn)在兩個位置。如果您的圖像只在一個區(qū)域中找到,那么使用list()
和locateAllOnScreen()
返回一個只包含一個元組的列表。
一旦有了想要選擇的特定圖像的四整數(shù)元組,就可以通過將元組傳遞給click()
來單擊該區(qū)域的中心。在交互式 Shell 中輸入以下內(nèi)容:
>>> pyautogui.click((643, 745, 70, 29))
作為一種快捷方式,您也可以將圖像文件名直接傳遞給click()
函數(shù):
>>> pyautogui.click('submit.png')
moveTo()
和dragTo()
函數(shù)也接受圖像文件名參數(shù)。記住locateOnScreen()
在屏幕上找不到圖像時會拋出異常,所以你應(yīng)該在try
語句中調(diào)用它:
try:
location = pyautogui.locateOnScreen('submit.png')
except:
print('Image could not be found.')
如果沒有try
和except
語句,這個未被捕獲的異常會使你的程序崩潰。既然你不能保證你的程序總能找到圖像,那么在調(diào)用locateOnScreen()
時使用try
和except
語句是個好主意。
獲取窗口信息
圖像識別是一種在屏幕上尋找東西的脆弱方式;如果單個像素是不同的顏色,那么pyautogui.locateOnScreen()
就找不到圖像。如果您需要找到特定窗口在屏幕上的位置,使用 PyAutoGUI 的窗口函數(shù)會更快、更可靠。
注
從 0.9.46 版本開始,PyAutoGUI 的窗口函數(shù)只能在 Windows 上運行,不能在 MacOS 或 Linux 上運行。這些特性來自 PyAutoGUI 包含的 PyGetWindow 模塊。
獲取活動窗口
屏幕上的活動窗口是當(dāng)前處于前臺并接受鍵盤輸入的窗口。如果您當(dāng)前正在Mu編輯器中編寫代碼,則Mu編輯器的窗口是活動窗口。在屏幕上的所有窗口中,一次只有一個是活動的。
在交互式 Shell 中,調(diào)用pyautogui.getActiveWindow()
函數(shù)來獲得一個Window
對象(在 Windows 上運行時,嚴(yán)格來說是一個Win32Window
對象)。
一旦有了那個Window
對象,就可以檢索該對象的任何屬性,這些屬性描述了它的大小、位置和標(biāo)題:
left, right, top, bottom
:窗口邊界的 x 或 y 坐標(biāo)的單個整數(shù)
topleft
,topright
,bottomleft
,bottomright
:一個由兩個整數(shù)組成的命名元組,表示窗口角點的(x, y)
坐標(biāo)
midleft
,midright
,midleft
,midright
:一個由兩個整數(shù)組成的命名元組,表示窗口邊界中間的(x, y)
坐標(biāo)
width
,height
:一個整數(shù),表示窗口的一個大小,以像素為單位
size
:窗口的(寬度,高度)的兩個整數(shù)的命名元組
area
:表示窗口面積的單個整數(shù),以像素為單位
center
:窗口中心的(x, y)
坐標(biāo)的兩個整數(shù)的命名元組
centerx
,centery
:窗口中心的 x 或 y 坐標(biāo)的單個整數(shù)
box
:一個含有四個整數(shù)的命名元組,用于窗口的(左、上、寬、高)度量
title
:標(biāo)題窗口頂部標(biāo)題欄中的一串文本
例如,要從window
對象獲取窗口的位置、大小和標(biāo)題信息,請在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> fw = pyautogui.getActiveWindow()
>>> fw
Win32Window(hWnd=2034368)
>>> str(fw)
'<Win32Window left="500", top="300", width="2070", height="1208", title="Mu 1.0.1 – test1.py">'
>>> fw.title
'Mu 1.0.1 – test1.py'
>>> fw.size
(2070, 1208)
>>> fw.left, fw.top, fw.right, fw.bottom
(500, 300, 2070, 1208)
>>> fw.topleft
(256, 144)
>>> fw.area
2500560
>>> pyautogui.click(fw.left + 10, fw.top + 20)
現(xiàn)在,您可以使用這些屬性來計算窗口內(nèi)的精確坐標(biāo)。如果你知道你想要點擊的按鈕總是在窗口左上角的右邊 10 個像素和左邊 20 個像素,并且窗口的左上角在屏幕坐標(biāo)(300, 500)
,那么調(diào)用pyautogui.click(310, 520)
(或者pyautogui.click(fw.left + 10, fw.top + 20)
),如果fw
包含窗口的Window
對象,將會點擊按鈕。這樣,你就不必依賴速度較慢、不太可靠的locateOnScreen()
函數(shù)來為你找到按鈕。
其他獲取窗口的方式
雖然getActiveWindow()
對于獲取函數(shù)調(diào)用時的活動窗口很有用,但是您需要使用一些其他函數(shù)來獲取屏幕上其他窗口的Window
對象。
以下四個函數(shù)返回一個Window
對象列表。如果他們找不到任何窗口,他們會返回一個空列表:
pyautogui.getAllWindows()
:返回屏幕上每個可見窗口的Window
對象列表。
pyautogui.getWindowsAt(x, y)
:返回包含點(x, y)
的每個可見窗口的Window
對象列表。
pyautogui.getWindowsWithTitle(title)
:返回標(biāo)題欄中包含字符串title
的每個可見窗口的Window
對象列表。
pyautogui.getActiveWindow()
:返回當(dāng)前接收鍵盤焦點的窗口的Window
對象。
PyAutoGUI 還有一個pyautogui.getAllTitles()
函數(shù),它返回每個可見窗口的字符串列表。
操縱窗口
窗口屬性能做的不僅僅是告訴你窗口的大小和位置。您還可以設(shè)置它們的值,以便調(diào)整窗口大小或移動窗口。例如,在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> fw = pyautogui.getActiveWindow()
? >>> fw.width # Gets the current width of the window.
1669
? >>> fw.topleft # Gets the current position of the window.
(174, 153)
? >>> fw.width = 1000 # Resizes the width.
? >>> fw.topleft = (800, 400) # Moves the window.
首先,我們使用Window
對象的屬性找出關(guān)于窗口大小的信息?和位置?。在 Mu 編輯器中調(diào)用這些函數(shù)后,窗口應(yīng)該會移動?而變窄?,如圖 20-5 所示。
圖 20-5:Mu 編輯器窗口前(上)和后(下)使用窗口對象屬性移動和調(diào)整其大小
您還可以找出并更改窗口的最小化、最大化和激活狀態(tài)。嘗試在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> fw = pyautogui.getActiveWindow()
? >>> fw.isMaximized # Returns True if window is maximized.
False
? >>> fw.isMinimized # Returns True if window is minimized.
False
? >>> fw.isActive # Returns True if window is the active window.
True
? >>> fw.maximize() # Maximizes the window.
>>> fw.isMaximized
True
? >>> fw.restore() # Undoes a minimize/maximize action.
? >>> fw.minimize() # Minimizes the window.
>>> import time
>>> # Wait 5 seconds while you activate a different window:
? >>> time.sleep(5); fw.activate()
? >>> fw.close() # This will close the window you're typing in.
isMaximized
?、isMinimized
、?和isActive
?屬性包含指示窗口當(dāng)前是否處于該狀態(tài)的布爾值。maximize()
?、minimize()
、?、activate()
、?,還有restore()
?方法改變窗口的狀態(tài)。用maximize()
或minimize()
最大化或最小化窗口后,restore()
方法會將窗口恢復(fù)到原來的大小和位置。
close()
方法?將窗戶關(guān)上。請小心使用這種方法,因為它可能會繞過任何要求您在退出應(yīng)用之前保存工作的消息對話框。
PyAutoGUI 窗口控制特性的完整文檔可以在找到。您還可以通過 PyGetWindow 模塊單獨使用 PyAutoGUI 的這些函數(shù),該模塊在pygetwindow.readthedocs.io
中說明。
控制鍵盤
PyAutoGUI 還具有向您的計算機(jī)發(fā)送虛擬按鍵的函數(shù),這使您能夠填寫表單或向應(yīng)用中輸入文本。
從鍵盤上發(fā)送字符串
pyautogui.write()
函數(shù)向計算機(jī)發(fā)送虛擬按鍵。這些按鍵的作用取決于哪個窗口是活動的,哪個文本字段具有焦點。您可能希望首先向所需的文本字段發(fā)送鼠標(biāo)單擊,以確保它獲得焦點。
舉個簡單的例子,讓我們用 Python 自動鍵入單詞Hello, world!
進(jìn)入文件編輯窗口。首先,打開一個新的文件編輯器窗口,并把它放在屏幕的左上角,這樣 PyAutoGUI 就可以在正確的地方點擊,使它成為焦點。接下來,在交互式 Shell 中輸入以下內(nèi)容:
>>> pyautogui.click(100, 200); pyautogui.write('Hello, world!')
請注意,將兩個命令放在同一行上,用分號隔開,可以防止交互式 Shell 在運行這兩個指令之間提示您輸入。這可以防止在click()
和write()
調(diào)用之間意外地將一個新窗口帶入焦點,這會破壞示例。
Python 將首先向坐標(biāo)(100, 200)
發(fā)送一個虛擬鼠標(biāo)點擊,這將點擊文件編輯器窗口并使其成為焦點。這個write()
調(diào)用將發(fā)送文本Hello, world!
到窗口,使它看起來像圖 20-6 。你現(xiàn)在有代碼可以為你打字了!
圖 20-6:使用 PyAutogGUI 點擊文件編輯器窗口并在里面輸入Hello, world!
默認(rèn)情況下,write()
函數(shù)會立即輸入完整的字符串。但是,您可以傳遞一個可選的第二個參數(shù),在每個字符之間添加一個短暫的停頓。第二個參數(shù)是暫停秒數(shù)的整數(shù)或浮點值。例如,pyautogui.write('Hello, world!', 0.25)
會在鍵入H
后等待四分之一秒,在e
后等待四分之一秒,以此類推。這種漸進(jìn)的打字機(jī)效果對于那些處理擊鍵速度跟不上 PyAutoGUI 的較慢的應(yīng)用可能很有用。
對于A
或者!
這樣的字符,PyAutoGUI 也會自動模擬按住SHIFT
鍵。
鍵名
不是所有的鍵都容易用單個文本字符來表示。例如,如何將SHIFT
或左箭頭鍵表示為單個字符?在 PyAutoGUI 中,這些鍵盤鍵由短字符串值來表示:'esc'
表示ESC
鍵,或者'enter'
表示ENTER
鍵。
代替單個字符串參數(shù),可以將這些鍵盤按鍵字符串的列表傳遞給write()
。例如,下面的調(diào)用按下 A 鍵,然后按下 B 鍵,然后按下左箭頭鍵兩次,最后按下 X 和 Y 鍵:
>>> pyautogui.write(['a', 'b', 'left', 'left', 'X', 'Y'])
因為按左箭頭鍵會移動鍵盤光標(biāo),所以會輸出XYab
。表 20-1 列出了 PyAutoGUI 鍵盤的按鍵串,你可以將這些按鍵串傳遞給write()
來模擬按下任意按鍵組合。
您還可以檢查pyautogui.KEYBOARD_KEYS
列表,查看 PyAutoGUI 將接受的所有可能的鍵盤按鍵字符串。'shift'
字符串是指左shift
鍵,相當(dāng)于'shiftleft'
。同樣適用于'ctrl'
、'alt'
和'win'
字符串;都是指左方向鍵。
表 20-1: PyKeyboard
屬性
鍵盤按鍵 | 含義 |
---|---|
'a' 、'b' 、'c' 、'A' 、'B' 、'C' 、'1' 、'2' 、'3' 、'!' 、'@' 、'#' 等等 |
單個字符的按鍵 |
'enter' (或'return' 或'\n' ) |
Enter 鍵 |
'esc' |
ESC 鍵 |
'shiftleft' ,'shiftright'
|
左右Shift 鍵 |
'altleft' ,'altright'
|
左右ALT 鍵 |
'ctrlleft' ,'ctrlright'
|
左右CTRL 鍵 |
'tab' (或'\t' ) |
Tab 鍵 |
'backspace' ,'delete'
|
Backspace 和Del
|
'pageup' ,'pagedown'
|
PgUp 和PgDn 鍵 |
'home' ,'end'
|
Home 和End 鍵 |
'up' 、'down' 、'left' 、'right'
|
向上、向下、向左和向右箭頭鍵 |
'f1' 、'f2' 、'f3' 等等 |
F1 到F12 鍵 |
'volumemute' 、'volumedown' 、'volumeup'
|
靜音、調(diào)低音量和調(diào)高音量鍵(有些鍵盤沒有這些鍵,但是您的操作系統(tǒng)仍然能夠理解這些模擬按鍵) |
'pause' |
Pause 鍵 |
'capslock' 、'numlock' 、'scrolllock'
|
CapsLock 、NumLock 和ScrollLock 鍵 |
'insert' |
INS 或INSERT 鍵 |
'printscreen' |
PRTSC 或PrintScr 鍵 |
'winleft' ,'winright'
|
左右Win 鍵(在 Windows 上) |
'command' |
命令(Cmd )鍵(在 MacOS 上) |
'option' |
Option 鍵(在 MacOS 上) |
按下并松開按鍵
與mouseDown()
和mouseUp()
函數(shù)非常相似,pyautogui.keyDown()
和pyautogui.keyUp()
會向計算機(jī)發(fā)送虛擬按鍵和釋放信號。他們被傳遞一個鍵盤按鍵串(見表 20-1 )作為他們的參數(shù)。為了方便起見,PyAutoGUI 提供了pyautogui.press()
函數(shù),它調(diào)用這兩個函數(shù)來模擬一次完整的按鍵。
運行下面的代碼,它將輸入一個美元符號字符(通過按住SHIFT
鍵并按 4 獲得):
>>> pyautogui.keyDown('shift'); pyautogui.press('4'); pyautogui.keyUp('shift')
這條線按下檔位
,按下(并松開)4,然后松開檔位
。如果需要在文本字段中鍵入字符串,那么write()
函數(shù)更合適。但是對于采用單鍵命令的應(yīng)用來說,press()
函數(shù)是更簡單的方法。
熱鍵組合
熱鍵或快捷方式是調(diào)用一些應(yīng)用功能的按鍵組合。復(fù)制選擇的常用熱鍵是CTRL+C
(在 Windows 和 Linux 上)或Cmd+C
(在 MacOS 上)。用戶按住CTRL
鍵,再按C
鍵,然后松開C
和CTRL
鍵。要使用 PyAutoGUI 的keyDown()
和keyUp()
函數(shù)實現(xiàn)這一點,您必須輸入以下內(nèi)容:
pyautogui.keyDown('ctrl')
pyautogui.keyDown('c')
pyautogui.keyUp('c')
pyautogui.keyUp('ctrl')
這相當(dāng)復(fù)雜。相反,使用pyautogui.hotkey()
函數(shù),該函數(shù)接受多個鍵盤按鍵字符串參數(shù),按順序按下它們,然后按相反的順序釋放它們。對于CTRL
-C 的例子,代碼簡單如下:
pyautogui.hotkey('ctrl', 'c')
這個函數(shù)對于較大的熱鍵組合特別有用。在 Word 中,CTRL+ALT+SHIFT+S
熱鍵組合顯示樣式窗格。不用進(jìn)行八個不同的函數(shù)調(diào)用(四個keyDown()
調(diào)用和四個keyUp()
調(diào)用),你可以只調(diào)用hotkey('ctrl', 'alt', 'shift', 's')
。
設(shè)置您的 GUI 自動化腳本
GUI 自動化腳本是自動化枯燥東西的好方法,但是你的腳本也可能很挑剔。如果一個窗口在桌面上的錯誤位置或者一些彈出窗口意外出現(xiàn),你的腳本可能在屏幕上點擊了錯誤的東西。以下是設(shè)置 GUI 自動化腳本的一些技巧:
- 每次運行腳本時使用相同的屏幕分辨率,這樣窗口的位置就不會改變。
- 腳本單擊的應(yīng)用窗口應(yīng)該最大化,這樣每次運行腳本時,它的按鈕和菜單都在同一個位置。
- 在等待內(nèi)容加載時添加大量暫停;你不希望你的腳本在應(yīng)用準(zhǔn)備好之前就開始點擊。
- 使用
locateOnScreen()
找到按鈕和菜單點擊,而不是依賴 XY 坐標(biāo)。如果你的腳本找不到它需要點擊的東西,停止程序而不是讓它繼續(xù)盲目點擊。 - 使用
getWindowsWithTitle()
確保你認(rèn)為你的腳本點擊的應(yīng)用窗口存在,并使用activate()
方法將該窗口放在前臺。 - 使用第十一章的中的
logging
模塊來保存你的腳本所做的日志文件。這樣,如果你不得不中途停止你的腳本,你可以修改它,從它停止的地方繼續(xù)。 - 向腳本中添加盡可能多的校驗。想象一下,如果出現(xiàn)一個意外的彈出窗口或者您的計算機(jī)失去互聯(lián)網(wǎng)連接,它會如何失敗。
- 您可能希望在腳本開始運行時對其進(jìn)行監(jiān)控,以確保其正常運行。
您可能還想在腳本的開頭放置一個暫停,這樣用戶就可以設(shè)置腳本將要點擊的窗口。PyAutoGUI 有一個sleep()
函數(shù),它的作用與time.sleep()
相同(它只是讓你不必在腳本中添加import time
)。還有一個countdown()
函數(shù),打印倒計時的數(shù)字,給用戶一個直觀的指示,腳本將很快繼續(xù)。在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> pyautogui.sleep(3) # Pauses the program for 3 seconds.
>>> pyautogui.countdown(10) # Counts down over 10 seconds.
10 9 8 7 6 5 4 3 2 1
>>> print('Starting in ', end=''); pyautogui.countdown(3)
Starting in 3 2 1
這些技巧有助于使您的 GUI 自動化腳本更易于使用,并且更能從不可預(yù)見的情況中恢復(fù)。
PyAutoGUI 函數(shù)回顧
因為本章涵蓋了許多不同的函數(shù),所以這里有一個快速的參考摘要:
moveTo(x, y)
:將鼠標(biāo)光標(biāo)移動到給定的 x 和 y 坐標(biāo)。
move(xOffset, yOffset)
:將鼠標(biāo)光標(biāo)相對于其當(dāng)前位置移動。
dragTo(x, y)
:按住左鍵的同時移動鼠標(biāo)光標(biāo)。
drag(xOffset, yOffset)
:按住左鍵,相對于鼠標(biāo)光標(biāo)當(dāng)前位置移動鼠標(biāo)光標(biāo)。
click(x, y, botton)
:模擬一次點擊(默認(rèn)為左鍵)。
rightClick()
:模擬右鍵點擊。
middleClick()
:模擬中鍵點擊。
doubleClick()
:模擬雙擊左鍵。
mouseDown(x, y, button)
:模擬在x, y
位置按下給定按鈕。
mouseUp(x, y, button)
:模擬在 x, y
位置釋放給定按鈕。
scroll(units)
:模擬滾輪。正參數(shù)向上滾動;負(fù)參數(shù)向下滾動。
write(message)
:在給定的消息字符串中鍵入字符。
write([key1, key2, key3])
:鍵入給定的鍵盤按鍵字符串。
press(key)
:按下給定的鍵盤按鍵串。
keyDown(key)
:模擬按下給定的鍵盤鍵。
keyUp(key)
:模擬釋放給定的鍵盤鍵。
hotkey([key1, key2, key3])
:模擬按順序按下給定的鍵盤按鍵串,然后按相反的順序釋放。
screenshot()
:將截圖作為Image
對象返回。(關(guān)于Image
物體的信息,參見第 19 章。)
getActiveWindow()
,getAllWindows()
,getWindowsAt()
和getWindowsWithTitle()
這些函數(shù)返回窗口對象,可以調(diào)整應(yīng)用窗口在桌面上的大小和位置。
getAllTitles()
返回桌面上每個窗口的標(biāo)題欄文本的字符串列表。
驗證碼和計算機(jī)倫理
“區(qū)分計算機(jī)和人類的全自動公共圖靈測試”或“驗證碼”是那些要求你在扭曲的圖片上鍵入字母或點擊消防栓照片的小測試。這些測試對人類來說很容易通過,盡管很煩人,但對軟件來說幾乎不可能解決。讀完這一章,你會發(fā)現(xiàn)寫一個腳本是多么容易,比方說,可以注冊數(shù)十億個免費電子郵件帳戶或向用戶發(fā)送大量騷擾消息。驗證碼通過要求一個只有人類才能通過的步驟來緩解這種情況。
然而,并不是所有的網(wǎng)站都有驗證碼,這些驗證碼很容易被不道德的程序員濫用。學(xué)習(xí)編碼是一項強大而令人興奮的技能,你可能會為了個人利益甚至只是為了炫耀而濫用這種能力。但是正如一扇沒有上鎖的門不能成為非法侵入的理由一樣,你的程序的責(zé)任落在了你,程序員的身上。繞過系統(tǒng)造成傷害、侵犯隱私或獲得不公平的優(yōu)勢并不聰明。我希望我寫這本書的努力能讓你成為最有生產(chǎn)力的自己,而不是唯利是圖的自己。
項目:自動填表
在所有無聊的任務(wù)中,填表是最令人害怕的雜務(wù)?,F(xiàn)在,在項目的最后一章,你將會殺死它。假設(shè)你在一個電子表格中有大量的數(shù)據(jù),你必須繁瑣地在其他應(yīng)用的表單界面中重新輸入數(shù)據(jù)——沒有實習(xí)生為你做這些。雖然有些應(yīng)用會有導(dǎo)入功能,允許你上傳帶有信息的電子表格,但有時似乎沒有其他方法,只能連續(xù)幾個小時無意識地點擊和鍵入。你在這本書里已經(jīng)走了這么遠(yuǎn);你當(dāng)然知道一定有自動完成這個無聊任務(wù)的一種方式。
這個項目的表單是一個谷歌文檔表單,你可以在找到??雌饋硐駡D 20-7 。
圖 20-7:本項目使用的表格
概括地說,下面是您的程序應(yīng)該做的事情:
- 點擊表單的第一個文本字段。
- 在表單中移動,在每個字段中鍵入信息。
- 單擊提交按鈕。
- 對下一組數(shù)據(jù)重復(fù)這個過程。
這意味著您的代碼需要執(zhí)行以下操作:
- 調(diào)用
pyautogui.click()
點擊表單和提交按鈕。 - 調(diào)用
pyautogui.write()
在字段中輸入文本。 - 處理
KeyboardInterrupt
異常,這樣用戶可以按下CTRL
-C 退出。
打開一個新的文件編輯器窗口,保存為formFiller.py
。
第一步:計算步驟
在編寫代碼之前,您需要計算出將填寫表單一次的準(zhǔn)確擊鍵和鼠標(biāo)點擊。調(diào)用pyautogui.mouseInfo()
啟動的應(yīng)用可以幫你算出具體的鼠標(biāo)坐標(biāo)。您只需要知道第一個文本字段的坐標(biāo)。點擊第一個字段后,只需按下Enter
即可將焦點移動到下一個字段。這將使您不必為每個字段計算要單擊的 x 和 y 坐標(biāo)。
以下是在表單中輸入數(shù)據(jù)的步驟:
- 將鍵盤焦點放在
name
字段上,這樣按鍵就可以在該字段中鍵入文本。 - 鍵入一個名稱,然后按下
Enter
。 - 鍵入最大的恐懼,然后按下
Enter
。 - 按下向下箭頭鍵正確的次數(shù)來選擇向?qū)щ娫矗阂淮螢?em>魔杖,兩次為護(hù)身符,三次為水晶球,四次為金錢。然后按下
Enter
。(請注意,在 MacOS 上,您必須為每個選項再按一次向下箭頭鍵。對于某些瀏覽器,您可能還需要按下Enter
。) - 按右箭頭鍵選擇機(jī)器戰(zhàn)警問題的答案。按一次
2
,兩次3
,三次4
,四次5
或者直接按空格鍵選擇1
(默認(rèn)高亮顯示)。然后按下tab
。 - 鍵入附加注釋,然后按下
Enter
。 - 按
Enter
來“點擊”提交按鈕。 - 提交表單后,瀏覽器會將您帶到一個頁面,您需要通過該頁面上的鏈接返回到表單頁面。
不同操作系統(tǒng)上的不同瀏覽器的工作方式可能與這里給出的步驟略有不同,所以在運行程序之前,請檢查這些按鍵組合是否適用于您的計算機(jī)。
第二步:設(shè)置坐標(biāo)
進(jìn)入autbor.com/form
,在瀏覽器中加載您下載的示例表格(圖 20-7 )。
使您的源代碼看起來像下面這樣:
#! python3
# formFiller.py - Automatically fills in the form.
import pyautogui, time
# TODO: Give the user a chance to kill the script.
# TODO: Wait until the form page has loaded.
# TODO: Fill out the Name Field.
# TODO: Fill out the Greatest Fear(s) field.
# TODO: Fill out the Source of Wizard Powers field.
# TODO: Fill out the RoboCop field.
# TODO: Fill out the Additional Comments field.
# TODO: Click Submit.
# TODO: Wait until form page has loaded.
# TODO: Click the Submit another response link.
現(xiàn)在,您需要實際想要輸入到該表單中的數(shù)據(jù)。在現(xiàn)實世界中,這些數(shù)據(jù)可能來自電子表格、純文本文件或網(wǎng)站,并且需要額外的代碼來加載到程序中。但是對于這個項目,您只需將所有這些數(shù)據(jù)硬編碼到一個變量中。將以下內(nèi)容添加到您的程序中:
#! python3
# formFiller.py - Automatically fills in the form.
--snip--
formData = [{'name': 'Alice', 'fear': 'eavesdroppers', 'source': 'wand',
'robocop': 4, 'comments': 'Tell Bob I said hi.'},
{'name': 'Bob', 'fear': 'bees', 'source': 'amulet', 'robocop': 4,
'comments': 'n/a'},
{'name': 'Carol', 'fear': 'puppets', 'source': 'crystal ball',
'robocop': 1, 'comments': 'Please take the puppets out of the
break room.'},
{'name': 'Alex Murphy', 'fear': 'ED-209', 'source': 'money',
'robocop': 5, 'comments': 'Protect the innocent. Serve the public
trust. Uphold the law.'},
]
--snip--
formData
列表包含四個不同名字的四個字典。每個字典都將文本字段的名稱作為鍵,將響應(yīng)作為值。設(shè)置的最后一點是將 PyAutoGUI 的PAUSE
變量設(shè)置為在每次函數(shù)調(diào)用后等待半秒鐘。此外,提醒用戶點擊瀏覽器,使其成為活動窗口。在您的程序中,在formData
賦值語句之后添加以下內(nèi)容:
pyautogui.PAUSE = 0.5
print('Ensure that the browser window is active and the form is loaded!')
第三步:開始輸入數(shù)據(jù)
一個for
循環(huán)將遍歷formData
列表中的每個字典,將字典中的值傳遞給 PyAutoGUI 函數(shù),該函數(shù)將虛擬地在文本字段中鍵入內(nèi)容。
將以下代碼添加到您的程序中:
#! python3
# formFiller.py - Automatically fills in the form.
--snip--
for person in formData:
# Give the user a chance to kill the script.
print('>>> 5-SECOND PAUSE TO LET USER PRESS CTRL-C <<<')
? time.sleep(5)
--snip--
作為一個小小的安全功能,腳本有一個五秒鐘的暫停?這給了用戶一個機(jī)會來點擊CTRL+C
(或者移動鼠標(biāo)光標(biāo)到屏幕的左上角來引發(fā)FailSafeException
異常)來關(guān)閉程序,以防它正在做一些意想不到的事情。在等待頁面加載時間的代碼之后,添加以下內(nèi)容:
#! python3
# formFiller.py - Automatically fills in the form.
--snip--
? print('Entering %s info...' % (person['name']))
? pyautogui.write(['\t', '\t'])
# Fill out the Name field.
? pyautogui.write(person['name'] + '\t')
# Fill out the Greatest Fear(s) field.
? pyautogui.write(person['fear'] + '\t')
--snip--
我們添加了一個偶然的print()
調(diào)用,在終端窗口顯示程序的狀態(tài),讓用戶知道正在發(fā)生什么?。
由于表單已經(jīng)有時間加載,調(diào)用pyautogui.write(['\t', '\t'])
按兩次TAB
并將name
字段置于焦點?。然后再次調(diào)用write()
在person['name']
中輸入字符串?。將'\t'
字符添加到傳遞給write()
的字符串的末尾,以模擬按下TAB 鍵
,這將鍵盤焦點移動到下一個字段,最大的恐懼。對write()
的另一個調(diào)用將把person['fear']
中的字符串輸入到這個字段中,然后跳轉(zhuǎn)到表單中的下一個字段。。
第四步:處理選擇列表和單選按鈕
“巫師能力”問題的下拉菜單和 RoboCop 字段的單選按鈕比文本字段更難處理。要用鼠標(biāo)點擊這些選項,您必須計算出每個可能選項的 x 和 y 坐標(biāo)。使用鍵盤箭頭鍵進(jìn)行選擇會更容易。
將以下內(nèi)容添加到您的程序中:
#! python3
# formFiller.py - Automatically fills in the form.
--snip--
# Fill out the Source of Wizard Powers field.
? if person['source'] == 'wand':
? pyautogui.write(['down', '\t'] , 0.5)
elif person['source'] == 'amulet':
pyautogui.write(['down', 'down', '\t'] , 0.5)
elif person['source'] == 'crystal ball':
pyautogui.write(['down', 'down', 'down', '\t'] , 0.5)
elif person['source'] == 'money':
pyautogui.write(['down', 'down', 'down', 'down', '\t'] , 0.5)
# Fill out the RoboCop field.
? if person['robocop'] == 1:
? pyautogui.write([' ', '\t'] , 0.5)
elif person['robocop'] == 2:
pyautogui.write(['right', '\t'] , 0.5)
elif person['robocop'] == 3:
pyautogui.write(['right', 'right', '\t'] , 0.5)
elif person['robocop'] == 4:
pyautogui.write(['right', 'right', 'right', '\t'] , 0.5)
elif person['robocop'] == 5:
pyautogui.write(['right', 'right', 'right', 'right', '\t'] , 0.5)
--snip--
一旦下拉菜單獲得焦點(請記住,您編寫了代碼來模擬在填寫完最大的恐懼字段后按下TAB
),按下向下箭頭鍵將移動到選擇列表中的下一項。根據(jù)person['source']
中的值,你的程序應(yīng)該在跳轉(zhuǎn)到下一個字段之前發(fā)送一個數(shù)量的向下箭頭鍵。如果這個用戶字典中的'source'
鍵的值是'wand'
?我們模擬按下一次向下箭頭鍵(選擇和)并按下TAB
?。如果'source'
鍵的值是'amulet'
,我們模擬按下向下箭頭鍵兩次并按下標(biāo)簽
,以此類推,得到其他可能的答案。這些write()
調(diào)用中的0.5
參數(shù)在每一個鍵之間添加了半秒鐘的停頓,這樣我們的程序就不會為表單移動得太快。
可以用右箭頭鍵選擇機(jī)器戰(zhàn)警問題的單選按鈕,或者,如果您想選擇第一個選項?,按空格鍵?。
第五步:提交表單,等待
您可以通過將person['comments']
作為一個參數(shù)傳遞來使用write()
函數(shù)填充附加注釋字段。您可以鍵入一個附加的'\t'
來將鍵盤焦點移動到下一個字段或提交按鈕。一旦提交按鈕成為焦點,調(diào)用pyautogui.press('enter')
將模擬按下ENTER
鍵并提交表單。提交表單后,您的程序?qū)⒌却迕腌妬砑虞d下一頁。
一旦加載了新頁面,它將有提交另一個響應(yīng)鏈接,該鏈接將瀏覽器導(dǎo)向一個新的空表單頁面。在第二步 中,您將該鏈接的坐標(biāo)作為一個元組存儲在submitAnotherLink
中,因此將這些坐標(biāo)傳遞給pyautogui.click()
以單擊該鏈接。
新表單準(zhǔn)備就緒后,腳本的外層for
循環(huán)可以繼續(xù)進(jìn)行下一次迭代,并將下一個人的信息輸入表單。
通過添加以下代碼完成您的程序:
#! python3
# formFiller.py - Automatically fills in the form.
--snip--
# Fill out the Additional Comments field.
pyautogui.write(person['comments'] + '\t')
# "Click" Submit button by pressing Enter.
time.sleep(0.5) # Wait for the button to activate.
pyautogui.press('enter')
# Wait until form page has loaded.
print('Submitted form.')
time.sleep(5)
# Click the Submit another response link.
pyautogui.click(submitAnotherLink[0], submitAnotherLink[1])
一旦主for
循環(huán)完成,程序?qū)⒉迦朊總€人的信息。在這個例子中,只有四個人要進(jìn)入。但是如果你有4000
人,那么寫一個程序來做這件事會節(jié)省你很多時間和打字!
顯示消息框
到目前為止,您編寫的程序都傾向于使用純文本輸出(使用print()
函數(shù))和輸入(使用input()
函數(shù))。然而,PyAutoGUI 程序?qū)⑹褂媚愕恼麄€桌面作為它的游樂場。你的程序運行在基于文本的窗口中,無論是 Mu 還是終端窗口,當(dāng)你的 PyAutoGUI 程序點擊并與其他窗口交互時,可能會丟失。如果Mu或終端窗口隱藏在其他窗口之下,這會使用戶很難獲得輸入和輸出。
為了解決這個問題,PyAutoGUI 提供了彈出消息框來通知用戶并接收他們的輸入。有四種消息框函數(shù):
pyautogui.alert(text) 顯示text
并有一個確定按鈕。
pyautogui.confirm(text)
顯示text
,有確定和取消按鈕,根據(jù)點擊的按鈕返回'OK'
或'Cancel'
。
pyautogui.prompt(text) 顯示text
并有一個文本字段供用戶鍵入,它以字符串形式返回。
pyautogui.password(text) 與prompt()
相同,但顯示星號,以便用戶可以輸入敏感信息,如密碼。
這些函數(shù)還有一個可選的第二個參數(shù),它接受一個字符串值作為消息框標(biāo)題欄中的標(biāo)題。這些函數(shù)不會返回,直到用戶點擊了它們上面的一個按鈕,所以它們也可以用來在你的 PyAutoGUI 程序中引入暫停。在交互式 Shell 中輸入以下內(nèi)容:
>>> import pyautogui
>>> pyautogui.alert('This is a message.', 'Important')
'OK'
>>> pyautogui.confirm('Do you want to continue?') # Click Cancel
'Cancel'
>>> pyautogui.prompt("What is your cat's name?")
'Zophie'
>>> pyautogui.password('What is the password?')
'hunter2'
這些行產(chǎn)生的彈出消息框看起來像圖 20-8 。
圖 20-8:從左上至右下,alert()
、confirm()
、prompt()
、password()
創(chuàng)建的窗口
這些函數(shù)可用于提供通知或向用戶提問,同時程序的其余部分通過鼠標(biāo)和鍵盤與計算機(jī)進(jìn)行交互。完整的在線文檔可以在pymsgbox.readthedocs.io
找到。
總結(jié)
使用pyautogui
模塊的 GUI 自動化允許您通過控制鼠標(biāo)和鍵盤與計算機(jī)上的應(yīng)用進(jìn)行交互。雖然這種方法足夠靈活,可以做人類用戶可以做的任何事情,但缺點是這些程序?qū)λ麄凕c擊或鍵入的內(nèi)容相當(dāng)盲目。當(dāng)編寫 GUI 自動化程序時,盡量確保如果給它們錯誤的指令,它們會很快崩潰。崩潰很煩人,但比程序繼續(xù)出錯好多了。
您可以在屏幕上移動鼠標(biāo)光標(biāo),并使用 PyAutoGUI 模擬鼠標(biāo)點擊、擊鍵和鍵盤快捷鍵。pyautogui
模塊還可以檢查屏幕上的顏色,這可以為您的 GUI 自動化程序提供足夠的屏幕內(nèi)容的概念,以知道它是否偏離了軌道。你甚至可以給 PyAutoGUI 一個截圖,讓它算出你想要點擊的區(qū)域的坐標(biāo)。
您可以將所有這些 PyAutoGUI 特性結(jié)合起來,在您的計算機(jī)上自動執(zhí)行任何無意識的重復(fù)任務(wù)。事實上,看著鼠標(biāo)光標(biāo)自己移動,看著文本自動出現(xiàn)在屏幕上,簡直是一種催眠。為什么不把你省下來的時間花在坐著看你的程序為你做所有的工作上呢?看到你的聰明把你從無聊的事情中拯救出來,你會有一種滿足感。
練習(xí)題
-
如何觸發(fā) PyAutoGUI 的故障安全來停止一個程序?
-
什么函數(shù)返回當(dāng)前的
resolution()
? -
哪個函數(shù)返回鼠標(biāo)光標(biāo)當(dāng)前位置的坐標(biāo)?
-
pyautogui.moveTo()
和pyautogui.move()
有什么區(qū)別? -
有哪些函數(shù)可以用來拖動鼠標(biāo)?
-
什么函數(shù)調(diào)用會打出
"Hello, world!"
的字符? -
你如何為特殊的鍵按鍵,比如鍵盤的左箭頭鍵?
-
如何將當(dāng)前屏幕內(nèi)容保存到一個名為
screenshot.png
的圖像文件中? -
什么代碼會在每次調(diào)用 PyAutoGUI 函數(shù)后設(shè)置兩秒鐘的暫停?
-
如果您想在 Web 瀏覽器中自動化點擊和擊鍵,您應(yīng)該使用 PyAutoGUI 還是 Selenium?
-
PyAutoGUI 容易出錯的原因是什么?
-
如何找到標(biāo)題中包含文本
Notepad
的每個窗口的大??? -
比如說,你如何讓 Firefox 瀏覽器活躍起來,并出現(xiàn)在屏幕上其他窗口的前面?
實踐項目
為了練習(xí),編寫執(zhí)行以下操作的程序。
空閑檢測
許多即時消息程序通過檢測鼠標(biāo)在一段時間內(nèi)(比如 10 分鐘)沒有移動來確定您是空閑還是離開了計算機(jī)。也許您不在計算機(jī)旁,但不想讓其他人看到您的即時消息狀態(tài),因此進(jìn)入了空閑模式。編寫一個腳本,每隔 10 秒稍微挪動一下鼠標(biāo)光標(biāo)。輕推應(yīng)該足夠小和不頻繁,以便它不會妨礙您在腳本運行時使用電腦。
使用剪貼板讀取文本字段
雖然您可以使用pyautogui.write()
向應(yīng)用的文本字段發(fā)送擊鍵,但是您不能單獨使用 PyAutoGUI 來讀取文本字段中已經(jīng)存在的文本。這就是 Pyperclip 模塊可以提供幫助的地方。您可以使用 PyAutoGUI 獲得文本編輯器(如 Mu 或記事本)的窗口,通過單擊它將其帶到屏幕的前面,在文本字段內(nèi)單擊,然后發(fā)送CTRL+A
或Cmd+A
熱鍵“全選”和CTRL+C
或Cmd+C
熱鍵“復(fù)制到剪貼板”然后,您的 Python 腳本可以通過運行import pyperclip
和pyperclip.paste()
來讀取剪貼板文本。
按照這個過程寫一個程序,從窗口的文本字段中復(fù)制文本。使用pyautogui.getWindowsWithTitle('Notepad')
(或任何你選擇的文本編輯器)獲得一個窗口對象。這個窗口對象的top
和left
屬性可以告訴你這個窗口在哪里,而activate()
方法將確保它在屏幕的前面。然后,您可以單擊文本編輯器的主文本字段,例如,通過使用pyautogui.click()
將100
或200
像素添加到top
和left
屬性值,將鍵盤焦點放在那里。調(diào)用pyautogui.hotkey('ctrl', 'a')
和pyautogui.hotkey('ctrl', 'c')
選擇所有文本并復(fù)制到剪貼板。最后,調(diào)用pyperclip.paste()
從剪貼板中檢索文本,并將其粘貼到 Python 程序中。從那里,您可以隨意使用這個字符串,但現(xiàn)在只需將它傳遞給print()
。
請注意,PyAutoGUI 的窗口函數(shù)只能在 PyAutoGUI 1.0.0 版的 Windows 上運行,不能在 MacOS 或 Linux 上運行。
即時消息機(jī)器人
Google Talk、Skype、Yahoo Messenger、AIM 和其他即時消息應(yīng)用通常使用專有協(xié)議,這使得其他人很難編寫可以與這些程序交互的 Python 模塊。但是,即使這些專有協(xié)議也不能阻止你編寫 GUI 自動化工具。
Google Talk 應(yīng)用有一個搜索欄,可以讓你在好友列表中輸入用戶名,并在你按下ENTER
時打開一個消息窗口。鍵盤焦點自動移動到新窗口。其他即時消息應(yīng)用也有類似的打開新消息窗口的方式。編寫一個程序,自動向你朋友列表中的一組人發(fā)送通知消息。你的程序可能需要處理一些異常情況,比如朋友離線,聊天窗口出現(xiàn)在屏幕的不同坐標(biāo)上,或者確認(rèn)框打斷了你的消息。你的程序?qū)⒉坏貌唤仄羴碇笇?dǎo)它的 GUI 交互,并采用檢測虛擬擊鍵何時沒有被發(fā)送的方法。
注
你可能想要建立一些假的測試帳號,這樣你就不會在編寫這個程序的時候不小心給你真正的朋友發(fā)垃圾郵件。文章來源:http://www.zghlxwxcb.cn/news/detail-493315.html
游戲機(jī)器人教程
你可以在nostarch.com/automatestuff2
找到一個鏈接,里面有一個很棒的教程,名為“如何構(gòu)建一個可以玩網(wǎng)頁游戲的 Python 機(jī)器人”。本教程解釋了如何用 Python 創(chuàng)建一個 GUI 自動化程序來玩一個叫做壽司游戲的 Flash 游戲。游戲包括點擊正確的配料按鈕來滿足顧客的壽司訂單。你越快無誤地完成訂單,你得到的分?jǐn)?shù)就越多。這是一個非常適合 GUI 自動化程序的任務(wù)——也是一個作弊獲得高分的方法!本教程涵蓋了許多與本章相同的主題,但也包括 PyAutoGUI 的基本圖像識別功能的描述。這個機(jī)器人的源代碼在asweigart/sushigoroundbot
,機(jī)器人玩游戲的視頻在youtu.be/lfk_T6VKhTE
。文章來源地址http://www.zghlxwxcb.cn/news/detail-493315.html
到了這里,關(guān)于Python 自動化指南(繁瑣工作自動化)第二版:二十、使用 GUI 自動化控制鍵盤和鼠標(biāo)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!