屏幕截圖小工具的制作過程問題記錄 python PIL pynput pyautogui pyscreeze


  最近想做一個腳本小工具,方便寫一些操作說明文檔,它的功能很簡單,就是把腳本打開之后,鼠標進行操作點擊時,會在點擊后進行截圖,並在圖上標記出點擊的位置,有點類似於錄屏軟件的圖片版,這樣的話,如果要想用文檔說明某些系統的操作步驟,就打開腳本一頓操作,操作完之后,每次步驟就自動記錄下來了,帶來方便。最后工具是做成了,但是中間的探索過程並不順利,所以在這里記錄一下思路和解決問題的過程。

  大體思路:鼠標左鍵點擊,能夠獲取點擊的坐標,並在點擊之后進行屏幕截圖操作,之后再用圖片處理庫給鼠標點擊位置加上某種標記,思路很簡單,開始考慮實施。

  首先這種記錄工具,我第一想到的是,python+selenium 因為selenium有方便的截圖功能,而且之前接觸也比較多,所以可能用氣來方便些,遂開始了。遇到的第一個問題,就是鼠標點擊如何獲取點擊的位置坐標,才發現原來selenium是用代碼來控制瀏覽器的行為,而不能監控瀏覽器中發生的行為,這不是它設計的初衷,所陷入困難。后來想到雖然selenium不能監控瀏覽器中發生的事件,但是js代碼可以,所以找到了解決方案,即寫一段js去監控鼠標點擊的操作,之后把返回值用一個div的自定義屬性在頁面上保存起來,然后不斷在selenium中去找這個div的屬性值,直到坐標更新,再進行后續的處理。一頓調試之后,代碼終於能正常返回坐標值,又處理了一些特殊情況,比如點開新的選項卡,關閉選項卡等一系列操作,用try except 捕獲異常處理,最后終於能夠比較健壯的返回點擊的坐標。

  有了坐標,接着研究圖片處理,用PIL庫,思路就是,打開截圖,在截圖上的坐標位置畫一個透明圓,重新保存,思路還是比較簡單,但是畫透明圓的時候遇到一點困難--明明用的是半透明的顏色,當畫到圖片上保存的時候,圓就不再透明了,研究了一下,最后用Image.composite()方法解決,終於能夠貼出比較完美的帶透明圓的

  后續在調試過程中,又發現了selenium的一個不起眼的函數,driver.execute_async_script(),因為一般執行js腳本的話用driver.execute_script()執行了,后來查了一下driver.execute_async_script()的用法,發現它是異步執行的,即,能收到執行的js的返回值,且接收到返回值時才繼續往下執行,否則等待30s之后報錯,這樣的話,我就能直接用js獲取坐標傳值給selenium的webdriver了,所以我去掉了中間傳值用的div,精簡了代碼,但原來的也留了一版,以便比較性能。

  之后就開始正式測試小工具啦,從我們的后台操作開始----也是新問題的開端。登錄的界面很正常,但是登進系統之后,點擊界面的導航部分能夠正常返回坐標,但點擊屏幕主體區域並不能返回坐標值,后來研究發現,是因為頁面里有iframe,而webdriver沒有切換到iframe中,所以獲取不到iframe中的點擊坐標,但如果我切換到iframe中,又不能返回導航部分的坐標了,這個問題確實比較麻煩,想了一下看能不能用兩個線程去做這件事情,即一個監控主區域的坐標,另一個進程監控iframe中的坐標?又是一頓修改,並處理同步問題,最后以失敗告終,因為一個打開的瀏覽器驅動只對應於一個webdriver,即使有兩個線程,其中一個切換到iframe上的話,另一個因為引用的同一個webdriver,所以也會去監控iframe,所以這個方法行不通。

  再后來,想到也許我應該換個思路,用selenium的話頁面越復雜,代碼也會越來越復雜,所以就轉換了一種思路,拋棄了selenium,直接在系統層面監控鼠標的點擊,后邊的圖片處理可以復用,順着這個思路去查找python相關的包,發現pyHook可以實現鼠標監控,但看了下安裝略復雜,心想一定有替代品,最后找到了pynput,可以完美實現鼠標監控,之后再找截圖工具這個比較多,看了別人的博客比較之后發現pyautogui是不錯的,所以裝了這個,使用時還有個小插曲,發現pyautogui的截圖功能總是報錯,網上也沒有相關解答,后來觀察源代碼發現,引入某個依賴包時報錯了,導致功能不能實現,為什么報錯沒有深究(發現可能是因為引入包時生成的路徑最后一個斜桿是反的),但自己把那段代碼粘出來手動引用一次就正常了。在這個過程中又發現,原來pyautogui的截圖功能完全依賴於另一個不起眼的包 pyscreeze,pyautogui的功能很強大,可以說是系統層面的selenium,能實現各種點擊、輸入操作,但是目前我是用到的只是它的快速截圖功能,所以我不再使用pyautogui,而直接用pyscreeze去截圖,結果也很完美又精簡,最后一頓拼接調試,終於完成了這個截圖工具的demo,代碼就貼出來記錄一下,用的版本是python3.5.2。

import time
from PIL import Image, ImageDraw
import os
from pynput import mouse
from pynput.mouse import Button
import pyscreeze
# import pyautogui
# from pyscreeze import (center, grab, locate, locateAll, locateAllOnScreen,
# locateCenterOnScreen, locateOnScreen, pixel, pixelMatchesColor,
# screenshot)


def picture_draw(path, locate):
oriImg = pyscreeze.screenshot()
# Img.save(path)
# oriImg = Image.open(path)
maskImg = Image.new('RGBA', oriImg.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(maskImg)
draw.ellipse(locate, fill=(255, 255, 0, 100))

final = Image.composite(maskImg, oriImg, maskImg)
final.save(path)


def on_move(x, y):
pass
# print('Pointer moved to {o}'.format((x,y)))

i = 1
def on_click(x, y, button, pressed):
global i
# button_name = ''
# print(button)
if button == Button.left:
button_name = 'Left Button'
elif button == Button.middle:
button_name = 'Middle Button'
elif button == Button.right:
button_name = 'Right Button'
else:
button_name = 'Unknown'
if pressed:
if button == Button.left:
button_name = 'Left Button'
picture_path = os.path.abspath('.') + '\\picture%s.png' % str(i)
picture_draw(picture_path, (int(x) - 25, int(y) - 25, int(x) + 25, int(y) + 25))
i += 1
print('{0} Pressed at {1} at {2}'.format(button_name, x, y))
else:
# print('{0} Released at {1} at {2}'.format(button_name, x, y))
pass
if not pressed:
return False


def on_scroll(x, y, dx, dy):
# print('scrolled {0} at {1}'.format(
# 'down' if dy < 0 else 'up',
# (x, y)))
pass

while True:
with mouse.Listener(no_move=on_move, on_click=on_click, on_scroll=on_scroll, suppress=False) as listener:
listener.join()

還有很多冗余和不完善代碼,先留着吧,以后可能用的到,尤其是pyautogui的強大功能。 感謝大神們的博客,這段代碼的主體依舊保留了你們的風格。  

  最后總結一下,這個小工具是自己在看書的時候想到的,就想來實現一下,實現過程中有很多體會,有以下3點:
1、只有熟練用好js才能使selenium的功能得到最大的發揮,那將使selenium幾乎無所不能,所以如果要寫自動化代碼,多用一下js,既能練習一下js,又能讓代碼更健壯。
2、之后就是python世界的奇妙了,只要你想到的功能,都會有對應的庫來支持,而且不止一個,我們要挑選那些最簡單實用最多的庫(pynput),而不是博客上寫的最多的(pyHook)。
3、最后最大的一個感想,做這個功能,開始走了彎路,用selenium去解決,但也因此深刻體會到了“失敗是成功之母”,我不是走了彎路,而是排除了一個錯誤選項,從而使我離真相更近,所以以后遇到問題,先不用過多的考慮哪個方案更能完美,或者擔心走了彎路浪費了時間,其實浪費也不會浪費多少,盡管做就是了,不只是寫程序哦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM