Python/2.7 for fun.

selenium cron to text

qkqhxla1 2019. 12. 5. 13:55

cron포맷을 영어 텍스트로 변환해야 할 일이 생겼다.


그러니까 예로 * * * * * 같은 경우 every minute 같은거로. 구글링을 하니 아예 파이썬 전용 pip라이브러리가 있다.


한 3개정도 찾았는데 ex) https://pypi.org/project/cron-descriptor/


크론 구문이 복잡해지면 얘가 잘 파싱하지를 못한다.(얘말고도 다른것들도 한가지씩 문제가 있었다.)


문제되는 케이스 예시.(에러 발생)

# -*- coding: utf-8 -*-

from cron_descriptor import get_description, ExpressionDescriptor
print(get_description("3 0-3,7-23/3 * * *"))

실제로 크론탭 작업시에는 https://crontab.guru/ 요 사이트를 참고해서 쓴다. 그리고 요 사이트는 어떤 크론 구문도 다 잘 영어 텍스트로 바꿔준다.(에러나는걸 한번도 못봄.)


그래서 라이브러리를 쓰는대신 crontab guru를 파싱해서 쓰도록 하려고 했다. 사이트를 분석해보니 매번 어디로 요청을 날리거나 하는게 아니라 내부에 있는 index.js에서 만들어지는 복잡한 로직을 통하는데... 분석하기가 너무 까다롭다.

크롬 개발자도구의 js beautifuler를 이용해서 보기 좋게 변환했는데도 분석이 진짜 힘들다..


분석하다 포기하고 그냥 selenium을 이용해서 저 자바스크립트 함수 리스트를 내 selenium에 로딩한 다음 호출하기로 생각했다.


크론탭 여러개를 텍스트로 변환할때 한개한개마다 크롬을 띄우고 끝나면 죽이고, 하는 방식은 cron guru에 요청도 많이 날리게 되고 리소스 낭비도 심할것같아서 한번만 띄우고 이미 있는 엔진을 돌려쓰는 방식으로 만들었다.


나중에 크롬 뜨는거 보이기 싫으면 phantomjs로 바꿔주면 될것같다.

# -*- coding: utf-8 -*-

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait


class SeleniumManager:
    BACKSPACE_KEY = u'\ue003'

    def __init__(self, browser_path):
        self.browser = webdriver.Chrome(browser_path)
        self.browser.get('https://crontab.guru')
        self.action = ActionChains(self.browser)
        self.cron_dict = {}

    def __del__(self):
        self.browser.quit()

    def remove_input_text(self):
        self.browser.execute_script("document.getElementById('input').value=''")

    def input_crontab(self, cron_text):
        self.browser.find_element_by_css_selector('input[id="input"]').send_keys(cron_text)

    def wait_until_browser_load(self, text):
        WebDriverWait(self.browser, timeout=3).until(
            lambda browser: browser.find_element_by_css_selector('div[class="human-readable"]').text[1:-1] != text
        )

    def get_text(self):
        return self.browser.find_element_by_css_selector('div[class="human-readable"]').text[1:-1]

    def get_cron_to_text(self, crontext):
        if crontext in self.cron_dict:  # 이미 한번 한적 있음 그거 갖다씀.
            return self.cron_dict[crontext]
        text = self.get_text()  # 현재 기본으로 떠있는 텍스트를 가져온다. 
        self.remove_input_text()  # 입력 칸을 비운다.
        self.input_crontab(crontext)  # 입력 칸에 크론 텍스트를 입력한다.
        self.wait_until_browser_load(text)  # 자바스크립트 엔진이 잘 실행될때까지 기다린다. 실행되었다의 기준은 이전 텍스트와 현재 텍스트가 달라졌느냐로 판단했다.
        text = self.get_text()
        self.cron_dict[crontext] = text
        return text


def main():
    selenium = SeleniumManager(browser_path='/Users/qkqhxla1/Downloads/chromedriver')
    print selenium.get_cron_to_text('* * 3,2,5 * 5')
    print selenium.get_cron_to_text('* * 3,5 * 2')
    print selenium.get_cron_to_text('* * 3,5 * 2')
    print selenium.get_cron_to_text('5 0 * 8 *')
    print selenium.get_cron_to_text('15 14 1 * *')
    print selenium.get_cron_to_text('*/2 * 3,5 * 2')


main()