gencode
2924 0
PS4 프로 자동 구입 매크로 만들기 -3-
2017-01-13 19:50:38 - genonfire
2017-12-08 19:57:55
과도한 매크로 사용은 서버에 큰 부담이 될 수 있으므로 반복적인 작업을 자동화 하거나 테스트 용도로 한정해서 사용하시기 바랍니다. 매크로 사용 시 발생하는 모든 문제에 대한 책임은 전적으로 매크로 사용자 본인에게 있습니다.


지난 포스팅의 최종 소스를 실행해 보면 로그인 없이 바로 아이템 페이지로 넘어갈 겁니다. login() 함수에서 아무것도 안하기 때문인데, 로그인 여부에 상관 없이 아이템 페이지로 이동하고 있죠. 일단 자동로그인을 만들기 위해 아이템 페이지로 이동하는 걸 잠시 막아둡시다.  아래처럼 앞에 # 를 붙이면 주석처리 되어 실행되지 않습니다.


    driver.get(HOMEPAGE)
    login(driver)
    # driver.get(ITEMURL)


이 상태에서 다시 실행해 보면 로그인 페이지에서 딱 멈춰 있게 됩니다. 파워쉘에 글자 뜨는 건 지금은 무시하세요, 나중에 다 지울 겁니다.


자동 로그인 만들기


앞서 시나리오 분석할 때 알아봤듯이 로그인은 아이디/패스워드 입력 후 로그인 버튼을 누르는 3단계로 이루어집니다. 즉, 코드 3줄만 짜면 됩니다.


우선 아이디부터 입력해 보죠.

아래 그림처럼 아이디 입력하는 부분에 마우스 갖다 대고 오른쪽 버튼을 누르면 나오는 ‘요소 검사(Q)’를 실행해 줍니다.




아래쪽에 도킹된 창에 선택된 객체의 HTML 소스가 하이라이트 되어 나타납니다. 이 소스를 참고해서 셀레늄으로 하여금 해당 객체를 찾고 거기에 키를 보내 아이디를 입력할 수 있게 됩니다.


def login(driver):
    WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//td/input[@name='id']"))).send_keys(USERID)


여기선 (By.XPATH, “//td/input[@name=’id’]”) 에만 집중하면 됩니다. 이게 바로 셀레늄으로 하여금 지정된 객체를 찾도록 만들어주는 문구입니다. XPATH 외에도 다른 방법이 많은데 XPATH 가 다른 방법을 다 포함한다고 보면 되므로 XPATH만 사용해도 됩니다. 그 외 다른 방법들이 궁금하거나 XPATH에 대해 자세히 알고 싶다면 아래 문서들을 참고하세요.

http://selenium-python.readthedocs.io/api.html#locate-elements-by

https://www.w3.org/TR/xpath20/


XPATH


XPATH는 중요하므로 조금만 더 설명을 하겠습니다.

XPATH로 위에 하이라이트 되어 있는 소스의 <input ~ name=”id”>를 선택하려면 “//input[@name=’id’]” 가 됩니다. 만약 name이 id가 아니라 password 였다면 “//input[@name=’password’]”가 되겠죠.

또한 input이 아니라 <img ~ src=”a.gif”> 라면 “//img[@src=’a.gif’]”라고 하면 됩니다.

즉, // 로 시작해서 찾으려는 태그 이름을 적어주고 [] 안에 추가로 찾을 해당 태그의 속성을 적어주면 됩니다. 속성은 여러개를 넣을 수 있는데 <input name=”id”, id=”id”, class=”button”> 을 찾고 싶으면 “//input[@name=’id’][@id=’id’][@class=’button’]” 이라고 하면 됩니다.


주의할 점이 이 방법은 조건에 해당 하는 모든 객체를 찾으므로 결과가 한 개가 아닐 수도 있습니다. 아래와 같은 태그들이 있다고 합시다.

1. <input class=”button” name=”id” type=”text” />

2. <input class=”button” name=”password” type=”text” />

3. <input name=”id” type=”text” />


여기서 “//input” 을 찾으면 3개가 다 나오고,

“//input[@name=’id’]” 는 1, 3 번

“//input[@class=’button’]” 은 1, 2 번

“//input[@name=’id’][@class=’button’]” 을 해야 1번만 나오게 됩니다.


아래와 같은 예도 살펴봅시다.

1. <td><input name=”id”></td>

2. <form><input name=”id”></form>


이 경우 “//input[@name=’id’]”로 찾게 되면 1, 2번이 모두 나옵니다. 이 때 1번만 찾고 싶으면 “//td/input[@name=’id’]”와 같이 하면 됩니다. 즉 ‘/’ 는 계층구조를 나타냅니다. 이처럼 찾으려는 객체 주변의 태그들의 구조와 각 태그의 속성을 활용해 찾으면 됩니다.


페이지 소스보기


이처럼 단 한개의 결과만 나오도록 XPATH 조건을 주는 것이 중요합니다. 이를 위해 요소 검사하기 뿐 아니라 페이지 소스 전체를 검색해서 같은 조건이 존재하는지를 파악하고 유니크한 결과가 나오도록 조건을 수정해 줘야 합니다.

그럼 “//input[@name=’id’]” 를 전체 소스에서 검색해 봅시다. 화면 아무 곳에서 마우스 오른쪽 버튼 클릭 후 ‘페이지 소스(V)’ 를 선택하면 창이 하나 뜨면서 페이지 전체 소스가 나옵니다.




찾기(Ctrl + F)로 ‘name=id’를 찾아 봅시다.

두 개가 나오는데 하나는 우리가 찾아야 할 아이디 입력하는 부분이고, 또 하나는 뭔진 모르겠지만 암튼 아닙니다. 이 두 개를 구분하기 위해 태그의 계층 구조를 이용해 봅시다.

먼저 걸러내야 할 놈을 보니 <form><input name=id> 구조입니다.




다음으로 찾아야 할 놈의 계층 구조는 아래와 같네요.




이건 <td><table><tbody><tr><td><input> 구조네요. 실제론 멀리 갈 필요 없이 <td><input> 만 이용해도 충분히 구분이 될 것 같습니다. 따라서 “//td/input[@name=’id’]” 와 같은 XPATH 구문을 쓰게 되면 우리가 원하는 아이디 입력을 할 수 있습니다.


키 입력하기


def login(driver):
    WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//td/input[@name='id']"))).send_keys(USERID)


아까 소스를 다시 보면 맨 뒤에 .send_keys(USERID) 가 보입니다. 이건 XPATH로 찾은 객체에 USERID란 키를 입력하라는 겁니다. USERID 부분에 본인의 아이디를 입력하면 됩니다.

동일한 방법으로 패스워드 입력하는 기능도 넣어 봅시다. (PASSWORD는 본인의 패스워드로 대체)


def login(driver):
    WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//td/input[@name='id']"))).send_keys(USERID)
    driver.find_element_by_xpath("//td/input[@name='passwd']").send_keys(PASSWORD)


이 두 줄 모두 동일한 XPATH로 검색해서 나온 객체에 키를 입력하는 기능을 합니다. 다만 이름에서도 알 수 있듯이 앞의 것은 해당 결과가 페이지에 없을 때 5초간 기다리지만 뒤의 건 없으면 바로 에러가 발생합니다. 이 함수는 로그인 페이지를 열자마자 실행되기 때문에 미처 객체가 로딩되지 않을 수도 있어 약간의 여유를 둔 겁니다. 상황에 따라 두 가지 방법을 잘 조합해서 사용하시면 됩니다.


여기까지 한 후 다시 스크립트를 실행해 보면 아이디/패스워드가 잘 입력되고 있는 걸 확인할 수 있네요.




스크립트 실행


마지막으로 로그인을 진행시켜 봅시다. 마찬가지로 요소 검사 기능을 이용해 소스가 어떻게 생겼는지 살펴봅시다.




이 경우 계속 해 오던대로 img 객체를 찾아 마우스 클릭을 날려주는 방법도 있지만 이건 다음에 해보기로 하고 지금은 스크립트를 바로 실행하는 방법을 써 볼 겁니다. 다 아시겠지만 위 소스는 Login 이미지를 누르면 “JavaScript:check()”라는 자바스크립트가 실행되는 구조입니다. 즉, JavaScript:check()를 실행하면 로그인이 진행된다는 뜻이죠. 아래처럼 스크립트를 바로 실행할 수 있습니다.


    driver.execute_script("JavaScript:check()")


이렇게 한 후 다시 스크립트를 실행하면 자동 로그인이 되는 걸 확인할 수 있습니다. 아래처럼 단 3줄로 자동 로그인을 구현했습니다.


def login(driver):
    WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//td/input[@name='id']"))).send_keys(USERID)
    driver.find_element_by_xpath("//td/input[@name='passwd']").send_keys(PASSWORD)
    driver.execute_script("JavaScript:check()")


로그인 확인


이제 처음에 잠시 주석 처리했던 # driver.get(ITEMURL) 을 다시 살려서 로그인 후 아이템 페이지로 이동하는지 테스트 해 봅니다. 아마도 아이템 페이지로 이동은 되는데 로그인이 될 때도 있고 안될 때도 있을 겁니다.




이는 로그인 과정이 끝나기 전에, 즉 로그인 과정이 이루어지는 중간에 다음 페이지로 이동했기 때문에 로그인이 취소된 것으로 추정됩니다. 따라서 우리는 아이템 페이지로 이동하기 전에 로그인이 완료되었는지 확인하는 과정을 추가로 넣어줘야 합니다.


로그인을 했을 경우엔 페이지 상단의 로그인 글자가 로그아웃으로 바뀌는 것에 착안해 로그아웃이 나올 때 까지 기다리는 코드를 넣어 보겠습니다. 역시 요소 검사를 이용해서 방법을 찾아보겠습니다.




하이라이트된 img 를 이용할 수도 있지만 주소가 좀 야리꾸리하니 상단의 <a href=”/html/mainm.html?type=logout “>을 이용하도록 하겠습니다. 찾을 때 까지 기다리는 건 아까 아이디 찾을 때 써 봤죠? 그대로 하면 됩니다.


    WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//a[@href='/html/mainm.html?type=logout ']")))


자, 다시 한 번 실행해 보면 정상적으로 로그인 된 후 아이템 페이지로 이동합니다. 여기까지의 전체 소스입니다.


#-*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

HOMEPAGE = "http://gamewoori.com/shop/member.html?type=login"
ITEMURL = "http://gamewoori.com/shop/shopdetail.html?branduid=885806&xcode=009&mcode=001&scode=&type=X&search=&sort=regdate"

USERID = "jackbauer"
PASSWORD = "12345678"

WAITTIME = 1000
SLEEPSEC = 5
LOOP = 3 # -1 for infinite

def login(driver):
    WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//td/input[@name='id']"))).send_keys(USERID)
    driver.find_element_by_xpath("//td/input[@name='passwd']").send_keys(PASSWORD)
    driver.execute_script("JavaScript:check()")

    WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//a[@href='/html/mainm.html?type=logout ']")))

def checkStock(driver, i):
    print '재고 확인 함수'

def checkout(driver):
    print '구매하기 누르는 함수'

def order(driver):
    print '주문서 작성 함수'

if __name__ == "__main__":
    driver = webdriver.Firefox()
    driver.wait = WebDriverWait(driver, 2)

    driver.get(HOMEPAGE)

    login(driver)
    driver.get(ITEMURL)

    i = 1
    while(checkStock(driver, i)):
        i += 1
        time.sleep(SLEEPSEC)
        if (i > LOOP and LOOP != -1):
            break
        driver.get(ITEMURL)

    checkout(driver)

    order(driver)


다음 포스팅에선 재고 확인과 주문 넣는 과정을 해 보겠습니다.

이 글이 마음에 드셨다면.. 1
gencode 의 다른 포스트
네이버 사전에 네이버 번역 API 연동하기 (파파고)
PS4 프로 자동 구입 매크로 만들기 -5-
PS4 프로 자동 구입 매크로 만들기 -4-
> PS4 프로 자동 구입 매크로 만들기 -3-
PS4 프로 자동 구입 매크로 만들기 -2-
PS4 프로 자동 구입 매크로 만들기 -1-
블루호스트 사용기
댓글 [ 0 ]