goodthings4me.tistory.com
수년 전 'Automatic Mouse and keyboard'라는 프로그램으로 웹 프로그램 테스트 업무에 활용했었다. 파이썬으로도 마우스와 키보드를 제어할 수 있는 라이브러리(pyautogui)가 있다는 것을 알게 되었는데, 업무 자동화 무료 강의를 보고 나중에 참고 자료로 활용하고자 정리했다.
파이썬을 활용한 업무자동화 - pyautogui로 마우스, 키보드 제어하기
목 차
- 윈도우 & 마우스 위치 이동
- 마우스 액션
- 스크린 샷 & pixel 좌표로 색상 값 얻기
- 이미지 인식 처리 - 기본
- 찾는 이미지 영역이 2개 이상 발견된 경우
- 이미지 처리 속도 개선
- 자동화 대상이 바로 보여지지 않는 경우
- 윈도우(창) 다루기
- 키보드 다루기
- 메시지 박스
- 자동화 로그 남기는 법
※ 라이브러리 설치 : pip install pyautogui
* window에서 제공하는 object 정보를 사용하는 것이 아니라 이미지 기반으로 진행하는 것임
■ 윈도우 & 마우스 위치 이동
import pyautogui
size = pyautogui.size() # 현재 화면의 스크린 사이즈(가로, 세로)를 가져옴
print(size) # Size(width=1920, height=1080)
# size[0] : width
# size[1] : height
# 절대 좌표로 마우스 이동
pyautogui.moveTo(700, 200) # 지정한 위치(가로 x, 세로 y)로 마우스를 이동
pyautogui.moveTo(100, 500, duration=1.5) # 1.5초 동안 100, 200 위치로 이동
# 상대 좌표로 마우스 이동(현재 커서가 있는 위치로부터) - move()
pyautogui.moveTo(100, 100, duration=1.5)
print(pyautogui.position()) # Point(x, y) 현재 위치
pyautogui.move(100, 100, duration=1.5) # 100, 100 기준으로부터 +100, +100으로 이동
print(pyautogui.position()) # Point(x, y)
pyautogui.move(100, 100, duration=1.5) # 200, 200 기준으로부터 +100, +100으로 이동
print(pyautogui.position()) # Point(x, y)
p = pyautogui.position()
print(p[0], p[1]) # x, y
print(p.x, p.y) # x, y
[결과]
Size(width=1920, height=1080)
Point(x=100, y=100)
Point(x=200, y=200)
Point(x=300, y=300)
300 300
300 300
■ 마우스 액션
import pyautogui
pyautogui.sleep(2) # 2초 대기
print(pyautogui.position()) # 마우스 현재 위치 Point(x=, y=)
## click() = mouseDown() + mouseUp()
pyautogui.click(19, 44, duration=1) # (19, 44) 좌표를 마우스 클릭
pyautogui.click(clicks=10) # 10번 클릭
pyautogui.mouseDown()
pyautogui.mouseUp()
pyautogui.doubleClick() # pyautogui.click(clicks=2)와 같음
## mouseDown/Up, 그림판 등에서 선 긋기 등에 활용
pyautogui.moveTo(1160, 290)
pyautogui.mouseDown()
pyautogui.moveTo(1300, 400)
pyautogui.mouseUp()
## 마우스 우클릭
pyautogui.rightClick() # 마우스 우클릭
pyautogui.middleClick() # 마우스 휠
## 마우스 드래그 - 예로, 화면에 있는 창 클릭 후 이동
pyautogui.sleep(2)
print(pyautogui.position())
pyautogui.moveTo(975, 240)
pyautogui.drag(100, 0, duration=0.25) # 현재 위치 기준으로 x=100, y=0 만큼 드래그
# 너무 빠른 동작으로 drag 수행이 안될 때는 duration 값을 설정하고 실행
pyautogui.dragTo(1200, 240, duration=0.25) # 절대 좌표 기준으로 드래그
## 마우스 스크롤
pyautogui.scroll(500) # 양수이면 위 방향으로 500만큼 스크롤
pyautogui.scroll(-300) # 음수이면 아래 방향으로 300 스크롤
## 마우스 정보
pyautogui.mouseInfo() # MouseInfo 프로그램 창 팝업
# 위치 정보를 얻을 곳에 마우스 포인트 위치 후 F1 클릭 시
# 19,44 229,243,255 #E5F3FF 정보(XY Position, RGB Color, RGB as Hex) 얻어짐
## 자동화 실행 실패 시 중지하기 - 화면 가장자리 모서리에 마우스 위치
# 단, pyautogui.FAILSAFE = False 시는 중지 안함 - 가급적 사용 자제
pyautogui.FAILSAFE = True # Default는 True
for i in range(10):
pyautogui.move(100, 100)
pyautogui.sleep(1.5)
## 모든 동작에 수 초씩 sleep 적용
pyautogui.PAUSE = 1 # 1초, .sleep(1) 안해도 됨
for i in range(5):
pyautogui.move(100, 100)
■ 스크린 샷 & pixel 좌표로 색상 값 얻기
import pyautogui
## 현재 화면 스크린 샷 후 저장
img = pyautogui.screenshot()
img.save('./image/screenshot_1.png') # 파일로 저장
## 마우스 위치 픽셀의 좌표에 대한 색상을 반환 - pixelMatchesColor()
# --> 이 지정 색과 일치하는 경우, 특정 기능 수행하도록 할 수 있다.
# 좌상단 spyder icon 픽셀을 pyautogui.mouseInfo()로 확인 11,12 217,8,8 #D90808
pixel = pyautogui.pixel(11, 12)
print(pixel) # (217, 8, 8) 일치함 확인
print(pyautogui.pixelMatchesColor(11, 12, (217, 8, 8))) # True
print(pyautogui.pixelMatchesColor(11, 12, pixel)) # True
print(pyautogui.pixelMatchesColor(11, 12, (217, 8, 9))) # False
■ 이미지 인식 처리 - 기본
- pyautogui로 window 자동화를 할 때는 자동화 대상이 되는 영역을 이미지로 만들고 그 이미지를 전체 또는 지정된 화면 내에서 찾아 그 값을 반환해주는 방식으로 동작함
- 이미지가 없거나 변경되어 발견 못할 경우, None 출력
- pyautogui는 이미지 기반이기 때문에 화면 해상도가 바뀌거나 이미지가 약간이라도 바뀌면 실패율이 높음
- 이미지 만들기 : winKey + Shift + s 후 마무스로 영역 드래그하여 이미지 만듦
- 영역 캡처 후 그림판에 붙여넣기 후 잘라서 파일로 저장(.png) - arrow_icon.png
import pyautogui
arrow_icon = pyautogui.locateOnScreen('./image/arrow_icon.png')
# 파일명은 winkey + shitf + s 로 만든 이미지명
print(arrow_icon) # Box(left=1283, top=89, width=22, height=18)
pyautogui.click(arrow_icon) # 영역 찾아서 클릭
file_history = pyautogui.locateOnScreen('./image/file_history.png')
print(file_history) # Box(left=1643, top=994, width=55, height=19)
pyautogui.click(file_history)
■ 찾는 이미지 영역이 2개 이상 발견된 경우
(예로 체크박스 여러 개), locateAllOnScreen() 사용
import pyautogui
for i in pyautogui.locateAllOnScreen('./image/image_icon.png'):
print(i)
pyautogui.click(i, duration=0.5)
# Box(left=1336, top=167, width=22, height=25)
# Box(left=1336, top=191, width=22, height=25)
# Box(left=1336, top=215, width=22, height=25)
# Box(left=1336, top=239, width=22, height=25)
# Box(left=1336, top=263, width=22, height=25)
# Box(left=1336, top=287, width=22, height=25)
# Box(left=1336, top=311, width=22, height=25)
# Box(left=1336, top=335, width=22, height=25)
# Box(left=1336, top=359, width=22, height=25)
# Box(left=1336, top=383, width=22, height=25)
■ 이미지 처리 속도 개선
- grayscale=True, region=(), confidence=0.999
import pyautogui
# 1. GrayScale - 흑백으로 전환해서 비교 처리 - argument로 grayscale=True
file_history = pyautogui.locateOnScreen('./image/file_history.png', grayscale=True)
print(file_history) # Box(left=1643, top=994, width=55, height=19)
pyautogui.click(file_history)
# 2. 범위 지정 - region=()
pyautogui.mouseInfo() # argument로 region=(x, y, width, height) 1485, 981, 1738, 1023
file_history = pyautogui.locateOnScreen('./image/file_history.png', region=(1485, 918, 1738-1458, 1023-981))
print(file_history) # Box(left=1643, top=994, width=55, height=19)
pyautogui.click(file_history)
# 3. 정확도 조정 - 자동화 대상 영역 이미지의 해상도 줄이고 몇 % 이상이면 인식
# 모듈 설치 pip install opencv-python 그리고 argument로 confidence=0.999 (디폴트 값, 99.9%)
file_menu = pyautogui.locateOnScreen('./image/file_menu.png', confidence=0.7) # confidence 조정하면서 테스트
print(file_menu) # Box(left=1, top=31, width=39, height=22)
pyautogui.click(file_menu)
■ 자동화 대상이 바로 보여지지 않는 경우
(프로그램 실행 또는 버튼 클릭했을 때 약간의 시간이 필요한 경우)
import pyautogui
import time
## 1. 계속 기다리기
# 예시) 메모장 메뉴(파일)를 자동화 대상으로 하고 메모장을 닫은 후, 프로그램 실행 중 메모장을 띄웠을 때 자동화 실행되도록 하기
# 메모장을 안띄워서 발견 못하기 때문에 'None'이 반환됨을 이용 - while 문으로 계속 체크 중 메모장 띄우면 while문 탈출
while pyautogui.locateOnScreen('./image/file_menu_notepad.png') is None:
file_menu_notepad = pyautogui.locateOnScreen('./image/file_menu_notepad.png')
print(file_menu_notepad)
pyautogui.click(file_menu_notepad)
## 2. 일정 시간 동안 기다리기 (TimeOut)
timeout = 10 # 10초 대기
start = time.time() # 시작 시간 설정
file_menu_notepad = None
while file_menu_notepad is None:
file_menu_notepad = pyautogui.locateOnScreen('./image/file_menu_notepad.png')
end = time.time() # 종료 시간 설정
print(file_menu_notepad)
if end - start > timeout: # 지정한 10초를 초과하면
print('TimeOut')
break
pyautogui.click(file_menu_notepad)
## 함수로 만들어보기
def find_target(img_file, timeout):
start = time.time() # 시작 시간 설정
target = None
while target is None:
target = pyautogui.locateOnScreen(img_file)
end = time.time() # 종료 시간 설정
print(target)
if end - start > timeout:
break
return target
def my_click(img_file, timeout=30):
target = find_target(img_file, timeout)
if target:
pyautogui.click(target)
else:
print(f'[TimeOut {timeout}s] Target not found ({img_file}). Terminate program.')
sys.exit()
my_click('./image/file_menu_notepad.png', 10)
■ 윈도우(창) 다루기
## windows에서 응프로그램(계산기 등) 실행 시, 그 응프로그램의 위치 값을 받아올 수 있음
fw = pyautogui.getActiveWindow() # 현재 활성화된 윈도우(forground window), 즉 응용프로그램 실행 창
print(fw.title) # 실행 창의 제목 정보, Spyder (Python 3.8),
print(fw.size) # 실행 창의 크기 정보(width, height), Size(width=1938, height=1060)
print(fw.left, fw.top, fw.right, fw.bottom) # 실행 창의 좌표 정보, -9 -9 1929 1051
pyautogui.click(fw.left + 25, fw.top + 20) # 실행 창을 줄이거나 이동해도 해당 위치에서 마우스 클릭이 됨
fwAll = pyautogui.getAllWindows() # 모든 윈도우 가져오기
for w in fwAll:
print(w)
# <Win32Window left="2390", top="-10", width="2420", height="1370", title="Daum - Chrome">
# <Win32Window left="2390", top="-10", width="2420", height="1370", title="NAVER - Chrome">
# <Win32Window left="-9", top="-9", width="1938", height="1060", title="D:\backup\[_강의]">
# ...
## 어떤 제목을 가지는 윈도우(창) 정보 가져오기
windows = pyautogui.getWindowsWithTitle('Chrome') # list로 반환
print(type(windows)) # <class 'list'>
print(windows) # [Win32Window(hWnd=3345288), Win32Window(hWnd=67414)]
print(windows[0])
# <Win32Window left="2390", top="-10", width="2420", height="1370", title="Daum - Chrome">
for w in windows:
print(w)
# <Win32Window left="2390", top="-10", width="2420", height="1370", title="Daum - Chrome">
# <Win32Window left="2390", top="-10", width="2420", height="1370", title="NAVER - Chrome">
w = pyautogui.getWindowsWithTitle('메모장')
print(w)
# [Win32Window(hWnd=591528), Win32Window(hWnd=329480)]
print(w[0])
# <Win32Window left="3380", top="370", width="941", height="793", title="메모1 - Windows 메모장">
w = pyautogui.getWindowsWithTitle('메모장')[0]
if w.isActive == False: # 현재 활성화되지 않았다면
pywinauto.application.Application().connect(handle=w._hWnd).top_window().set_focus()
w.activate() # PyGetWindowException: Error code from Windows: 0 - 작업을 완료했습니다.
if w.isMaximized == False: # 최대화되지 않았다면
w.maximize()
pyautogui.sleep(1)
if w.isMinimized == False:
w.minimize()
pyautogui.sleep(1)
w.restore() # 화면 원복 (직전 상태)
■ 키보드 다루기
import pyautogui
import pywinauto
import pyperclip
w = pyautogui.getWindowsWithTitle('메모장')[0]
pywinauto.application.Application().connect(handle=w._hWnd).top_window().set_focus()
pyautogui.write('12345 나도코딩') # pyautogui는 영문, 숫자만 가능
pyautogui.write('NadoCoding', interval=0.25) # write 속도 조정
## 방향키, 기능키 등과 같이 쓰는 법 - list 형태로 넣기
pyautogui.write(['enter', 't', 'e', 's', 't', 'left', 'right', 'backspace', 'l', 'a', 'enter'], interval=0.25)
# enter t e s t 적고 왼쪽 방향키 1번, 오른쪽 방향키 1번, 백스페이스 1번 l a 적고 enter -- 키조작
# 'left', 'right' 등은 구글 automate the boring stuff with python 검색
# - https://automatetheboringstuff.com/
# - Chapter 20 – Controlling the Keyboard and Mouse with GUI Automation
#################################################################################
# PyKeyboard Attributes
# Keyboard key string / Meaning
# 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '!', '@', '#' / The keys for single characters
# 'enter' (or 'return' or '\n') / The ENTER key
# 'esc' / The ESC key
# 'shiftleft', 'shiftright' / The left and right SHIFT keys
# 'altleft', 'altright' / The left and right ALT keys
# 'ctrlleft', 'ctrlright' / The left and right CTRL keys
# 'tab' (or '\t') / The TAB key
# 'backspace', 'delete' / The BACKSPACE and DELETE keys
# 'pageup', 'pagedown' / The PAGE UP and PAGE DOWN keys
# 'home', 'end' / The HOME and END keys
# 'up', 'down', 'left', 'right' / The up, down, left, and right arrow keys
# 'f1', 'f2', 'f3', and so on / The F1 to F12 keys
# 'pause' / The PAUSE key
# 'capslock', 'numlock', 'scrolllock' / The CAPS LOCK, NUM LOCK, and SCROLL LOCK keys
# 'insert' / The INS or INSERT key
# 'printscreen' / The PRTSC or PRINT SCREEN key
# 'winleft', 'winright' / The left and right WIN keys (on Windows)
# 'command' / The Command (image) key (on macOS)
# 'option' / The OPTION key (on macOS)
# 'volumemute', 'volumedown', 'volumeup' / The mute, volume down, and volume up keys (some
# keyboards do not have these keys, but your
# operating system will still be able to understand
# these simulated keypresses)
#################################################################################
# 특수 문자 입력 - shift 4 -> $
pyautogui.keyDown('shift')
pyautogui.press('4')
pyautogui.keyUp('shift')
# 조합키(Hot Key) - Ctrl + A
pyautogui.keyDown('ctrl')
pyautogui.keyDown('a')
pyautogui.keyUp('a') # press('a')
pyautogui.keyUp('ctrl')
# 간편한 조합키(Hot Key) - hotkey()
pyautogui.hotkey('ctrl', 'alt', 'shift' 'a') # Ctrl + Alt + Shift + A
pyautogui.hotkey('ctrl', 'a') # Ctrl + A
# 한글 처리 - pip install pyperclip - 어떤 문장을 클립보드에 넣는 모듈
pyperclip.copy('나도코딩')
pyautogui.hotkey('ctrl', 'v')
def kor_write(text):
pyperclip.copy(text)
pyautogui.hotkey('ctrl', 'v')
kor_write('업무자동화 나도코딩')
■ 메시지 박스
import pyautogui
# countdown()
print('곧 시작합니다...')
pyautogui.countdown(3) # 3 2 1
print('자동화 시작')
# alert(), confirm(), prompt(), password()
pyautogui.alert('확인 버튼만 있는 팝업 경고창..!', '경고')
result = pyautogui.confirm('계속 진행하겠습니까?', '확인') # 확인, 취소 버튼
print(result) # 확인 OK, 취소 Cancel 리턴
result = pyautogui.prompt('파일명을 무엇으로 하시겠습니까?', '입력') # 사용자 입력
print(result) # 입력값, 입력 안하면 None 출력
result = pyautogui.password('암호를 입력하세요.')
print(result)
■ 파이썬 자동화 로그 남기는 법
- 레벨은 debug < info < warning < error < critical 순이며 설정 이상 레벨만 저장
- debug 레벨 이상 모든 로그 저장하려면, DEBUG
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s [%(levelname)s] %(message)s')
# format='%(asctime)s [%(levelname)s] %(message)s' - 시간 [로그레벨] 사용자 메시지 형태 로그 작성
# 로그 레벨별 로그 남기기
logging.debug('이거 누가 짠거야 ~') # 개발자 레벨 로그
logging.info('자동화 수행 준비') # 사용자에게 보이는 정보
logging.warning('이 스크립트는 오래되었습니다. 실행상에 문제가 있을 수 있습니다') # 경고 메시지
logging.error('에러가 발생했습니다. 에러 코드는 ...')
logging.critical('복구가 불가한 심각한 문제가 발생했습니다.')
## 터미널과 파일에 함께 로그 남기기
# 시간 [로그레벨] 메시지 형태 로그 작성
logFormatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
logger = logging.getLogger() # 로거 가져오기
logger.setLevel(logging.DEBUG) # 로그 레벨 설정
# 스트림 로그 출력 - 터미널(화면)
streamHandler = logging.StreamHandler()
streamHandler.setFormatter(logFormatter)
logger.addHandler(streamHandler)
# 파일
filename = datetime.now().strftime('mylogfile_%y-%m-%d_%H%M%S.log') # 현재 시간으로 하되 형태로 변경
fileHandler = logging.FileHandler(filename, encoding='utf-8')
fileHandler.setFormatter(logFormatter)
logger.addHandler(fileHandler)
# Test
logger.debug('로그를 남겨보는 테스트를 진행합니다.')
'코딩 연습 > 코딩배우기' 카테고리의 다른 글
[Python] 파이썬을 활용한 업무자동화 - 웹 자동화(iframe 스크래핑, Web Element, 동적페이지 스크래핑) 연습 코드 정리 (0) | 2021.07.07 |
---|---|
[Python] 파이썬 파일 시스템(디렉토리, 파일) 다루기 연습 코드 정리 (0) | 2021.07.06 |
[Python] 파이썬을 활용한 업무자동화 - 엑셀 자동화(with openpyxl) 연습 코드 정리 (0) | 2021.07.01 |
[Python] 파이썬 웹 크롤링 - 스크래핑 관련 연습 코드 [네이버 날씨 & 뉴스, 오늘의 영어지문 등 가져오기] (0) | 2021.06.29 |
[Python] 파이썬 웹 크롤링 - 스크래핑 관련 유튜브 강의[나도코딩] 연습 코드 정리 (1) | 2021.06.27 |
댓글