반응형

자바 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() 메소드로 종료 시켜야 한다.

반응형
반응형
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/

 

반응형
반응형

SFTP 접속하는 라이브러리는 jsch 다.

sftp: <http://www.jcraft.com/jsch/>

 

내서버에서 다른 서버 22번 sftp 접속하는 방법이다.

 

public class FTPUtil {

	private static Log logger = LogFactory.getLog(FTPUtil.class);

    private Session session = null;
    private Channel channel = null;
    private ChannelSftp channelSftp = null;

   // SFTP 서버연결
    public void connect(String user, String url, String password) throws Exception{

        logger.info(url);
        //JSch 객체 생성
        JSch jsch = new JSch();
        try {
            //세션객체 생성 ( user , host, port )
            logger.info("session");
            session = jsch.getSession(user, url);
            //password 설정
            session.setPassword(password);
            //세션관련 설정정보 설정
            java.util.Properties config = new java.util.Properties();
            //호스트 정보 검사하지 않는다.
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            //접속
            session.connect();
            //sftp 채널 접속
            channel = session.openChannel("sftp");
            channel.connect();

        } catch (JSchException e) {
            logger.error("connect JSchException error : " +e.toString());
            throw new Exception("JSchException");
        } catch (Exception e) {
        	 logger.error("connect error : " +e.toString());
             throw new Exception(e.toString());
		}
        channelSftp = (ChannelSftp) channel;
    }

    // 단일 파일 업로드
    public void upload( String dir , File file){
        FileInputStream in = null;
        try{ //파일을 가져와서 inputStream에 넣고 저장경로를 찾아 put
            in = new FileInputStream(file);
            channelSftp.cd(dir);
            channelSftp.put(in,file.getName());
        }catch(SftpException se){
            se.printStackTrace();
        }catch(FileNotFoundException fe){
            fe.printStackTrace();
        }finally{
            try{
                in.close();
            } catch(IOException ioe){
                ioe.printStackTrace();
            }
        }
    }


    public ChannelSftp moveDirectory(String dir){
    	 try{ //경로탐색후 inputStream에 데이터를 넣음
             channelSftp.cd(dir); // 결로 이동
         }catch(SftpException se){
             se.printStackTrace();
         }
		return channelSftp;
    }

    /**
     *
     * @param dir  서버의 저장되어 있는 경로
     * @param fileNm 다운받으려는 파일명
     * @param saveDir 현 서버의 다운받으려고 하는 경로
     */
    public void download(String dir, String fileNm, String saveDir){
        InputStream in = null;
        FileOutputStream out = null;
        //String path = "...";
        try{ //경로탐색후 inputStream에 데이터를 넣음
            channelSftp.cd(dir); // 결로 이동
            in = channelSftp.get(fileNm);

        }catch(SftpException se){
        	logger.error("download " +se.toString());
        }
        try {
            out = new FileOutputStream(new File(saveDir));
            int i;
            while ((i = in.read()) != -1) {
                out.write(i);
            }

        } catch (IOException e) {
        	logger.error("download io error : " +e.toString());
        } finally {
            try {
                out.close();
                in.close();
            } catch (IOException e) {
            	logger.error("download close error : " +e.toString());
            }
        }

    }

    /**
     * 인자로 받은 경로의 파일 리스트를 리턴한다.
     * @param path
     * @return
     */
    public Vector<ChannelSftp.LsEntry> getFileList(String path, String lsCmd) {

    	Vector<ChannelSftp.LsEntry> list = null;
    	try {
    		channelSftp.cd(path);
    		list = channelSftp.ls(lsCmd);
		} catch (SftpException e) {
			e.printStackTrace();
			return null;
		}
    	return list;
    }

    // 파일서버와 세션 종료
    public void disconnect(){
        channelSftp.quit();
        session.disconnect();
    }
}
반응형

+ Recent posts