Selenium을 활용한 웹 요청, 응답 및 WebSocket 데이터 캡처

Selenium으로 웹 또는 ElectronJS/Cef 기반 애플리케이션을 테스트할 때, 특정 요소 조작 후 발생하는 네트워크 요청과 그 파라미터가 올바른지 확인해야 하는 경우가 있습니다. 이러한 필요성을 충족하기 위해 Chrome의 성능 로그를 활성화하고 driver.get_log("performance")를 사용하여 요청 정보를 확인할 수 있습니다. 이 과정에서 네트워크 관련 로그를 필터링하고, 필요한 경우 Chrome DevTools Protocol(CDP) 명령을 사용하여 상세한 요청 및 응답 본문 데이터를 추출할 수 있습니다.

Chrome 성능 로그 활성화 및 기본 정보 추출

Selenium에서 Chrome의 네트워크 성능 로그를 수집하려면, 드라이버를 초기화할 때 goog:loggingPrefs 옵션을 설정해야 합니다. 'performance' 로깅을 'ALL'로 설정하면 모든 성능 관련 이벤트가 기록됩니다. 다음은 헤드리스 모드에서 Chrome을 실행하고 기본적인 네트워크 로그를 가져오는 예제 코드입니다.

import json
from pprint import pprint
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# Chrome 옵션 설정 객체 생성
chrome_options = Options()
# 성능 로그를 기록하도록 'performance' 로깅 수준을 'ALL'로 설정
chrome_options.set_capability('goog:loggingPrefs', {'performance': 'ALL'})
# (선택 사항) 브라우저 UI 없이 실행하는 헤드리스 모드 활성화
chrome_options.add_argument("--headless")

# 설정된 옵션으로 Chrome 드라이버 초기화
browser = webdriver.Chrome(options=chrome_options)

# 타겟 URL로 이동
target_url = 'https://httpbin.org/get'
browser.get(target_url)

# 'performance' 로그 타입의 모든 로그 항목 가져오기
performance_logs = browser.get_log("performance")

print(f"총 성능 로그 항목 수: {len(performance_logs)}\n")

# 각 로그 항목을 반복하며 파싱 및 출력
for log_entry in performance_logs:
    # 로그 메시지는 JSON 문자열 형태이므로 파싱
    parsed_message = json.loads(log_entry["message"])["message"]

    # Network 관련 이벤트(요청, 응답, WebSocket) 필터링
    if "Network.response" in parsed_message["method"] or \
       "Network.request" in parsed_message["method"] or \
       "Network.webSocket" in parsed_message["method"]:
        print("--- 네트워크 관련 이벤트 발견 ---")
        pprint(parsed_message)
        print("--------------------------------\n")

# 드라이버 종료
browser.quit()

위 코드를 실행하면 Network.requestWillBeSent, Network.responseReceived, Network.webSocketCreated 등 다양한 네트워크 관련 이벤트를 콘솔에서 확인할 수 있습니다. 이 로그에는 요청 URL, 응답 상태 코드, 헤더 정보 등이 포함되지만, 요청 본문(POST 데이터)이나 응답 본문의 상세 내용은 기본적으로 포함되지 않습니다.

Chrome DevTools Protocol(CDP)을 이용한 상세 정보 추출

네트워크 요청의 본문 데이터나 응답의 전체 내용을 파악하려면 Chrome DevTools Protocol(CDP) 명령을 직접 사용해야 합니다. Selenium은 execute_cdp_cmd 메서드를 통해 CDP와 상호 작용할 수 있습니다. 특히 Network.getRequestPostData 명령으로 요청 본문을, Network.getResponseBody 명령으로 응답 본문을 가져올 수 있습니다.

import json
from pprint import pprint
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import WebDriverException

# Chrome 옵션 설정
chrome_options = Options()
chrome_options.add_argument("--headless") # 헤드리스 모드
chrome_options.set_capability('goog:loggingPrefs', {'performance': 'ALL'}) # 성능 로그 활성화

# 드라이버 초기화
browser = webdriver.Chrome(options=chrome_options)

target_url = 'https://httpbin.org/get'
browser.get(target_url)

# 모든 성능 로그를 가져옴
all_performance_logs = browser.get_log("performance")

print(f"총 성능 로그 항목 수: {len(all_performance_logs)}\n")

# Network.responseReceived 이벤트를 찾아 상세 정보 추출
for log_entry in all_performance_logs:
    parsed_message = json.loads(log_entry["message"])["message"]

    if parsed_message["method"] == 'Network.responseReceived':
        response_params = parsed_message['params']
        request_url = response_params['response']['url']

        # 'data:,' 형태의 초기 페이지 요청은 일반적으로 무시합니다.
        if request_url.startswith('data:'):
            continue

        print(f'--- 요청/응답 정보 ---')
        print(f'처리 중인 URL: {request_url}')

        request_identifier = response_params['requestId']
        status_code = response_params['response']['status']
        mime_type = response_params['response'].get('mimeType', '알 수 없음')

        print(f'  상태 코드: {status_code}')
        print(f'  MIME 타입: {mime_type}')

        # 요청 본문 데이터 가져오기 (주로 POST 요청에 해당)
        try:
            # Network.getRequestPostData CDP 명령을 사용하여 요청 본문 가져오기
            # GET 요청의 경우 POST 데이터가 없으므로 WebDriverException이 발생할 수 있음
            request_payload = browser.execute_cdp_cmd('Network.getRequestPostData', {'requestId': request_identifier})
            if request_payload and 'postData' in request_payload:
                print(f'  요청 본문: {request_payload["postData"]}')
        except WebDriverException:
            # POST 데이터가 없는 경우 예외 무시
            pass
        except Exception as e:
            print(f"  요청 본문 가져오기 실패: {e}")

        # 응답 본문 데이터 가져오기
        try:
            # Network.getResponseBody CDP 명령을 사용하여 응답 본문 가져오기
            response_body = browser.execute_cdp_cmd('Network.getResponseBody', {'requestId': request_identifier})
            if response_body and 'body' in response_body:
                # 너무 긴 응답 본문은 일부만 출력
                print(f'  응답 본문: {response_body["body"][:500]}...')
        except Exception as e:
            print(f"  응답 본문 가져오기 실패: {e}")
        print(f'----------------------\n')

browser.quit()

이 코드를 실행하면 httpbin.org/get 요청에 대한 응답 본문이 출력되는 것을 확인할 수 있습니다. 이는 웹 테스트 시 특정 요청의 데이터 유효성을 검사하거나, 애플리케이션의 동작을 디버깅하는 데 매우 유용하게 활용될 수 있습니다.

태그: selenium python Chrome DevTools Protocol 네트워크 요청 웹 자동화

8월 3일 08:10에 게시됨