본문 바로가기
Python

파이썬 자동화 프로그램 만들기(pyautogui)

by To올라운더 2023. 8. 28.
반응형

사람은 어떻게 컴퓨터를 업무에 활용할까?

 

대부분 원하는 정보를 눈으로 찾고,

마우스를 클릭하거나, 키보드를 통해 내용을 입력하는 형태일 것이다..

 

그럼 이런 일을 컴퓨터가 직접할 수 있다면, 내가 하는 일도 대신해 줄 수 있지 않을까?

 

맞다. 자동화를 위한 가장 먼저 해야할 일은 이런 일을 작은 단위로 나누는 일이고(분석단계),

나눠진 작은 단위의 일을 코드로 작성하는 것이 우리가 해야할 일이다.

 

업무 형태에 따라 적합한 라이브러리가 다양하게 있지만,

오늘 주로 다뤄볼 라이브러리는 pyautogui이다.

 

pyautogui는 화면에서 원하는 그림을 찾거나,

키보드, 마우스를 조작하는 일을 할 수 있다.

에서 언급한 사람이 업무를 처리하는 일을

pyautogui가 할 수 있을 것 같은 느낌이 든다면 반은 성공한거다.

 

1. pyautogui 설치하기

 - pyautogui는 파이썬에서 지원하는 기본 라이브러리가 아니다.

  그래서 추가 설치가 필요하다.

pip list # 설치된 라이브러리 확인하기

#pyautogui 설치하기
pip install pyautogui

 - pip list를 통해 설치된 라이브러리를 확인해보면 pyautogui가 설치되어 있지 않은데, 

  pip install pyautogui를 입력하면 설치가 진행된다. 

 (실제 설치가 완료되고 나서 pip list를 확인해보면, pyautogui 뿐 아니라 pyperclip 등 다른 라이브러리도 함께 설치가 진행된다.

 

 

 

2. 필요한 배경 지식

 - pyautogui를 다루기 전 우리가 인식하는 화면이 어떤 구성을 갖추었는지 알고 있다면,

   이후 다룰 내용도 좀 더 쉽게 이해할 수 있을 것이다.

 

 - 우리가 일반적으로 화면(모니터)라고 부르는 영역은 픽셀(=우리말 화소) 단위로 구성된다.

   이런 픽셀이 모여 해상도라는 단위로 표현을 하는데, 가로 1920개, 세로 1080개의 픽셀로 구성된 화면을

   1920 * 1080 해상도라고 표현하기도 하고, FHD(Full HD)라고 부르기도한다.

   그리고 해상도와 모니터의 크기를 혼동하는 경우가 있는데, 일반적으로 모니터가 클수록 지원할 수 있는 해상도가 높은건 사실이지만, 두 가지가 동일함을 나타내지는 않는다. 아래의 우측 그림에서 보듯이, 모니터의 크기는 단순히 대각선의 길이를 나타내는 길이이며(보통 inch를 사용) TV와 같이 inch가 클수록 모니터가 크다라고 받아 들이면 된다. 반변 최대 해상도는 픽셀(화소)가 가로*세로로 몇개가 들어갔는지를 나타내는 단위로 같은 inch더라도 해상도(픽셀 수)에 따라 더 선명한 화면을 볼 수 있다.(최대 해상도가 높은 모니터는 아래의 해상도를 지원할 수 있다.)

 - 어쨋든 수많은 픽셀이 모여서 우리가 원하는 화면을 출력하게 되는데, 가로, 세로 픽셀의 위치를 알면 해당 화면에서의 위치를 확인할 수 있다. 위의 그림과 같이 좌측 상단을 (0, 0), 우측상단을 (1920, 0) 등의 형태로 표현하는 것도 가능하며, 좌측 상단을 기준으로 우측으로 이동하거나, 아래로 이동하면 해당 값이 증가한다.

 - 아래의 코드로 현재 모니터의 해상도를 확인할 수 있다.

import pyautogui as pag

print(pag.size()) # 현재 모니터 해상도 확인

 - 현재 width=1920, height=1080 으로 나타나며, 윈도우 - 디스플레이 설정을 확인 해볼 때에도 동일하게 나타난다.

 

3. 마우스 사용하기

 3-1) 마우스 현재 위치 확인

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당


for try_count in range(1,6):
    mouse_position = pag.position() # 마우스 위치 
    print(f'{try_count}번째 마우스 위치 : {mouse_position}')
    time.sleep(1)

 - 코드는 단순히 pyautogui 라이브러리의 position() 메서드로 마우스의 위치를 알 수 있다.

 - 반복문을 통해 해당 함수가 실행되는 동안 마우스를 움직여보면 위와 같은 마우스의 위치를 확인할 수 있다. 

 

 3-2) 마우스 이동

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당

pag.moveTo(400, 200)
print(pag.position())
time.sleep(1)


pag.moveTo(1400, 200)
print(pag.position())
time.sleep(1)


pag.moveTo(1400, 500)
print(pag.position())
time.sleep(1)


pag.moveTo(400, 500)
print(pag.position())
time.sleep(1)

  - 자세히 보면 커서가 해당 위치로 이동 되는 것을 볼 수 있는데, 이런 동작을 눈으로 직접 확인하고 싶다면,

    파라미터를 추가해주면 된다. 기존에 파라미터는 가로와 세로의 위치만 입력했다면, 그 뒤에 이동 시간을 추가할 수 있다.

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당

pag.moveTo(400, 200, 3) # (가로 위치, 세로 위치, 마우스 커서 이동 시간)
print(pag.position())
time.sleep(1)


pag.moveTo(1400, 200, 3)
print(pag.position())
time.sleep(1)


pag.moveTo(1400, 500, 3)
print(pag.position())
time.sleep(1)


pag.moveTo(400, 500, 3)
print(pag.position())
time.sleep(1)

 

 3-3) 마우스 (좌)클릭, 더블클릭, 우클릭, 드래그

 - 마우스의 기본 동작인 클릭과 드래그도 메서드로 지원하고 있다.

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당

pag.moveTo(400, 200, 2)
print(pag.position())
pag.click() # 클릭
print('(좌)클릭')
time.sleep(1)

pag.moveTo(1400, 200, 2)
print(pag.position())
pag.doubleClick() # 더블클릭
print('더블클릭')
time.sleep(1)

pag.moveTo(1400, 400, 2)
print(pag.position())
pag.click(clicks=2, interval=0.5) # click 옵션을 이용한 더블클릭
print('더블클릭2')
time.sleep(1)

pag.moveTo(1400, 500, 2)
print(pag.position())
pag.rightClick() # 우클릭
print('우클릭')
time.sleep(1)

pag.moveTo(1000, 500, 2)
print(pag.position())
pag.click(button='right') # click 옵션을 이용한 우클릭
print('우클릭2')
time.sleep(1)

pag.dragTo(400, 500, 2) # 드래그 현재 위치 -> (400, 500)
print(pag.position())
print('드래그')
time.sleep(1)

 - 우클릭과 더블클릭의 경우, 기본적인 메서드가 지원되지만 click 메서드의 옵션을 통해서도 사용할 수 있다.

 

 

 3-4) 마우스 휠 사용하기

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당

time.sleep(1)
pag.scroll(-50) # '-' 일때, 화면(휠)이 아래로 이동

time.sleep(1)
pag.scroll(50) # '+' 일때, 화면(휠)이 위로 이동

# 특정 위치에서 휠 조작하기
pag.scroll(-100, x=200, y=500) # 가로 200, 세로 500의 위치에서 휠을 아래로 -100 이동

- 휠의 동작도 특정 위치에서 가능한데, 마지막 처럼 휠의 동작과 위치를 지정해주면 해당 위치로 이동한 뒤 휠이 조작된다.

 

 

4. 키보드

 4-1) 키보드 입력하기

 - 우리가 별 생각없이 키보드를 입력하지만 실제 2가지 형태의 입력을 하고 있다.

 - 하나는 단축키와 같은 조합키를 사용할 때, Ctrl 버튼을 계속 누른 상태에서 대기 하는 형태의 입력과

   두번째는 순간적인 한번의 클릭으로 동작이 끝나는 형태의 입력이다. 

 - 첫번째의 대기 형태의 입력을 Ctrl, Alt, Shift 키 등을 하고, 나머지 키들은 두번째 처럼 한번의 입력으로 한번의 출력이 나타난다.

 - 첫번째 처럼 키를 누르고 있는 상태를 pyautogui에서는 keyDown 메서드를 두번째처럼 단순 입력으로 끝나는 상태를 press를 이용한다고 생각하면 된다.(실제 press 에는 keyDown과 keyUp을 통해 합성키를 만들 수도 있다. 하지만 실제 keyDown과 keyUp을 사용하는 경우는 드물고, 합성키(단축키)의 경우 hotkey 메서드를 이용한다.

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당

time.sleep(5)
pag.press('h')
pag.press('e')
pag.press('l')
pag.press('l')
pag.press('o')

time.sleep(1)
pag.press('home')
pag.press('enter')

 - 해당 코드를 실행하면 처음 5초 대기 시간을 이용해 메모장이나 워드 등 입력이 가능한 창을 띄우면

  hello 가 입력되고 다시 가장 앞으로 이동(home)한 뒤 enter 를 입력한다.

 

 - press의 경우, 키보드의 기능키들을 입력할 수 있는데, 반복 입력이 필요할 경우 presses 파라미터를 지정하면 원하는 횟수만큼 반복할 수 있다. 

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당

time.sleep(5)
pag.press('enter', presses=5) # 5회 반복
pag.press('1', presses=3) # 3회 반복

 

 

 4-2) 타이핑 입력하기

 - press 버튼을 이용하면 모든 키를 입력할 수 있으나, 긴 문장을 press를 이용해 입력하기에는 많은 부분에서 불편하다. 

 - 그래서 실제 입력이 필요한 경우, write 메서드를 이용하면 한번에 입력할 수 있다.

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당


time.sleep(3)
pag.write('Hellow Python World!\n')

pag.write('안녕하세요.') # 한글 지원안됨

- 그런데 해당 결과를 확인해보면 처음 입력한 영문(Hellow Python World!)는 정상적으로 입력되는 반면, 아래의 한글(안녕하세요.)는 마지막 온점만 입력되는 것을 볼 수 있는데, pyautogui는 한글의 write를 지원하지 못한다. 그럼 한글을 입력할 수 있는 방법이 없는가? 편법처럼 클립보드의 복사 기능을 이용하는 방법이 있다.

 

 4-3) 클립보드 복사, 붙여넣기를 이용한 입력+단축키(합성키)

 - hotkey 매서드를 이용하면 단축키를 입력할 수 있는데, 해당 키를 이용하면 우리가 일반적으로 사용하는 Ctrl+C(복사)와 Ctrl+V(붙여넣기) 등의 기능을 사용할 수 있다.

 - 그리고 실제 위에서 실행하지 못했던 한글의 입력 또한 pyperclip 을 이용하면 해당 변수 값을 클립보드에 복사 할 수 있는데, pyperclip은 pyautogui를 설치하면 함께 설치가 진행되는 라이브러리이다.

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당
import pyperclip

time.sleep(3)
pag.write('Hellow Python World!\n')

kor_text = '안녕하세요.'

pyperclip.copy(kor_text) # kor_text를 클립보드에 복사
pag.hotkey('ctrl','v') # 단축키 사용
pag.press('enter')

 

5. 화면 인식하기

 - 이제 자동화의 마지막 퍼즐인 화면 인식이 남아있다.

 - 화면의 스크린은 처음 필요한 배경지식을 설명하며, 잠시 언급을 하였는데,

   좌측 상단이 (0, 0) 우측으로 이동할 수록 x 좌표가 증가, 하단으로 이동할 수록 y좌표가 증가한다.

 - 해당 화면에서 원하는 이미지를 찾기 위해서 원하는 부분에 캡처를 진행한다.

 - 윈도우 10의 경우,  Win+Shift+S 을 입력하면 화면 캡처를 진행할 수 있다.

 - 이번 실습에서는 윈도우 파일 탐색기를 닫아볼 예정인데, 아래의 빨간색 영역을 캡처로 저장한다.

   (toolbar.jpg)

 

 - 사용방법은 locateOnScreen('경로') 를 이용하면 되는데... 실제 인식률이 너무 낮다.

 - 그래서 일반적으로 opencv나 다른 라이브러리를 사용하게 되는데 우리는 screen_search 라이브러리를 사용해 볼 예정이다. pyautogui의 locateOnScreen은 아래에서 보듯이 바로 옆에 파일탐색기가 띄어져 있지만 찾지 못한다....

import pyautogui as pag
import time # 반복동작 확인을 위한 대기시간 할당


search_image = pag.locateOnScreen('./toolbar.jpg')
print(search_image)

pip install screen_search # screen_search 라이브러리 설치
import pyautogui as pag
import screen_search

search_image = screen_search.Search('./toolbar.jpg')
search_image_pos = search_image.imagesearch()
print(search_image_pos)

 - 해당 코드를 실행하면 pyautogui와는 달리 결과값을 꽤 잘 찾아낸다. 

 - 간단히 코드를 보면 4라인에서 찾아야할 이미지 파일을 변수로 선언해주고, screen_search 라이브러리의 Search 매서드를 통해서 경로를 파라미터로 입력한다.

 - 해당 객체(search_image)에 위치 값을 나타내는 imagesearch() 매소드를 확인해보면 위에서 알아본 것과 같이 화면 모니터 좌측 상단이 0, 0이며 해당 값을 기준으로 튜플로 x, y의 위치가 나타난다. 

-  만약 해당 이미지를 화면 내에서 찾지 못하면, (-1, -1) 이 출력된다.

- 해당 값이 있다면 image를 찾아낸것이고, 해당 값이 -1이라면 찾지 못한것이기 때문에 해당 값으로 조건문을 구성할 수 있다.

- 위에서 처럼 같은 기능을 하기위한 라이브러리나 매서드라도 상황에 따라 효과적인 라이브러리가 따로 있다. 그래서 많이 찾아보고 많은 테스트를 해볼 수록 좋은 결과로 나타나는데,

- 처음 쓰는 라이브러리의 매서드나 파라미터를 어떻게 알 수 있을까? 라는 의문이 들 수 있다.

  : 해당 얘기는 다음 포스팅에서 작성해보겠다.

 

- 그러나 screen_search 라이브러리를 사용할 경우, 찾고자 하는 이미지의 좌측 상단 위치 값은 확인되나, 이미지의 중앙을 클릭할 순 없다. 중앙을 클릭하거나 위치를 찾기 위해서는 다른 라이브러리의 도움이 필요한데, open_cv, PIL 을 사용하면 해당 이미지의 사이즈를 받아올 수 있다.

 - 이미지의 크기를 반으로 나눈 값과 좌측 상단의 값을 더해주면 자연스럽게 이미지의 중앙을 선택할 수 있다.

import pyautogui as pag
import screen_search

search_image = screen_search.Search('./toolbar.jpg')
search_image_pos = search_image.imagesearch()
print(f'이미지 파일 좌측 상단 위치 : {search_image_pos}')

if search_image_pos[0] != 1: # 해당 이미지를 찾으면

    from PIL import Image # 이미지 크기를 확인 하기 위해 사용
    

    im = Image.open('./toolbar.jpg')
    print(f'이미지 크기 : {im.size}') # 이미지 크기 출력

    print(f'이미지 중앙 위치 값 : {search_image_pos[0]+im.size[0]/2, search_image_pos[1]+im.size[1]/2}')
    print(f'이미지 중앙 위치 값(올림 적용) : {search_image_pos[0]+round(im.size[0]/2), search_image_pos[1]+round(im.size[1]/2)}')

 - 코드 상 특별한건 없다. 위에 서술한 것처럼 이미지의 중앙을 선택하기 위해 pil 라이브러리를 이용해 이미지 크기를 확인하였고, screen_search 를 이용해 찾은 위치 정보와 pil을 이용해 찾은 이미지 크기의 절반을 더해 위치 값을 출력했다.

 - 그런데 반으로 나누다 보니 0.5라는 값이 나와 반올림을 위해 round를 사용했는데, 몫만 남기는 형태로 'im.size[0] // 2' 로 값을 산출해도 1픽셀의 위치 차이만 있을 뿐 유사한 위치값이 나온다.(round는 사사오입을 따른다. 4 이하는 버림, 5이상은 올림)

 

여기까지 습득했다면 기본기는 갖춰졌다.

 

이제 얼마나 세부적이고 다양한 상황을 조건문으로 나누고 분석할 수 있는지에 따라 매크로의 성능은 향상될 수 있다.

반응형