[실습] Selenium 사용자의 브라우저 이벤트 가져오기 #3 [input]

Topic. Selenium 에서 사용자가 브라우저 input 하는 이벤트를 감지하여 기록해보는 방법을 알아봅니다.


1. 개요

클릭이벤트는 클릭 한번이 끝이기에 감지가 쉬웠습니다.

하지만 input 의 입력은 연속적이며 타이핑이 끝나는시점이 명확하지 않습니다.

이를 해결하는 방법을 알아봅시다.


2. 실습

1. Input 생성

우선 input 엘리먼트를 생성하여 페이지에 붙여줍시다.

driver.execute_script("""
    let input1 = document.createElement("input");
    input1.id = 'input1';
    input1.type = 'text';
    input1.placeholder = 'input1';
    document.body.append(input1);
    document.body.append(document.createElement("br"))
    let input2 = document.createElement("input");
    input2.id = 'input2';
    input2.type = 'text';
    input2.placeholder = 'input2';
    document.body.append(input2);
""")

 


2. 이벤트리스너 등록방식 수정

그리고 이벤트리스너 주입 스크립트 안에 콜백함수를 정의하여

window.eventList 배열에 저장하는 방식을 깔끔하게 수정해봅시다.

event_regist_script = """
    function saveScript(e){
        window.eventList.push({
            type : event.type
            ,location : window.location.href
            ,tag : event?.target?.tagName
            ,id : event?.target?.id
            ,value : event?.target?.value
            ,text : event?.target?.innerText
            ,keyCode : event?.keyCode
        })
    }
    if(window.event_listener_flag){
        console.log("event recording...")
    }else{
        window.eventList = []
        document.addEventListener('click', saveScript);
        document.addEventListener("change", saveScript); //추가 
        document.addEventListener("keydown", saveScript); //추가
        document.addEventListener("input", saveScript); //추가
        window.event_listener_flag = 'registed'
        console.log("event registed...")
    }
"""

 

input 엘리먼트의 value 값 변경 이벤트를 감지하기 위해 change, keydown, input 이벤트 리스너가 추가되었습니다.

이렇게 세개의 이벤트를 추가한 이유는 각각의 차이점을 비교해서 사용하기 위함입니다.


3. 전역 EventList 저장용 변수 선언

이제 이 파이썬 변수에 이벤트를 모두 기록할 것입니다.

allEventList = []

 

 

순서를 요약하면

웹브라우저의 이벤트를 자바스크립트 이벤트리스너를 통해 감지하여 window객체를 사용하여 변수에 저장하고

selenium driver 를 통하여 파이썬 변수에 저장하는 순서로 진행될 것입니다.

allEventList 는 반복문 밖에 있고

eventList 는 반복문 안에 있으므로 값을 계속 비워주어야 중복저장되지 않습니다.

while True :
    driver.execute_script(event_regist_script)
    eventList = driver.execute_script("""
        return window.eventList;
    """)
    for event in eventList :
    	print(event)
        allEventList.append(event)
    
    # Javascript eventList 비우기
    driver.execute_script("""
        window.eventList = []
    """)
    
    time.sleep(1)

4. Input 이벤트의 미세한 차이

소스를 위와같이 작성을 마쳤으면 프로그램을 실행하고 input 엘리먼트에 글씨를 입력해봅시다.

아래를 자세히 보면 input, keydown, change 모두 input 테그의 변화를 감지하는 이벤트이지만 각각 감지하는 방식이 조금씩 차이가 있습니다.

 

[한글 테스트]

 

[영어 테스트]

 

* keydown : keyCode 감지 가능, 마지막글자 안찍힘, input창 벗어나면 감지 불가

* input        : keyCode 감지 불가, 마지막글자 찍힘   , input창 벗어나면 감지 불가

* change    : keyCode 감지 불가, 마지막글자 찍힘   , input창을 벗어나면 감지 (Tab Key 로 벗어나기, 다른 영역 클릭 등...)

 

위와같이 각각 이벤트마다 성격이 미세하게 다릅니다.

'마지막글자 안찍힘'은 한글 테스트에서나 영어테스트에서나 [type] keydown 의 마지막을 보면 [type] input 이벤트는 입력과 동시에 이벤트를 감지하지만 [type] keydown 이벤트는 마지막글자를 감지하지 못하는 것을 볼 수 있습니다.

 

이와같은 이유로 이벤트 세개를 모두 잡아야 사용자가 브라우저에서 행하는 모든 액션을 놓치지 않고 잡을 수 있습니다.

 

이제 우리가 원하는 글자는 맨 아래에 감지된 최종 input 엘리먼트의 value 값만 가져오는것이 목표입니다.


5. 파이썬 OrderedDict

파이썬에는 순서가 보장되는 딕셔너리가 있습니다.

딕셔너리는 키와 값으로 이루어진 한 쌍이 하나의 데이터입니다.

 

input 엘리먼트에 글을 연속적으로 입력할 경우

딕셔너리에 엘리먼트의 ID를 키로, 값을 이벤트로 저장하며 같은 input 엘리먼트의 value를 계속 덮어 씌울것입니다.

 

그리고 input 이나 keydown 이벤트가 아닌

다른 click, change 이벤트를 감지하면 딕셔너리에서 꺼내 allEventList 배열에 저장하면 됩니다.

마찬가지로 딕셔너리는 빈값으로 초기화 해주어야 중복저장이 되지 않습니다.

 

소스를 아래와 같이 수정해봅시다.

while True :
    driver.execute_script(event_regist_script)
    eventList = driver.execute_script("""
        return window.eventList;
    """)
    for event in eventList :
        if event['type'] in ['keydown','input'] : //분기
            orderedDict.__setitem__(event['id'],event)
        else:
            for key,val in orderedDict.items():
                allEventList.append(val)
            orderedDict.clear() //딕셔너리 초기화
        if event['type'] in ['click'] : //분기
            allEventList.append(event)
    driver.execute_script("""
        window.eventList = []
    """)
    print(allEventList)
    time.sleep(1)

소스와 같이 브라우저에서 넘어온 이벤트들을 분기할 필요가 있어졌습니다.

click, keydown, input 등 이벤트 타입마다 다르게 동작하도록 프로그램을 작성할 수 있습니다.

[
{'id': 'input1', 'keyCode': None, 'location': 'data:,', 'tag': 'INPUT', 'text': '', 'type': 'click', 'value': ''}
,{'id': 'input1', 'keyCode': 9, 'location': 'data:,', 'tag': 'INPUT', 'text': '', 'type': 'keydown', 'value': '스트라이프'}
]

 

 첫번째 input 엘리먼트에 글씨를 입력하고 탭을 눌러 두번째 input 엘리먼트로 커서를 이동하면

allEventList 변수에 이벤트가 딱 두개만 저장된 것을 볼 수 있습니다.

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유