[파이썬 / 자동화] 머신러닝 없이 Selenium과 OCR로 네이버페이 자동결제 - 키패드 인식 후 자동 입력

파이썬으로 필요한 물건을 자동 구매할 수 있도록 프로그래밍 중인데, 가장 어려웠던 부분이 바로 결제 하는 부분이다. 요즘 쓸데없는 보안프로그램 설치하는 일이 없어서 비교적 쉽게 할 수는 있었지만, 그래도 가장 난관이었던 부분 중 하나이다. 검색해봐도 머신러닝 등을 이용하는 것들이 많았는데, 머신러닝을 하려면 컴퓨터 리소스도 많이 잡아먹기도 하거니와 트레이닝세트, 테스트세트 등 준비해야할 것이 많아 너무 번거로워보였다. 

 

간편결제 키패드만 인식해서 클릭하면 되는 건데 그렇게까지 어렵게 할 필요가 있을까 싶어서 열심히 연구하여 겨우 구성하였다. 만들고 디버깅하는데 8시간쯤 걸렸는데, 이걸 보는 다른 사람들은 30분만에 끝낼 수 있을 것이다.

 

 

 

설치 필요한 모듈

cv2

이미지 처리를 위한 모듈이다.

pipenv install opencv-python-headless

 

pytesseract

OCR (이미지에서 문자 인식) 기능을 위한 모듈이다

pipenv install pytesseract

 

pytesseract는 python에서 Tesseract-OCR이라는 OCR 기능을 이용하기 위한 모듈로,

컴퓨터에 Tesseract-OCR engine을 별로도 설치해야한다.

 

UB-Mannheim tesseract 홈페이지에서 다운로드 받을 수 있다.

https://github.com/UB-Mannheim/tesseract/wiki

 

Home

Tesseract Open Source OCR Engine (main repository) - UB-Mannheim/tesseract

github.com

 

사용할 때 설치 경로를 알아야한다.

나중에 파이썬 코드 작성시 다음 코드를 넣을 것이다.

pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

 

 

개요

네이버페이 자동결제는 다음과같은 단계로 이루어진다

1. Selenium을 입력하여 네이버페이 결제 비밀번호를 입력하는 단계까지 진행한다.

2. 네이버페이 비밀번호 키패드 입력창을 캡쳐하여 스크린샷으로 저장한다.

3. 스크린샷을 가공하여 OCR 인식이 잘 되도록 한다.

4. OCR로 스크린샷을 인식한 뒤 각 숫자의 좌표를 return한다.

5. 스크린샷 사이즈와 실제 창 사이즈 간의 배율을 계산하여, 실제 좌표를 계산한다.

6. Javascript code를 이용하여 숫자 클릭

 

 

 

 

과정

0. Import module

import cv2
import numpy as np
import pytesseract
from PIL import Image

 

1. Selenium을 입력하여 네이버페이 결제 비밀번호를 입력하는 단계까지 진행한다.

사람마다 여기까지는 코드가 다 다를테니, 이건 각자 상황에 맞추어서 진행하는 수밖에 없다.

결제하기 창이 나오면 다음과 같이 진행된다.

 

#네이버페이 비밀번호 입력 창이 나오기 전의 창 handles를 미리 기억해둔다
all_handles = driver.window_handles  

#결제하기 버튼 클릭
self.xp('//*[@id="container"]/div[10]/div/div/a').click()

#네이버페이 비밀번호 입력 창의 Window 핸들
#창이 뜰 때까지 시간이 걸리므로 for loop와 time.sleep을 이용해 새 창이 뜰때까지 시도
previous_all_handles = all_handles 
for _ in range(5):
    all_handles = driver.window_handles
    new_handles = [i for i in all_handles if i not in previous_all_handles]
    if new_handles:
        new_handle = new_handles[0]
        break
    time.sleep(0.5)

 

 

2. 네이버페이 비밀번호 키패드 입력창 캡쳐

네이버페이 비밀번호 입력 창으로 이동한 뒤 screenshot을 찍는다.

 

#네이버페이 비밀번호 입력창 스크린샷
driver.switch_to.window(new_handle)
driver.save_screenshot('screenshot.png')

 

3. 스크린샷 가공

그냥은 OCR이 거의 인식을 못한다.

이유는 모르겠다, 그냥 단색 바탕에 숫자일 뿐인데 가공하고 안하고에 따라 인식률이 엄청나게 차이난다.

가공하지 않으면 거의 인식을 못하더라.

# Specify the path to Tesseract-OCR executable
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

# Load the screenshot
image = cv2.imread('screenshot.png')

# Upperhalf mask
# Get the dimensions of the image
height, width = image.shape[:2]
midpoint = height // 2

# Create a mask for the upper half
# Note: Use (0, 0, 0) for black, (255, 255, 255) for white, or any other color that matches the background of the lower half.
mask_color = (255, 255, 255)  # Assuming white background
upper_half_mask = np.full((midpoint, width, 3), mask_color, dtype=np.uint8)

# Replace the upper half of the image with the mask
image[0:midpoint, :] = upper_half_mask

# Convert the image to graycolor and do threshold
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
invert = 255 - thresh

 

 

이렇게 된다.

 

4. OCR로 스크린샷 인식하여 좌표 Return

custom_config = r'--oem 3 --psm 6'
text_data = pytesseract.image_to_data(invert, config=custom_config, lang='eng+kor', output_type=pytesseract.Output.DICT)

 

이 text_data에 인식된 숫자들이 나와있다.

거기에서 목표한 숫자의 좌표를 되돌려주는 함수를 작성하자

def get_ocr_pos(text_data, num):
    target_number = str(num)
    for i, text in enumerate(text_data['text']):
        if text == target_number:
            x = text_data['left'][i] + text_data['width'][i] / 2
            y = text_data['top'][i] + text_data['height'][i] / 2
            print(f"Found {target_number} at position: ({x}, {y})")  

    return (x, y)

 

 

5. 스크린샷과 View-port 사이 scale 계산

스크린샷의 해상도랑 실제 창 해상도 (viewport_size)가 다른 경우가 있다.

이 경우 좌표가 달라지기 때문에 얼마나 차이나는지 계산해야한다.

screenshot_image = Image.open('screenshot.png')
screenshot_size = screenshot_image.size
viewport_size = driver.execute_script("return [window.innerWidth, window.innerHeight];")
scale = screenshot_size[0] / viewport_size[0]

 

실제 좌표는 위에서 구한 x, y를 scale로 나눈 것이 될 것이다.

 

 

6. Javascript code를 이용하여 숫자 클릭

script = """
    var element = document.elementFromPoint(arguments[0], arguments[1]);
    if (element) {
        element.click();
    }
"""
print(f"Scalded position: ({x}, {y})")  
driver.execute_script(script, x, y)

 

이러면 기가막히게 클릭이 된다.

 

 

 

막상 끝나고보면 참 어려울 것 없는 코드인데, 생각대로 안 움직이는 부분들이 많아서 하나하나 해결하는데 엄청난 어려움이 있었다. 이 문제에 봉착한 사람들이 이 글을 통해 엄청난 행복을 느끼길 바란다.