-
[Python] 파이썬을 활용한 업무자동화 - 데스크탑 자동화(pyautogui - 파이썬으로 마우스, 키보드 제어하기) 연습 코드 정리코딩 연습/코딩배우기 2021. 7. 6. 20:57반응형
수년 전 '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
