Java 크롤링 API - Java keulolling API

서론

웹 프로젝트를 하다보면 Iframe 혹은 Web에 있는 필요한 데이터를 긁어와서 우리 화면에 뿌려야 하는 상황이 생기는데, 웹 크롤링(Web Scraping)을 사용하면 쉽게 필요한 데이터만 가져와서 사용할 수 있습니다.

Iframe 같은 경우는 URL 객체로 HTML을 문자열로 읽어오는데 시간이 오래 걸립니다. (10초 이상이 걸리는 경우도 있습니다.) 따라서 이런 경우는 스케줄러(Scheduler)를 통해 각 프로젝트의 특성에 맞게(ex) 한 달에 한 번) JSON 파일로 만들고, 해당 JSON 파일을 비동기나, 동기로 읽어와서 뿌려야 합니다.

크롤링(Scraping) 이란 ?

웹 크롤링이란 컴퓨터 소프트웨어 기술로 웹 사이트들에서 원하는 정보를 추출하는 것을 의미합니다. 크롤링의 정식 명칭은 Scraping 입니다.

Web scraping is a computer software technique of extracting information from websites

Jsoup

Jsoup Library는 자바에서 제공하는 HTML Parser 입니다.

Maven Dependency

Jackson Library

스프링부트 에서는 Json Format을 사용하기 위해서 Jackson Library를 사용합니다. 스프링에서는 Dependecy를 추가해야하고, 스프링 부트는 기본으로 제공합니다.

Logic

  • Jsoup 라이브러리를 사용해서 HTML parsing
  • 필요한 값들만 프로퍼티로 가지는 DTO 생성
  • select와, attr 메서드를 이용해서 필요한 값 추출 후에 DTO에 저장
  • DTO를 리스트에 저장
  • call Method convertValue by ObjectMapper
  • call Method writeValueAsString by ObejctMapper
  • 스케줄러 생성
  • 스케줄러의 메서드에서 JSON 생성파일 호출
  • 필요한 곳에서 JSON 파일을 읽는 메서드를 호출

Example

0. 아이스브레이크를 위한 서론

  • 예전에 Jsoup을 사용해서 크롤링 예제를 만든적이 있습니다만, Jsoup에 대한 이해도가 낮아 Apache HTTP를 사용해서 만든적이 있습니다.

  • 결론부터 말하면, Jsoup 자체가 HTTP Connection 을 기반으로 만들어졌기 때문에 단독 라이브러리로도 웹 크롤링이 충분히 가능 합니다.

  • 짧게 말해서 그냥 Jsoup Jar 하나면 웹 크롤링이 가능합니다.

  • Java Project는 1.7 / 1.8 에 대해서는 테스트 해보았습니다.

  • 빠르게 실무 적용이 가능한 예제를 보시려면 제일 아래로 내리세요.

1. Jsoup 다운로드 및 프로젝트 추가

  • 다운로드

    • URL : //jsoup.org/download

  • File

    • jsoup-1.11.2.jar core library

    • jsoup-1.11.2-sources.jar optional sources jar

    • jsoup-1.11.2-javadoc.jar optional javadoc jar

  • 다운로드 파일은?

    • 기본적으로 jsoup-x.xx.x.jar 만 다운 받아도 됩니다만 source로 들여다 보고 싶다, API도 보고 싶다하면 선택적으로 -sources나 -javadoc jar 도 다운로드 받으시면 됩니다.

  • 프로젝트 추가

    • 여기서 설명하는 예제는 기본 Java Project로 만들었고 수동 Build Path를 통해 Jar를 등록했습니다.

    • Maven 에서는 아래와 같이 사용하시면 됩니다.

      <dependency>   <groupId>org.jsoup</groupId>   <artifactId>jsoup</artifactId> <version>1.11.2</version> </dependency>

2. 가장 기본이 되는 Jsoup GET 방식 예제

  • 가장 기본이 되는 GET 방식 예제는 아래와 같습니다.

    try { // 1. URL 선언 String connUrl = "//www.daum.net"; // 2. HTML 가져오기 Document doc = Jsoup.connect(connUrl).get(); // 3. 가져온 HTML Document 를 확인하기 System.out.println(doc.toString()); } catch (IOException e) { // Exp : Connection Fail e.printStackTrace(); }

3. 가장 기본이 되는 Jsoup POST 방식 예제

  • 가장 기본이 되는 Jsoup POST 방식 호출은 아래와 같습니다.

    try { // 1. URL 선언 String connUrl = "//map.daum.net"; // 2. HTML 가져오기 Document doc = Jsoup.connect(connUrl).post(); // TODO POST의 data 값은 Jsoup.data(...) 을 사용하시면 됩니다. // 3. 가져온 HTML Document 를 확인하기 System.out.println(doc.toString()); } catch (IOException e) { // Exp : Connection Fail e.printStackTrace(); }
    • POST의 data 값은 Jsoup.data(...) 을 사용하시면 됩니다.

4. 쓸만한 Jsoup 예제

  • 기본 예제는 Hello World 예제를 보셨다면, 실제로 적용하기에는 문제들이 있습니다.

    • USER AGENT 가 없어 Malform URL 이 될 수 있습니다.

    • HTTPS 호출 시, 인증서가 없어 에러가 발생합니다.

  • 위에 두가지에 대해서 적용가능한 JSOUP 예제는 계속해서 예제로 설명드리겠습니다.

이슈해결 > Malform URL 은 어떻게?

  • Malform URL 에러는 아래와 같이 header / userAgent / ignoreContentType(true) 옵션을 추가하면 해결 됩니다.

private final static String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36"; public static void main(String[] args) { try { // 1. URL 선언 String connUrl = "//map.daum.net"; // 2. HTML 가져오기 Connection conn = Jsoup.connect(connUrl) .header("Content-Type", "application/json;charset=UTF-8") .userAgent(USER_AGENT) .method(Connection.Method.GET) .ignoreContentType(true); Document doc = conn.get(); // 3. 가져온 HTML Document 를 확인하기 System.out.println(doc.toString()); } catch (IOException e) { // Exp : Connection Fail e.printStackTrace(); } }

이슈해결 > SSL (HTTPS) 은 어떻게 호출해야하나?

  • 아래 에러는 SSL 인증 에러입니다.

    javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979) at sun.security.ssl.Handshaker.process_record(Handshaker.java:914) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559) at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185) at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153) at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:730) at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:706) at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:299) at org.jsoup.helper.HttpConnection.get(HttpConnection.java:288) at com.derveljun.jsoup.JsoupCrawlerExample.main(JsoupCrawlerExample.java:27)Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491) ... 15 moreCaused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141) at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382) ... 21 more
    • SSL 에러 발생은 아래 예제를 통해 적용가능합니다. JAVA Vers해봅니다. SSL을 우회적으로 인증하는 방법입니다.

    // SSL 우회 등록 public static void setSSL() throws NoSuchAlgorithmException, KeyManagementException { TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; }​ public void checkClientTrusted(X509Certificate[] certs, String authType) {}​ public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); }
    • SSL 설정을 메소드로 따로 빼서 사용하는 예제 입니다.

      package com.derveljun.jsoup;​ import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate;​ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager;​ import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document;​ public class JsoupCrawlerExample { private final static String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36"; // SSL 우회 등록 public static void setSSL() throws NoSuchAlgorithmException, KeyManagementException { TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; }​ public void checkClientTrusted(X509Certificate[] certs, String authType) {}​ public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } public static void main(String[] args) { try { // 1. URL 선언 String connUrl = "//www.daum.net"; // 2. SSL 체크 if (connUrl.indexOf("//") >= 0) { JsoupCrawlerExample.setSSL(); } // 3. HTML 가져오기 Connection conn = Jsoup.connect(connUrl).header("Content-Type", "application/json;charset=UTF-8").userAgent(USER_AGENT).method(Connection.Method.GET).ignoreContentType(true); Document doc = conn.get(); // 4. 가져온 HTML Document 를 확인하기 System.out.println(doc.toString()); } catch (IOException e) { // Exp : Connection Fail e.printStackTrace(); } catch (KeyManagementException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }​

점심시간 안에 끝내려고 했는데 시간이 너무 많이 지났네요 ^^;;

다음에 시간되면 조금더 글좀 다듬겠습니다.

부족한 부분이나 첨언 / 문의 사항은 댓글 부탁드립니다.

Toplist

최신 우편물

태그