가이드
파이썬 ADB 자동화 튜토리얼: 안드로이드 에뮬레이터 제어
Macro Automation Studio용 파이썬 ADB 자동화 튜토리얼: 앱이 adb를 대신 관리하는 동안 BlueStacks에서 탭, 스와이프, 입력, 키 누름, 스크린샷을 파이썬으로 실행합니다.
- Windows
- Mac
- 에뮬레이터
- 클라우드 기기
- 휴대폰
- Python SDK
이 페이지의 내용
대부분의 파이썬 ADB 자동화 튜토리얼은 adb shell input tap을 감싼 subprocess 래퍼로 끝납니다. Macro Automation Studio(MAS)용 이 파이썬 ADB 자동화 튜토리얼은 다른 길을 갑니다: 앱이 adb를 갖고, 스크립트는 mas 패키지로 앱과 통신합니다. 이 가이드는 새 프로젝트에서 시작해 앱을 열고, 검색어를 찾고, 스크롤하고, 스크린샷을 저장하는 스크립트까지 갑니다. adb 호출을 쓰지 않고 BlueStacks, LDPlayer, MuMu Player, MEmu를 조작하고 싶은 파이썬 개발자를 위한 가이드입니다.
시작하기 전에
- MAS가 설치되어 있고 체험 또는 요금제로 로그인한 상태. MAS 설치를 참고하세요.
- ADB가 켜진 에뮬레이터가 실행 중이고 Device Groups에 기기로 추가된 상태. 기기를 참고하세요.
- 기본 파이썬을 알고 있어야 합니다. 그 밖에 직접 설치할 것은 없습니다. MAS는
mas가 들어 있는 자체 Python 3.13 환경을 함께 제공합니다.
구성 요소가 맞물리는 방식
MAS는 프로젝트 폴더를 작업 디렉터리로 하여 스크립트를 python -u -m src.app로 시작합니다. 연결 정보는 환경 변수(MAS_RPC_PORT, MAS_RPC_HOST, MAS_DEVICE_ID, MAS_SESSION_TOKEN)로 넘깁니다. 모든 mas.* 호출은 앱으로 가는 JSON-RPC 2.0 요청이 됩니다. 앱이 adb 명령, 템플릿 검색 또는 OCR을 실행하고 결과를 돌려줍니다. 첫 줄이 실행되기 전에 기기가 바인딩되어 있으므로 connect() 호출도, 관리할 시리얼도 없습니다. 개념을 참고하세요.
프로젝트 만들기
- Macros를 열고 Create New Project를 클릭합니다.
- Code-Based를 선택하고 Target Device를 mobile로 설정한 뒤 프로젝트 이름을 입력하고 Create Project를 클릭합니다.
- 프로젝트를 엽니다. Code Editor에
src/app.py가 표시됩니다. - 기기 선택기에서 에뮬레이터를 고르고, 코드 조각을 시험하고 싶을 때마다 Run(F5)을 클릭합니다. Stop은 Shift+F5입니다.
화면 크기와 좌표
SDK에 넘기는 모든 좌표는 기기 화면 왼쪽 위 모서리 기준의 픽셀 오프셋이며, 기기 자체 해상도 기준입니다. 모니터에 보이는 에뮬레이터 창의 크기는 상관없습니다.
import mas
size = mas.get_screen_size()
print(f"Screen: {size.width}x{size.height}")
center = (size.width // 2, size.height // 2)
mas.click(*center)get_screen_size()는 width와 height가 있는 ScreenSize를 반환합니다. get_device_info()는 거기에 기기 name, type, connected 플래그를 더해 반환합니다. 스크립트가 해상도 변경을 견뎌야 한다면 끝의 예제처럼 위치를 크기의 비율로 계산하세요. 템플릿 이미지와 OCR 영역은 해상도 변경을 견디지 못합니다. 이미지 인식 가이드를 참고하세요.
파이썬 adb 스크린샷 찍기
import base64
import mas
shot = mas.take_screenshot()
print(shot.width, shot.height, shot.timestamp)
with open("screen.png", "wb") as f:
f.write(base64.b64decode(shot.base64))take_screenshot()은 Screenshot을 반환합니다: base64에 PNG가 담기고, width와 height는 픽셀 크기, timestamp는 ISO 8601 문자열입니다. 위 파일은 작업 디렉터리인 프로젝트 폴더에 저장됩니다. 같은 객체를 screenshot=shot으로 여러 find_object 호출에 넘기면 다시 캡처하지 않고 모두 한 프레임에서 찾습니다.
탭
mas.click(270, 800) # tap, then wait 1000 ms
mas.click(270, 800, delay_ms=300) # shorter pause for tight loopsclick(x, y, delay_ms=1000)은 한 번 탭하고 앱이 반응할 수 있도록 delay_ms만큼 기다립니다. 탭에는 누르고 있는 시간이 없습니다. 길게 누르려면 duration_ms와 함께 key_press를 쓰세요.
스와이프
mas.swipe((270, 750), (270, 300), duration_ms=400) # scroll a list down: drag from lower to upper
mas.swipe((100, 500), (400, 500), duration_ms=1000) # drag and drop: slow and deliberateswipe(from_coords, to_coords, duration_ms=1000)은 (x, y) 튜플 두 개를 받습니다. 짧은 지속 시간은 튕기고, 긴 지속 시간은 끕니다. 핀치 제스처는 별도 호출입니다: zoom_in()과 zoom_out()은 기본적으로 화면 중앙에서 percent=50으로 동작합니다.
입력
mas.click(270, 120, delay_ms=500) # focus the field first
mas.input_text("hello world")
mas.input_text("new value", clear=True) # replace what is thereinput_text(text, delay_ms=0, clear=False)는 포커스된 필드에 입력하므로 먼저 필드를 탭하세요. clear=True는 커서를 끝으로 옮기고 기존 문자를 지운 뒤 입력합니다.
키 누르기
from mas import KeyCode
mas.key_press(KeyCode.BACK)
mas.key_press(KeyCode.HOME)
mas.key_press(KeyCode.ENTER)
mas.key_press(KeyCode.DELETE, repeat=10) # ten presses in one command
mas.key_press(KeyCode.POWER, duration_ms=3000) # long presskey_press(key_code, modifiers=None, duration_ms=100, repeat=1)은 Android 키 이벤트를 보냅니다. duration_ms가 500 이상이면 길게 누르기가 되며, 그 이상의 정확한 밀리초는 반영되지 않습니다. repeat는 1부터 100까지이며 파이썬 반복문보다 훨씬 빠릅니다. KeyCode에는 D패드, 볼륨, 미디어, 숫자 키도 있습니다.
앱 열기와 확인
mas.open_app("com.android.settings", timeout_ms=5000)
print(mas.get_current_app()) # package name in front
print(mas.is_app_focused("com.android.settings")) # True or False
if mas.get_app_state("com.android.chrome") == mas.NOT_RUNNING:
mas.open_app("com.android.chrome")
mas.close_app("com.android.settings")open_app(package_name, timeout_ms=2000)은 패키지 이름으로 실행하고 timeout_ms만큼 기다립니다. 패키지 이름을 알아내려면 앱을 손으로 열고 mas.get_current_app()을 출력하세요. get_app_state는 NOT_INSTALLED, NOT_RUNNING, RUNNING_IN_BACKGROUND_SUSPENDED, RUNNING_IN_BACKGROUND 또는 RUNNING_IN_FOREGROUND를 반환합니다. close_app은 패키지를 강제 종료합니다.
완전한 스크립트
스크립트는 Android 설정을 열고, 검색어를 찾고, 결과를 스크롤하고, 스크린샷을 저장하고, OCR로 검색어가 화면에 있는지 확인합니다. 검색 상자는 Asset Lab에서 자른 템플릿입니다. ID를 본인 것으로 바꾸세요. 스와이프는 어떤 해상도에서도 동작하도록 화면 크기의 비율을 씁니다.
import base64
import sys
import time
import mas
from mas import KeyCode
PACKAGE = "com.android.settings"
TERM = "Display"
images = mas.images({"search_box": 301})
def main():
size = mas.get_screen_size()
mas.log(f"Screen {size.width}x{size.height}")
mas.open_app(PACKAGE, timeout_ms=5000)
if not mas.is_app_focused(PACKAGE):
mas.log(f"{PACKAGE} did not come to the front", level="error")
sys.exit(2)
box = mas.find_object_retry(images.search_box, total_tries=3, time_sleep=2.0)
if box is None:
mas.log("Search box not found; crop it in Asset Lab", level="error")
sys.exit(2)
mas.click(box.x, box.y, delay_ms=800)
mas.input_text(TERM, clear=True)
mas.key_press(KeyCode.ENTER)
time.sleep(2)
x = size.width // 2
mas.swipe((x, int(size.height * 0.75)), (x, int(size.height * 0.35)), duration_ms=600)
time.sleep(1)
shot = mas.take_screenshot()
with open("search_results.png", "wb") as f:
f.write(base64.b64decode(shot.base64))
mas.log(f"Saved search_results.png ({shot.width}x{shot.height})")
text = mas.read_text(screenshot=shot, psm=11)
mas.log(f"Term on screen: {TERM.lower() in text.text.lower()}")
mas.key_press(KeyCode.HOME)
if __name__ == "__main__":
main()psm=11을 쓴 read_text는 프레임 전체의 흩어진 텍스트를 읽으므로 결과 목록에 알맞습니다. 카운터 하나를 읽기 위한 영역과 모드는 OCR 가이드에서 다룹니다. 직접 쓰기보다 작업을 설명하고 싶다면 MAS Agent가 이런 스크립트를 대신 써 줍니다.
이 파이썬 튜토리얼에서 adb가 하는 일
adb를 직접 호출하지는 않지만 밑에서 일하고 있는 것은 adb입니다.
- 바이너리. 설치 프로그램이 adb를 함께 제공합니다. Windows에서는
C:\ProgramData\MacroAutomationStudio\3rdparty에 있고, 내장 사본이 대안입니다. Mac에서는 앱 번들 안에 있고, Homebrew의 adb가 대안입니다. PATH에는 아무것도 추가되지 않습니다. 자세한 내용은 설치 페이지에 있습니다. - 연결. 에뮬레이터는 로컬 TCP 포트로 adb를 노출합니다. Start나 Run을 클릭하면 MAS가 기기 카드의 포트로
adb connect 127.0.0.1:<port>를 실행하고 실행 동안 그 세션을 유지합니다. - 입력.
click은adb shell input tap,swipe는adb shell input touchscreen swipe,input_text는adb shell input text,key_press는adb shell input keyevent가 됩니다. 핀치 제스처는 에뮬레이터의 터치 입력 장치에 직접 씁니다. - 화면.
take_screenshot과 모든find_object는 Windows에서 PNG를 망가뜨리지 않는 바이너리 안전 형식인adb exec-out screencap -p로 캡처합니다.get_screen_size는wm size를 읽고,get_current_app은 윈도우 매니저를 읽습니다. - 앱.
open_app은monkey로 런처 인텐트를 보내고am start로 대체하며,close_app은am force-stop을 실행합니다. - 서버. MAS는 자체 adb 서버를 실행합니다. PATH의 두 번째 adb 빌드가 자기 서버를 시작하면 둘이 서로를 교체하고 기기가 Connecting으로 떨어집니다. ADB 문제 해결 페이지에 포트별 해결법이 있습니다.
앱이 이 모든 것을 하므로 BlueStacks에서 쓴 스크립트는 LDPlayer, 클라우드 기기, 또는 로컬 포트로 연결한 본인 휴대폰에서도 수정 없이 실행됩니다.
잘못될 수 있는 것
Could not discover RPC port
스크립트가 MAS가 아니라 터미널에서 시작되어 환경 변수가 없습니다. Code Editor의 Run이나 기기 카드에서 실행하세요.
탭이 엉뚱한 곳에 떨어짐
기기 픽셀 대신 창 픽셀을 쓰고 있거나 에뮬레이터 해상도가 바뀌었습니다. mas.get_screen_size()를 출력해 넘기는 좌표와 비교하세요. Asset Lab의 좌표는 이미 기기 픽셀입니다.
input_text가 아무것도 입력하지 않음
포커스된 필드가 없습니다. click으로 필드를 탭하고 입력 전에 delay_ms=500을 주세요. 일부 앱은 필드를 덮는 키보드를 엽니다. 입력 후 key_press(KeyCode.BACK)이 닫아 줍니다.
기기를 찾을 수 없거나 offline
에뮬레이터에서 ADB가 꺼져 있거나, 기기 카드의 포트가 틀렸거나, 다른 adb 서버가 넘겨받았습니다. ADB 문제 해결을 따르세요.
조작하는 앱이 게임이라면 다른 자동화 계정과 똑같이 조심해서 다루세요. 100% 안전한 자동화 도구는 없으므로, 책임감을 가지고 본인의 판단에 따라 자동화하세요.
다음 단계
관련 페이지
감사합니다. 잘못된 내용이 있으면 Discord에서 알려 주세요.
궁금한 점이 있으신가요? Discord에서 질문하기