본문 바로가기

Develop/Back-End

Selenium 을 이용하여 특정 URL 요청과 응답 값 모니터링 하는 기능 만들기

이번에 내가 맡게 된 업무는 셀레니움과 jpa를 이용하여

특정 url 요청이 있는 지 확인하고

그 요청으로 인한 응답 값을 모니터링 및 데이터를 적재하는 업무다 😂

 

사실 이번에 셀레니움을 이용하여 간단하게 개발을 하였지만

아쉬움이 너무 많이 남았다. 

 

셀레니움으로 chrome inspect device를 충분히 조작할 수 있을 것이라고 생각했지만..

내가 잘 모르는건지 내 생각과 달리 디바이스의 dom이나 script 를 조작하지 못했다.. 😥

 

그렇지만, 모바일 환경에 테스트 자동 도구에는 어떤 것들이 있는지 알아보게 되었고

다음 같은 상황이 생겼을 때는 더 빠르게 기술을 결정 할 수 있을 것 같다. 🥱

( ex  appium )

 

* 셀레니움(셀레늄) :  웹 애플리케이션 자동화 및 테스트를 위한 포터블 프레임워크이다. 셀레늄은 테스트 스크립트 언어를 학습할 필요 없이 기능 테스트를 만들기 위한 플레이백 도구를 제공한다. (셀례늄 IDE) C 샤프, 그루비, 자바, 펄, PHP, 파이썬, 루비, 스칼라 등 수많은 유명 프로그래밍 언어들에서 테스트를 작성하기 위한 테스트 도메인 특화 언어(Selenese)를 제공한다. 이 테스트들은 현대의 대부분의 웹 브라우저에서 수행이 가능하다. 셀레늄은 윈도우, 리눅스, macOS 플랫폼에서 디플로이된다. 아파치 2.0 라이선스로 배포되는 오픈 소스 소프트웨어이다. 웹 개발자는 무료로 다운로드, 사용할 수 있다.

 


진행상황

모니터링 하기 위한 어느정도 구색은 갖추었고

jpa를 이용하여 간단하게 데이터베이스에 저장한다.


이슈 & 고민 및 해결

 

1. 환경에 설치 된 크롬 버전 문제 

 => webdrivermanager 해결 (주소 : https://github.com/bonigarcia/webdrivermanager)

 이걸 이용하면 WebDriverManager.chromedriver().setup(); 코드 한줄로 해결 할 수 있다. 

 번거롭게 경로 지정 , 웹드라이버 설치도 필요 없다 👍

2. 셀레니움으로 브라우저 403 , 500 오류 날 시

=> 크롬 옵션에 --remote-allow-origins=* 있는데도 불구하고 계속 오류가 난다면 

https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-http-jdk-client

추가해보자 이게 설명이 없어서 정확한 용도는 모르겠으나 

자바(jdk) 11+ 이상 일 시 사용을 권장하니 오류가 계속 발생 할 시 의존성 추가를 권장한다.😴

아마 jdk1.8 보다 상위 버전에서 발생하는 오류인 것 같은데 이 라이브러리로 해결 할 수 있다고 한다
https://www.selenium.dev/blog/2022/using-java11-httpclient/

 

나는 해당 문제를 최대한 피하고자 진행 당 시 모두 최신버전을 이용하였다.

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.1.3'
	id 'io.spring.dependency-management' version '1.1.3'
}

group = 'com.acp'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '4.12.1'
	implementation group: 'io.github.bonigarcia', name: 'webdrivermanager', version: '5.5.3'
	implementation 'org.seleniumhq.selenium:selenium-http-jdk-client:4.9.1'
	implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.15.2'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.batch:spring-batch-test'
}

tasks.named('test') {
	useJUnitPlatform()
}



참고 레퍼런스

https://ko.wikipedia.org/wiki/%EC%85%80%EB%A0%88%EB%8A%84_(%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4) 

셀레니움 위키백과 

 

셀레늄 (소프트웨어) - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 셀레늄(Selenium)은 웹 애플리케이션 자동화 및 테스트를 위한 포터블 프레임워크이다. 셀레늄은 테스트 스크립트 언어를 학습할 필요 없이 기능 테스트를 만들

ko.wikipedia.org

 

https://www.selenium.dev/

셀레니움 Dev 홈페이지

 

Selenium

Selenium automates browsers. That's it!

www.selenium.dev

https://github.com/SeleniumHQ/selenium

셀레니움 Github

 

GitHub - SeleniumHQ/selenium: A browser automation framework and ecosystem.

A browser automation framework and ecosystem. Contribute to SeleniumHQ/selenium development by creating an account on GitHub.

github.com

https://chromedriver.chromium.org/downloads

크롬 웹 드라이버 다운로드 

 

ChromeDriver - WebDriver for Chrome - Downloads

Current Releases If you are using Chrome version 115 or newer, please consult the Chrome for Testing availability dashboard. This page provides convenient JSON endpoints for specific ChromeDriver version downloading. For older versions of Chrome, please se

chromedriver.chromium.org


TO DO List

1. 모바일 환경 테스트 & 배치 또는 스케줄러

2. appium 검토해보기

3. 지금은 스프링 스케줄 이용하고 있지만  스프링 배치로 마이그레이션 해보기 ( 시간이 남을 경우 😛)

 


Example Code

위키백과 예제코드 사용언어 Java

import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import static org.openqa.selenium.support.ui.ExpectedConditions.presenceOfElementLocated;
import java.time.Duration;

public class HelloSelenium {

    public static void main(String[] args) {
        WebDriver driver = new FirefoxDriver();
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        try {
            driver.get("https://google.com/ncr");
            driver.findElement(By.name("q")).sendKeys("cheese" + Keys.ENTER);
            WebElement firstResult = wait.until(presenceOfElementLocated(By.cssSelector("h3")));
            System.out.println(firstResult.getAttribute("textContent"));
        } finally {
            driver.quit();
        }
    }

특정 URL 요청이 있는지 확인하고

그 요청이 있다면 응답데이터 확인 후 Database에 저장하는 코드

package com.acp.AbusingCheckProject.job;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v116.network.Network;
import org.openqa.selenium.devtools.v116.network.Network.GetResponseBodyResponse;
import org.openqa.selenium.devtools.v116.network.model.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.acp.AbusingCheckProject.entity.logTbl;
import com.acp.AbusingCheckProject.repository.LogTblRepository;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.github.bonigarcia.wdm.WebDriverManager;

@Component
public class NetworkMonitoring {

    @Value("${chrome.driver}")
    public String driverPath;

    @Autowired
    private LogTblRepository logTblRepository;

    @Scheduled(fixedRate = 3600000)
    public void executeNetworkMonitoring() {
        try {
        // ChromeDriver 경로 설정
        WebDriverManager.chromedriver().setup();

        // Chrome 옵션 설정
        ChromeOptions options = new ChromeOptions();
        //options.setCapability("goog:chromeOptions", Map.of("w3c", false));
        Map<String, Object> deviceMetrics = new HashMap<>();
        deviceMetrics.put("width", 1078);
        deviceMetrics.put("height", 924);
        deviceMetrics.put("pixelRatio", 3.0);
        Map<String, Object> mobileEmulation = new HashMap<>();
        mobileEmulation.put("deviceMetrics", deviceMetrics);
        mobileEmulation.put("userAgent", "Mozilla/5.0 (Linux; Android 8.0.0;" +  "Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/67.0.3396.99 Mobile Safari/537.36");
        options.setExperimentalOption("mobileEmulation", mobileEmulation);
        WebDriver driver = new ChromeDriver(options);

        // 페이지 로딩 타임아웃 설정
        driver.manage().deleteAllCookies();
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(30));

        // Chrome DevTools 설정
        DevTools devTools = ((ChromeDriver) driver).getDevTools();
        devTools.createSession();
        devTools.send(Network.clearBrowserCookies());
        devTools.send(Network.setCacheDisabled(true));
        
        // Network.enable 활성화
        devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));
        
        // 네트워크 이벤트 리스너 등록
        devTools.addListener(Network.requestWillBeSent(), entry -> {
            Request request = entry.getRequest();
            String requestUrl = request.getUrl();
            if (requestUrl.contains("요청 URL")) {
                System.out.println("요청이 발생했습니다: " + requestUrl);
            }
            
	            if (requestUrl.contains("모니터링 요청 URL")) {
	              try {
	                  String responseContent = getResponseContent(requestUrl);
	                  System.out.println("응답이 발생했습니다: " + responseContent);
	                  ObjectMapper om = new ObjectMapper();
	                  Map<String,Object> jsonMap = om.readValue(returnStr(responseContent),Map.class);
	                  
	                  logTbl logtbl = logTbl.builder()
	                                   .result(jsonMap.containsKey("a") ? (String) jsonMap.get("a") : "")
	                                   .uid(jsonMap.containsKey("b") ? (String) jsonMap.get("b") : "")
	                                   .fp(jsonMap.containsKey("c") ? (String) jsonMap.get("c") : "")
	                                   .tc(jsonMap.containsKey("d") ? (String) jsonMap.get("d") : "")
	                                   .sc(jsonMap.containsKey("e") ? (String) jsonMap.get("e") : "")
	                                   .score(jsonMap.containsKey("f") ? (String) jsonMap.get("f") : "")
	                                   .tg1(jsonMap.containsKey("g") ? (String) jsonMap.get("g") : "")
	                                   .tg2(jsonMap.containsKey("h") ? (String) jsonMap.get("h") : "")
	                                   .siteUrl("모니터링URL")
	                                   .logDate(LocalDateTime.now())
	                                   .build();
	                  
	                  logTblRepository.save(logtbl);
	                  driver.quit();   // WebDriver 종료
	              } catch (IOException e) {
	                  e.printStackTrace();
	              }
	          }
        });
        
        // 사이트로 이동
        driver.get("모니터링할URL");
        
        
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }

    private static String getResponseContent(String url) throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setRequestMethod("GET");

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        }
    }


    public String returnStr(String str){
        String filteredJson = "";
        try{
            // JSON 문자열의 시작 위치를 찾습니다.
            int start = str.indexOf("(");

            // JSON 문자열의 끝 위치를 찾습니다.
            int end = str.lastIndexOf(")");

            // JSON 문자열을 추출합니다.
                filteredJson = str.substring(start + 1, end);
        }catch (Exception e){
            e.printStackTrace();
        }
        return filteredJson;
    }
}
반응형