과도한 매크로 사용은 서버에 큰 부담이 될 수 있으므로 반복적인 작업을 자동화 하거나 테스트 용도로 한정해서 사용하시기 바랍니다. 매크로 사용 시 발생하는 모든 문제에 대한 책임은 전적으로 매크로 사용자 본인에게 있습니다.
마지막으로 주문서 작성을 해 보겠습니다. 이번엔 입력할 게 꽤 많아 보이지만 잘 분석해 보면 지금 껏 해 왔던 내용의 반복에 지나지 않습니다. 또 이후 과정이나 다른 쇼핑몰을 살펴봐도 결국 지금까지 해왔던 내용을 조금만 응용해 보면 다 할 수 있고, 쇼핑몰들 구성이 거의 비슷하기 때문에 어렵지 않습니다. 심지어는 여러 웹사이트를 같은 데서 만든 흔적도 보이는데 소스가 비슷하거나 XPATH로 찾아야 할 태그 이름마저 서로 동일한 곳도 있습니다.
가장 처음 우리를 겁먹게 만드는 건 배송정보입력으로 이름, 연락처, 주소, 주문메세지까지 입력할 칸이 너무 많습니다만, 시나리오 짤 때도 경험했다시피 “위 정보와 같음”, “자택”에 체크하면 회원정보가 알아서 입력됩니다. 따라서 딱 두 줄만 짜면 되겠네요.
이를 위해 미리 회원 정보에 전화번호/주소 등을 정확히 입력해 둘 필요가 있습니다. 다른 시나리오에서 응용할 때도 미리 할 수 있는 건 해 놓으면 불 필요한 코드를 많이 줄일 수 있습니다. 특히 ActiveX 설치 팝업이 뜨면 망하는 경우도 있으니 미리 다 설치 해 두세요.
“위 정보와 같음” 그리고 “자택”을 각각 인스펙터로 살펴보겠습니다.
딱 봐도 간단히 될 거 같네요.
samecheck = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='checkbox'][@name='same']"))).click() homeradio = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='radio'][@name='place'][@value='H']"))).click()
이 번엔 팝업을 닫아 보도록 하겠습니다. 이 팝업은 굳이 닫지 않아도 주문하는데 아무 지장이 없지만, 이런 경우가 왕왕 있기도 하고 간혹 필수로 처리해야 되는 경우도 있기 때문에 윈도우 다루는 법을 익혀 둘 필요가 있습니다.
def closePopups(driver, default_window): handles = list(driver.window_handles) if (len(handles) > 1): for handle in handles: if (handle != default_handle): driver.switch_to_window(handle) driver.close() handles.remove(handle) driver.switch_to_window(default_handle)
새로운 함수를 만들었습니다. 이 closePopups() 함수는 현재 webdriver가 갖고 있는 윈도우를 확인한 후 여러 개일 경우 기본 윈도우를 제외한 나머지를 다 닫는 역할을 합니다. 이 함수를 order() 함수에서 불러주면 될 거 같습니다. default_window라고 새로운 변수가 생겼는데 이건 기본 윈도우인지를 확인하기 위해 추가한 겁니다. 아래 처럼 팝업이 뜨기 전에 현재 윈도우 핸들 값을 갖고 있다가 나중에 비교하는데 사용하면 됩니다.
default_handle = driver.current_window_handle order(driver, default_handle)
추가로 이 시나리오에선 사용하지 않지만, 가끔 자바 스크립트로 된 팝업을 띄우는 곳도 있습니다.
이런 류의 팝업을 없애려면 아래처럼 하면 되고, 확인이 아닌 취소 버튼을 누르려면 alert.accept() 대신 alert.dismiss() 로 넣어주면 됩니다.
WebDriverWait(driver, 5).until(EC.alert_is_present()) alert = driver.switch_to_alert() alert.accept()
이번엔 인터넷 쇼핑에서 가장 중요한 적립금을 써 보도록 하겠습니다. 우선 적립금을 쓰기 위한 로직을 정리해 보면 오른쪽의 사용가능 적립금이 얼마인지를 확인한 다음 100원 이상이면 100원 단위로 잘라서 왼쪽의 사용할 적립금 부분에 입력하는 방식이네요.
역시 인스펙터로 두 적립금 박스를 살펴 보겠습니다.
하나씩 살펴보니 역시 어렵지 않게 처리할 수 있을 것 같습니다. 늘 하던대로 XPATH로 찾고 value 값을 get_attribute()로 가져온 후 그 중 얼마를 써야하는지 계산하고 왼쪽 박스에 send_keys()로 보내면 딱 되겠네요. 하면 이렇게 됩니다.
point = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='text'][@name='okreserve']"))) mypoint = int(point.get_attribute("value")) print "point: %d" % mypoint if (mypoint >= 100): pointinput = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='text'][@name='usereserve']"))) usepoint = mypoint / 100 * 100 pointinput.send_keys(usepoint) print "use point: %d" % usepoint
다시 스크립트를 실행해 보면 포인트 계산도 잘 되고 파워쉘에 로그도 잘 찍히고 있네요.
이상으로 python + selenium 을 이용한 매크로 작성법 포스팅을 마칩니다. 지금까지 나왔던 내용을 잘 응용하고, 구글링을 동반하면 새로운 시나리오에 대해서도 별 무리없이 작성이 가능할 겁니다. 다시 한 번 강조하지만 매크로 사용에 각별히 주의하시고 되팔렘 물건은 사지도 팔지도 맙시다. 끝으로 지금까지 작성한 전체 소스입니다.
https://github.com/genonfire/macpro
#-*- 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" PS4PRO = "http://gamewoori.com/shop/shopdetail.html?branduid=885806&xcode=009&mcode=001&scode=&type=X&search=&sort=regdate" ITEMURL = PS4PRO USERID = "" PASSWORD = "" 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, 5).until(EC.presence_of_element_located((By.XPATH, "//a[@href='/html/mainm.html?type=logout ']"))) def checkStock(driver, i): mktotal = driver.find_element_by_xpath("//strong[@id='MK_p_total']") totalPrice = mktotal.get_attribute("innerHTML") if (totalPrice == "0"): print('[%d]%s' % (i, 'out of stock')) return True else: print "Go! Checkout~" return False def checkout(driver): driver.execute_script("JavaScript:send_multi('', 'baro', '');") def closePopups(driver, default_window): handles = list(driver.window_handles) if (len(handles) > 1): for handle in handles: if (handle != default_handle): driver.switch_to_window(handle) driver.close() handles.remove(handle) driver.switch_to_window(default_handle) def order(driver, default_handle): samecheck = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='checkbox'][@name='same']"))).click() homeradio = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='radio'][@name='place'][@value='H']"))).click() closePopups(driver, default_handle) point = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='text'][@name='okreserve']"))) mypoint = int(point.get_attribute("value")) print "point: %d" % mypoint if (mypoint >= 100): pointinput = WebDriverWait(driver, WAITTIME).until(EC.presence_of_element_located((By.XPATH, "//input[@type='text'][@name='usereserve']"))) usepoint = mypoint / 100 * 100 pointinput.send_keys(usepoint) print "use point: %d" % usepoint 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) default_handle = driver.current_window_handle order(driver, default_handle)
혹시 셀레니움으로 적립금 말고 카드결제도 구현이 가능한지 궁금합니다.