반응형

자바 selenium 사용시 새탭을 열고 싶은 경우가 있다.

 

 

탭을 여는 방법은 여러가지 있는데 키보드 이벤트를 이용한 경우와 자바스크립트를 이용하는 경우가 있다.

 

자바스크립트를 이용하는 경우

((JavascriptExecutor) driver).executeScript("window.open()");

위의 방식으로 새탭을 열고, 새탭의 브라우저도 컨트롤 하고 싶은 경우 tab 을 이동해야 한다.

탭을 이동하는 방법은

ArrayList<String> tabs = new ArrayList<String>(driver.getWindowHandles()); // 탭리스트 가져오기
driver.switchTo().window(tabs.get(1)).navigate().to("https://www.naver.com");

getWindowHandles 로 탭 리스트를 가져와서 원하는 탭 index 로 이동하면 된다. 탭을 새로 만들었으면 

driver.switchTo().window(tabs.get(1)).navigate().to("https://www.naver.com"); // 방금만든 1번 탭으로 이동

로 탭 이동 후 새로운 탭에서 이동하고자 하는 url 로 이동하자.

 

샘플코드

예시로 탭을 새로 만들고 이미 요청했던 화면을 닫는 코드.

System.setProperty(WEB_DRIVER_ID, WEB_DRIVER_PATH);
//Driver SetUp
ChromeOptions options = new ChromeOptions();
options.setHeadless(true);
options.setCapability("ignoreProtectedModeSettings", true);

driver = new ChromeDriver(options);
// driver 는 알아서 생성하자.

RemoteWebDriver driver = webdriverExecutor.getWebDriver();

for(int i = 1; i < 100; i ++) {
  driver.get("https://juntcom.tistory.com/" + i);
  ((JavascriptExecutor) driver).executeScript("window.open()"); // 새로운 탭 열기
  driver.close(); // 포커스가 첫번째 탭이므로 첫번째 탭이 닫힌다.
  ArrayList<String> tabs = new ArrayList<String>(driver.getWindowHandles()); // 탭리스트
  driver.switchTo().window(tabs.get(0)).navigate().to("https://www.naver.com"); 
  // 탭이 2개였지만 close로 한개가 없으므로 현재 탭은 1개 그러므로 0번 탭으로 포커스 후 url 이동하자
}

여기서 driver.close 를 하지 않으면 계속해서 새탭이 만들어질 것이다.

get() 메소드는 현재탭에서 url 변경이다.

 

키 이벤트로 열기

// Open a new tab
driver.findElement(By.cssSelector("body")).sendKeys(Keys.CONTROL + "t");

컨트롤 T 로 탭을 열 수 있어, 다음과 같이 사용할 수 있다.

 

반대로 탭을 키 이벤트로 닫을 수도 있다.

Actions action = new Actions(driver);
// Close the newly opened tab
action.keyDown(Keys.CONTROL).sendKeys("w").perform();
// Switch back to original
action.keyDown(Keys.CONTROL).sendKeys("1").perform();

컨트롤 W 는 현재 포커스의 브라우저를 닫는 단축키

컨프롤 숫자는 해당 번호의 순서 탭으로 이동하는 단축키이다.

 

또한 탭이 많을 경우 제어가 힘들수도 있는데, 원래 탭으로 돌아갈 수 도 있다.

// I had to grab the original handle
String originalHandle = driver.getWindowHandle();

// And switch back to the original handle. I am not sure why, but
// it just did not work without this, like it has lost the focus
driver.switchTo().window(originalHandle);

 

 

stackoverflow.com/questions/17547473/how-to-open-a-new-tab-using-selenium-webdriver[새탭열기 및 이동]

반응형
반응형

자바로 크롤링을 하려면 SSR - 서버사이드 렌더링 인 경우 url 에 로 http 리퀘스트 해서 받아온 응답을

element 를 찾아 파싱만 하면 된다.

 

하지만 문제가 CSR - 클라이언트사이드 렌더링 의 경우 크롤링을 하기 어렵다.

뿐만 아니라 로그인 및 기타 인증이 필요한 사이트일 수록 크롤링을 하기 어렵다.

 

그래서 브라우져를 프로그래밍으로 조작해서 필요한 데이터만 추출하는 라이브러리를 사용해야 한다.

라이브러리는 Selenium 라이브러리다.

 

Selenium?

: Selenium은 주로 웹앱을 테스트하는데 이용하는 프레임워크이다. ebdriver라는 API를 통해 운영체제에 설치된 Chrome등의 브라우저를 제어하게 된다.

spring boot 를 활용해서 maven 으로 pom.xml 에 다음과 같이 추가한다.

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.3.1</version>
</dependency>

 

브라우져의 드라이버도 각자 받아야한다.

유의해야 할 것은 크롬의 경우 사용자가 사용하는 브라우져의 크롬 버젼을 확인해서 같은 버젼의 드라이버를 받아야 한다.

설정 > chrome 정보 에서 버젼을 확인한다.

 

드라이버 세팅

public class Webdriver {

    //WebDriver 설정
    private WebDriver driver;

    //Properties 설정
    public static String WEB_DRIVER_ID = "webdriver.chrome.driver";
    public static String WEB_DRIVER_PATH = "/Users/chromedriver"; // 다운받은 크롬드라이버 경로


    public Webdriver() {
        //System Property SetUp

        chrome();
        //firefox();
    }
    
    private void chrome(){
        System.setProperty("webdriver.chrome.driver", DRIVER_PATH);
		ChromeOptions options = new ChromeOptions();
		options.setHeadless(true);
		options.addArguments("--lang=ko");
	    options.addArguments("--no-sandbox");
	    options.addArguments("--disable-dev-shm-usage");
		options.addArguments("--disable-gpu");
        options.setCapability("ignoreProtectedModeSettings", true);


	    ChromeDriver driver = new ChromeDriver(options);
	    driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);
    }
    
    private void firefox() {
        System.setProperty("webdriver.gecko.driver", "/Users/eomjuntae/develop/spring/crawling/src/main/resources/geckodriver");
        driver = new FirefoxDriver();
    }
    ...
    
    public void useDriver(String url) {
    	driver.get(url);	
    }
}    

위와 같이 크롬 드라이버에 관한 세팅을 하자. 

파이어폭스 드라이버 메소드도 만들었다. 위의 경로 설정으로 사용하면 된다.

 

드라이버를 생성후 get 메소드를 사용하면 된다.

Webdriver webdriver = new Webdriver();
webdriver.useDriver("https://juntcom.tistory.com");

 

드라이버 close 및 quit

브라우저를 띄우게 되면 항상 close 를 시켜줘야 한다. 하지 않으면 메모리만 잡아먹게 된다.

close() 메소드와 quit() 메소드의 차이는

quit 는 브라우져 종료

close 는 탭 종료 이다.

더이상 사용하지 않을 경우 quit() 메소드로 종료 시켜야 한다.

반응형
반응형
  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

 

상속이란

기반이 되는 상위 클래스의 특성을 하위 클래스에게 적용하고, 거기에 더해 필요한 특성을 추가 확장하는 방식을 말한다.

상속의 목적은 기존 기능의 확장과 코드의 재사용이다.

 

상속의 장점

하위클래스에 상위 클래스를 extend 만 하여도 코드가 자동적으로 재사용된다는 것이며, 확장성을 위해 변경하여야 할 부분의 로직만 overiding을 사용하여 재정의도 가능하다.

 

상속 시 단점

상속을 사용하여 코드를 재사용하느 경우에는 기존 상위 클래스가 어떻게 작성되었는지 고려해야 한다.

상속의 특성상 상위 클래스와 하위 클래스가 결합상태로 묶여, 추후 코드의 수정으로 상위클래스의 변경 여파가 하위클래스에 전파될 수 있는 상황이 발생한다.

 

상속을 사용하는 경우

프로젝트 구조 자체가 상속을 위해 설계된 경우

is-a 관계가 명확한 경우 (동물 -> 포유류 -> 고래 등 상위 분류와 하위분류가 명확한 경우)

이미 존재하는 클래스의 메서드와 필드를 재사용할 수 있기때문에 사용한다. 하지만 단순히 재사용만을 위해 상속을 사용하게 되면 oop 의 목적과는 맞지 않는 클래스가 될 수 있다.

 

자바 상속의 특징

다중상속을 지원하지 않는다.

모든 클래스는 Object 클래스의 자식 클래스이다.

 

자바의 상속 구조

단일 상속 구조

public class 상위클래스 {

}

public class 하위클래스 extends 상위클래스 {

}

계층적 상속 구조

public class 상위클래스 {

}

public class 하위클래스A extends 상위클래스 {

}

public class 하위클래스B extends 상위클래스 {

}

자바는 다중 상속을 허용하지 않는다.

다중 상속을 허용하지 않는 이유는 하위 클래스의 코드를 복잡하게 만들기 떄문이다.

다중상속을 하려면 인터페이스를 이용한 다중 상속이 있다.

public class 상위클래스 {

}

public interface 인터페이스{

}

public class 하위클래스 extends 상위클래스 implements 인터페이스 {

}

 

Super 키워드

super 키워드는 자식클래스에서 부모 클래스를 가리키는 키워드이다.

super 키워드를 통해 자식클래스는 부모 클래스의 필드나 메서드를 호출할 수 있다.

super() 메서드는 부모클래스의 생성자를 호출하는 메서드이다.

자식클래스의 생성자에는 기본적으로 super() 를 호출해야 하는데 기입하지 않더라도 컴파일러가 기본으로 추가한다.

class Parent {
	
    public Parent() {
    	System.out.println("부모 클래스 생성자");
    }
    
}

class Child extends Parent {

    public Child() {
    //  super(); 내부적으로 동작한다.
    	System.out.println("자식 클래스 생성자");
    }
}

class Main {
    public static void main(String[] args) {
        Parent p = new Parent();
        
        System.out.println(">>");
        
        Child c = new Child();
    }
}

// 출력내용

부모 클래스 생성자  // 얘도 알고보면 앞에 Object의 생성자가 호출 되겠지.
>>
부모 클래스 생성자다.  // 부모클래스의 생성자가 호출되고 자식 클래스의 생성자가 호출된다.
자식 클래스 생성자다.  

자식클래스의 기본생성자를 만들때 부모 클래스의 기본 생성자가 없으면 컴파일 에러가 생긴다.

class Parent {  //  기본 생성자가 없는 부모 클래스

    String name;
    
    public Parent(String name) {
        this.name = name;
    }
    
}

class Child extends Parent {
    
    public Child() {    // 이 경우 부모 클래스에 기본 생성자가 없기 때문에
    // super();         // 컴파일 에러가 난다.
    }
    
    public Child() {   
        super("super class"); // 부모 클래스의 생성자가 정확히 호출 되므로 에러가 없다.
    }
    
}

 

메서드 오버 라이딩

오버라이딩은 부모클래스로부터 상속받은 메서드를 자식 클래스에서 재정의 하는 것이다.

class Parent {
    public void say() {
        System.out.println("I`m parent.");
    }
}

class Child extends Parent {
    
    @Override
    public void say() {
        System.out.println("I`m child.");
    }
}

class Main {
	public static void main(String[] args) {
        Parent parent = new Parent();
        Child child = new Child();
        
        parent.say();
        child.say();
    }
}

@Override 어노테이션은 없어도 되지만 개발자에게 부모클래스에 있는 메서드라는것을 명시하기 위해 쓰는 것이다.

부모클래스에 없는 메서드에 어노테이션을 붙일 경우 컴파일 오류가 난다.

 

다이내믹 메서드 디스패치 (Dynamic Method Dispatch)

메서드 디스패치란 메소드를 어떻게 호출할 지를 정해서 호출하는 것이다.

정적 메소드 디스패치 (static method dispatch) 는 메서드가 어떻게 실행될지가 컴파일 타임에 결정된다.

스태틱 메소드 디스패치 (Static Method Dispatch)

스태틱 메소드 디스패치는 구현 클래스를 통해 컴파일 시점에서 컴파일러가 어떤 메소드를 호출할지 명확하게 알고 있는 경우
컴파일 시 생성된 바이트코드에도 정보가 남아있으며 애플리케이션 실행 전에 호출할 메소드 결정
대표적으로, 메소드를 오버로딩(Overloading)하면 매개변수 타입과 개수에 따라 어떤 메소드를 호출할지 알 수 있는 경우(메소드 시그니처 변경)
상위 클래스가 있더라도 하위 클래스(구현 클래스)로 선언을 하고 하위 클래스의 인스턴스를 생성

다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

다이나믹 메소드 디스패치는 인터페이스나 추상 클래스에 정의된 추상 메소드를 호출하는 경우로, 호출되는 메소드가 런타임 시 동적으로 결정되는 것
인터페이스 또는 추상 클래스로 선언하고 구현/상속 받은 하위 클래스의 인스턴스를 생성
컴파일러가 알고 있는 타입에 대한 정보를 토대로 런타임 시 해당 타입의 객체를 생성하고 메소드를 호출

 

public interface Animal {
  String method();
}

public class Dog implements Animal {

  @Override
  public String method() { . . . }

  public void bark() { . . . }
}

public class Cat implements Animal {

  @Override
  public String method() { . . . }

  public void meow() { . . . }
}
// 예시 1
public static void main(String[] args) {
  // 다이나믹 메소드 디스패치
  Animal animal = new Dog();
  System.out.println(animal.method());
}

// 예시 2
public class Example {
  private Animal animal;

  public Example(Animal animal) {
    this.animal = animal;
  }

  public void print() {
    // 다이나믹 메소드 디스패치
    System.out.println(animal.method());
  }
}
  • 런타임 전에는 객체 생성이 되지 않기 때문에 Animal animal = new Dog();를 해도, 컴파일러는 Dog가 생성됨을 알 수 없으므로 Animal이 정의한 method() 메소드만 접근 가능
  • Example 클래스의 생성자 인자로 넘어오는 매개값의 타입이 Dog인지 Cat인지(혹은 그 외)는 실행 시 확인 가능.  실행 시 동적으로 확인해서 다이나믹 디스패치

 

더블 디스패치 (Double Dispatch)

다이나믹 메소드 디스패치가 2번 발생하는 것
디자인 패턴 중 방문자 패턴 (Visitor Pattern) 과 밀접한 관계

방문자 패턴 (Visitor Pattern)

일반적으로 OOP는 객체가 스스로 행위를 수행하게 하지만, 경우에 따라(ex. 확장성 고려, OCP 위배) 객체의 행위 수행을 외부 클래스에 위임

이 때 사용하는 디자인 패턴 종류는 전략 패턴, 커맨드 패턴, 방문자 패턴

  • 전략 패턴 (Strategy Pattern)
    • 하나의 객체가 여러 동작을 하게 하는 패턴 (1:N)
  • 커맨드 패턴 (Command Pattern)
    • 하나의 객체가 하나의 동작(+보조 동작)을 하게 하는 패턴 (1:1)
  • 방문자 패턴 (Visitor Pattern)
    • 여러 객체에 대해 각 객체의 동작들을 지정하는 패턴 (N:N)

 

추상클래스

추상(abstract)이란 실체 간에 공통되는 특성을 추출한 것
추상 클래스는 객체를 직접 생성할 수 있는 실체 클래스의 공통적인 특성(필드, 메소드)을 추출해서 선언한 클래스

추상 클래스는 인스턴스 생성 불가능

Animal animal = new Animal(); // (X)
Animal animal = new Dog();    // (O)

클래스 선언 시 abstract 키워드 추가

public abstract class 클래스 {
  // 필드
  // 생성자
  // 메소드
}
  • 추상 클래스를 상속받은 자식 객체가 생성될 때, 내부에서 super 메소드가 호출되어 추상 클래스 객체를 생성하기 때문에 생성자 필요

추상 메소드 오버라이딩

추상 클래스는 자식 클래스의 공통 기능을 추출해 구현한 클래스이지만, 경우에 따라 자식 클래스마다 구현 내용이 달라질 수 있기 때문에 필요 시 추상 메소드를 작성
추상 메소드는 추상 클래스에서만 선언 가능하며, 메소드 선언부만 있는 형태
자식 클래스가 반드시 실행 내용을 구현하게 하기 위해 추상 메소드 선언

public abstract class Animal {
  public abstract void sound();
}

구현

public class Dog {
  @Override
  public void sound() {
    System.out.println("bark");
  }
}
public class Cat {
  @Override
  public void sound() {
    System.out.println("meow");
  }
}

 

final 키워드

메소드에 붙는 경우 - 오버라이딩 불가

class Parent {

    final void run() { // 메소드에 final이 붙는 경우.
        System.out.println("run");
    }
    
}

class Child extends Parent {

    @Override  //  오버라이딩을 못함, 컴파일 에러.
    void run() {
    }
    
}

클래스에 붙는 경우 - 상속 불가

final class Parent { // 클래스에 final이 붙는 경우.
}


class Child extends Parent { // 상속받을 수 없음, 컴파일 에러.
}

 

Object 클래스

 자바에서 모든 클래스들의 상위 클래스이다. 내가 임의로 만든 클래스도 Object 클래스를 상속받고 있다. extends Object를 써넣지 않았는데도 어떻게 이게 되는 걸까? 이것은 컴파일러가 컴파일 타임에 쓱 끼워 넣어 준다.

 

 내가 만든 클래스는 다른 클래스를 상속받고 있는데, 다중 상속이 되지 않는 자바인데 어떻게 Object 상속 부분을 끼워 넣어줄까? 이것은 컴파일러가 똑똑하게 가상 상위 클래스를 찾아 거기에 Object 상속을 슥 끼워넣어 준다.

 

 이렇게 해서 직접적으로나 간접적으로나 모든 클래스는 Object 클래스를 상속받는다.

반응형
반응형
public static String getIp(){
    String result = null;
    try {
        result = InetAddress.getLocalHost().getHostAddress();
    } catch (UnknownHostException e) {
        result = "";
    }
   return result; 
}

InetAddress.getLocalHost().getHostName()

을 사용하면 서버에서 hostname 을 찾는것과 같은 이름을 찾는다.

 

 

반응형
반응형

jaxb - Java Architecture for XML Binding 

jaxb 라는 xml 를 바인딩하는 라이브러리가 있다.

한마디로 파싱 관련 라이브러리다.

 

JAXB 는 XML 문서정보를 거의 동일한 구조의 오브젝트로 직접 매핑한다.

 

스키마 컴파일은 xjc 라는 명령어로 수행한다.

xsd 라는 xml 로 정의된 문서를 java 오브젝트로 컴파일 하는 작업이다.

 

XJC(JAXB binding compiler) 툴

xjc 도구를 사용하여 XML 스키마 파일을 전체 어노테이션 지정된 Java 클래스로 컴파일할 수 있다.

xjc는 JDK bin 디렉터리에 포함되어 있다.

 

확인하기

1) 내 java jdk 경로

EX) C:\Program Files\Java\jdk1.8.0_191\bin

위 예시 경로에 xjc 가 있는지 확인

2) cmd 커맨드창에 xjc -version 명령어를 통해 xjc가 사용가능한지 확인

 

사용하기

"C:\Program Files\Java\jdk1.8.0_191\bin\xjc" -p 패키지명 파일명.xsd

EX) "C:\Program Files\Java\jdk1.8.0_191\bin\xjc" -p com.example.java start.xsd

위 명령어를 사용하면 start.xsd 라는 xsd 파일이 java pojo(vo) 로 변경된다.

단, 위의 명령어 경우 start.xsd 가 있는 폴더로 이동해서 위 명령어 사용하자.

그러면 해당 폴더에 .java 파일이 우루루 생긴다.

 

docs.oracle.com/javase/7/docs/technotes/tools/share/xjc.html [xjc 옵션 문서]
stackoverflow.com/questions/1650249/how-to-make-generated-classes-contain-javadoc-from-xml-schema-documentation[xsd 밑 xjc 예시]
반응형
반응형

문자열로 된 수식 계산하기

예를 들어 데이터에 "1+1" 이라는 스트링이 넘어올 경우 이 값의 결과값 2 가 필요한 경우가 있다.

이런 경우에 스크립트 엔진을 이용해 문자열을 계산하자 

파싱안하고 스크립트 엔진으로 연산하기

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;

public class Test {
  public static void main(String[] args) throws Exception{
      ScriptEngineManager mgr = new ScriptEngineManager();
      ScriptEngine engine = mgr.getEngineByName("JavaScript");
      String foo = "30+2";
      System.out.println(engine.eval(foo));
    }
}

 

반응형
반응형

gson 을 사용할 떄 json 구조에 맞춰서 fromJson 메소드를 호출하면 자동으로 파싱을 해주지만,

종종 커스텀을 해서 파싱을 해야 할떄가 있다.

 

1. 해야하는 순간은 해당 데이터가 모두 동일한 포맷으로 파싱이 되어야 한다든지,

EX) 공백 "" 스트링이 있으면 항상 null 로 치환, 또는 날짜가 포함된 경우 동일한 포맷으로 파싱해야 하는 경우.

2. 일반적인 방식으로 파싱이 어려운 경우

EX) json 객체의 키값이 가변적인 경우여서 객체명으로 파싱이 불가능할 경우

{ 
 "1234": {
	"name" : "cat"
 },
 "5594": {
 	"name": "dog" 
 }
}

EX) 같은 json 키 이지만 값에 value 에 따라 파싱되는 객체 구조가 다를 경우

 

 

여기서 registerTypeAdapter 에 넣는 class 에 따라 해당 클래스가 있는 json 이 gson 으로 파싱될떄 deserializer 가 호출된다.

String.class 를 넣으면 파싱되는 데이터타입중에 String 인 데이터는 모두 deserialize 를 호출하게 된다.

CustomDeserializer 클래스

public class CustomDeserializer implements JsonDeserializer<ReturnVo> {

	/**
	 * JsonElement json parameter 은 parsing 하려는 전체 json 데이터
	 */
	@Override
	public ReturnVo deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
	
		ReturnVo returnVo = new ReturnVo();
      	JsonObject jsonObject = json.getAsJsonObject();
        /**
          파싱 
        */
    return returnVo;
	}
}

사용 예시

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(ReturnVo.class, new CustomDeserializer()); 
Gson gson = builder.create();
ReturnVo response = gson.fromJson(jsonStr, ReturnVo.class); /** fromJson 호출시 CustomDeserializer 가 호출됨*/
return response;

 

 

참고문헌

> https://stackoverflow.com/questions/28319473/gson-same-field-name-different-types [Same field name, different types] 

반응형
반응형

Gson casting 오류

 

gson 을 사용하다 보면

__com.google.gson.internal.LinkedTreeMap cannot be cast to my class__

이런 오류가 나는데, casting 을 사용하려고 하면 나는 오류다.

Gson gson = new Gson();
list = gson.fromJson(jsonString, new TypeToken<List<T>>(){}.getType());

jsonString list 로 fromJson 하는 경우에 생기는 오류이다.

 

 

결론은 아래 메소드로 다시 json 변환 후 object 로 변환 하던지, 아니면 gson 파싱 객체를 casting하지 않도록 만들어야 한다.

//array 를 json String 으로 변환
public static <T> String arrayToString(ArrayList<T> list) {
    Gson g = new Gson();
    return g.toJson(list);
}
// json String 을 list Object 로 변환
public static <T> List<T> stringToArray(String s, Class<T[]> clazz) {
    T[] arr = new Gson().fromJson(s, clazz);
    return Arrays.asList(arr); //or return Arrays.asList(new Gson().fromJson(s, clazz)); for a one-liner
}

참고문헌

https://stackoverflow.com/questions/27253555/com-google-gson-internal-linkedtreemap-cannot-be-cast-to-my-class  
www.programmersought.com/article/46923204627/

 

반응형

+ Recent posts