(완료)1# Python 으로 지출관리 사이트에서 모든지출 데이터 xlsx 파일 뽑아내고 Zapier로 구글시트에 업데이트하기 (Using Python, crawling and exporting company wide expenses data with Xlsx file. Update a Google sheet from this Xlsx file with Zapier.)
우리 회사는 스팬딧이라는 지출관리 프로그램을 쓴다.
회사 전체 지출을 가져오고 실시간 지출 데이터를 통해 예산 확인을 손쉽게 하려는 목적이다.
아이디어 구상은 이렇다.
1. 지출 데이터는 이메일로만 출력할 수 있다. (여러가지가 있지만 나에게 필요한 것은 구글시트에 들어가기 좋은 데이터이므로 xlsx 출력을 하고자함.)
2. 이 지출 데이터를 파이썬으로 크롤링을 하는데 Selenium 으로 로그인하고, 메일로 받는 것 까지는 여기까지 과정에서 완료. 하되 웹훅을 하나 보낸다. 어디로? Zapier로
3. Email parsor by Zapier 를 통해 이메일 본문 안의 URL의 파일을 다운받아서 구글 드라이브에 업로드하는 것
파이썬 모듈 중 핵심은 Selenium 과 requests 이다.
- Selenium 모듈 : 웹 브라우저 자동화 도구로, Chrome 브라우저를 제어하여 Spendit 앱의 사용자 인터페이스를 자동으로 조작하는데 쓰임.
- reuquests 모듈 : 웹훅
일단 코드부터 오픈해보자면.. 아래와 같다.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import datetime
import requests
'''Selenium predefine'''
driver_path = '크롬드라이버의 path'
id_xpath = '//*[@id="email"]'
password_xpath = '//*[@id="password"]'
login_button = '//*[@id="root"]/div/div/div[2]/form/button'
service = Service(driver_path)
options = webdriver.ChromeOptions()
options.add_experimental_option("detach", True)
'''
headless 모드 설정
'''
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument("--start-maximized") # added for prevent error when it runs in headless mode
options.add_argument("--window-size=1920,1080") # added for prevent error when it runs in headless mode
driver = webdriver.Chrome(service=service, options=options)
driver.get("https://app.spendit.kr/sign_in")
id_input = driver.find_element(By.XPATH, id_xpath)
id_input.send_keys('아이디')
password_input = driver.find_element(By.XPATH, password_xpath)
password_input.send_keys('비밀번호')
password_input.send_keys(Keys.RETURN)
# wait until the page is loaded
# wait = WebDriverWait(driver, 30)
# wait.until(EC.visibility_of_element_located((By.CLASS_NAME, 'intercom-1epm6qj.e4nbtsn3')))
time.sleep(10)
# click the expense button
button = driver.find_element(By.XPATH,'//*[@id="expense"]')
button.click()
# wait until the list is loaded - can not find
# wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="filtered-custom"]/div[1]/div/div[3]/div/div[2]/button/span[1]')))
time.sleep(10)
print()
# start date input
button = driver.find_element(By.XPATH,'/html/body/div[1]/div/div[2]/div/aside/div[2]/div[2]/div/div[1]/div/input') #first click
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[1]/button[2]') #year back
button.click()
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[2]/div/div[1]/div[4]') #2023
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[2]/div/div[1]/div[1]') # January
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[2]/div/div/div[2]/div[1]/div[1]') # 1st - active를 제외하는게 핵심
button.click()
time.sleep(10)
# end date input
button = driver.find_element(By.XPATH,'/html/body/div[1]/div/div[2]/div/aside/div[2]/div[3]/div/div[1]/div/input') #first click
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[1]/button[2]') #year back
button.click()
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[2]/div/div[1]/div[4]') #2023
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[2]/div/div[3]/div[4]') # December
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/div/div/div[2]/div/div/div[2]/div[6]/div[1]') # 31st - 다섯번째 일요일이라 배열에서 5번째 = 4번째
button.click()
time.sleep(10)
'''
select filter self-> all members
'''
#
#
button = driver.find_element(By.XPATH,'/html/body/div[1]/div/div[2]/div/aside/div[2]/div[10]/div/button') # self
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/ul[2]/li[2]/ul/li[1]/div') # all members' expense 모든사용자
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/ul[2]/li[3]/button') # confirm
button.click()
time.sleep(10)
'''
select all and download Spendit template
'''
button = driver.find_element(By.XPATH,'/html/body/div[1]/div/div[2]/div/section/div[3]/div/div/table/thead/tr/th[1]/span/input') # checkbox
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/ul/li[2]/button') # all expenses
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[1]/div/div[2]/div/section/div[1]/div/div[1]/div/button[1]/span') # download button
button.click()
button = driver.find_element(By.XPATH,'/html/body/div[2]/div[2]/ul/li[2]/ul/li/span') # Spendit Template
button.click()
time.sleep(1)
driver.quit()
# Zapier 연동을 위한 웹훅 보내기
now = datetime.datetime.now()
year = str(now.year)
month = str(now.month).zfill(2)
day = str(now.day).zfill(2)
hour = str(now.hour).zfill(2)
minute = str(now.minute).zfill(2)
second = str(now.second).zfill(2)
# making a webhook key
time_sent = year + month + day + hour + minute + second
data = "{sent_time : " + time_sent + "}"
# for debugging
print(time_sent)
# URL setting
webhookURL = "https://hooks.zapier.com/hooks/catch/8712349/3oxfgkw/"
# POST request
response = requests.post(webhookURL, data=data)
# response code confirmation
if response.status_code == 200:
print("Data transferred successfully")
else:
print("Data transfer failed")
코드는 대충 모두 button 이라는 함수에 내가 웹상에서 클릭을 원하는 것을 선택하게한다. 그리고 선택한 것을 button.click() 명령으로 클릭하도록 구성되어있다.
크롬에서 F12를 누르면 개발자 메뉴가 우측에 나타나는데 이 때, 위사진에서 네모모양에 커서가 있는 버튼을 누르면 원하는 것을 선택했을 때 어떤 코드로 구성된 것을 누르는지 확인이 가능하다.
일단 나는 웹훅을 사용해서 Zapier에 신호를 주는 걸로 코드를 마무리했다.
댓글
댓글 쓰기