본문 바로가기
개발 일기/기타

공공데이터 파싱하기(한국관광공사 API, JAVA, JPA)

by URMOO 2020. 7. 25.
반응형

 

안녕하세요.

오랜만에 블로그에 들어왔네요.

오늘 포스팅은 공공데이터 파싱에 관한 것입니다.

 

사실, 이 프로젝트를 시작했을 때, 이번에는 그냥 가데이터로 진행해야지 생각했었지만..

제대로 된 데이터로 하는 게 더 괜찮을 것 같다는 생각이 계속 들더라고요..

그래서 제가 프로젝트에 사용할 데이터를 조금만 DB에 저장해서 사용하기로 했습니다. 

 


 

우선, http://data.go.kr 로 접속하셔서 회원가입을 신청해주세요

그 후, 원하는 데이터를 선택하신 후 사용신청을 해주시고 서비스 키를 받아주세요

인증키가 제대로 떴다면, 밑에 예제를 한번 테스트해 보세요.

키를 발급받았다 하더라도 적용되는 시간이 있어서 10분 정도는 API를 사용하지 못하실 수 도 있습니다.

밑에 사진이 조금 잘렸지만.. 키를 넣어보시고 미리보기를 눌러주시면 새 창으로 호출한 API를 보실 수 있을 겁니다.

 

 

이제 JAVA에서 api를 파싱 해 볼까요?

 

공공데이터 포탈은 api 사용에 대한 가이드가 참 잘 돼있는 것 같습니다. 

각 언어별로 파싱 코드를 제공하고 있고,  가이드 문서가 잘 돼있어 필요한 정보를 쉽게 얻을 수 있습니다. 

(api by api.. 기관 by 기관 이라는게 조금 함정입니다..)

 

일단 코드를 제공해 주니 제공해주는 코드를 이용하도록 하겠습니다. 

각 api 상세 페이지로 들어가셔서 밑으로 쭉 내리시면 파싱 코드가 있습니다.

 

저는 java를 사용하니, java의 코드를 보도록 하겠습니다.

밑에가 조금 잘렸습니다..

 

 

일단 이 코드를 이용하기 위해서 파싱을 위한 컨트롤러를 하나 생성하였습니다.

사실 service로 넘겨서 구현을 해야겠지만... 그냥 컨트롤러에서 다 구현한 점 이해 부탁드립니다. (_ _)

 

제가 원하는 데이터는 지역별 관광지의 주소와 이름이었습니다. 

관광공사의 api에는 지역을 코드로 나누었기 때문에 지역 코드를 db에 수기로 저장해주었습니다. 

(사실 이 데이터도 api로 제공해주고 있지만... 10개밖에 되지 않기 때문에... )

 

 

아, city를 따로 db에 저장하는 이유는 제가 db설계를 그렇게 했기 때문에.... 

제 DB설계를 보고 싶으시다면 여기로 이동해 주세요 ^^

 

01. Travel 기능 명세 및 ERD

오늘은 Travel의 기능 명세와 ERD를 포스팅하겠습니다. 사용자에 따라 기능을 나눠봤는데, 개발하는 중간에 추가할 수도, 삭제할 수도 있을 것 같습니다. 간단히 Travel에 대해 설명을 드리자면 관리

doohee94.tistory.com

 

이제 진짜 파싱을 해봅시다. 

일단, 지역별로 10개 정도의 데이터를 저장을 하려고 합니다. 

 

우선, 지역 번호를 배열에 저장해놓고 for문안에 샘플 코드를 넣어줍니다. 

 

api 가이드를 보시면서 필요한 데이터를 빌더에 넣어주시면 됩니다.

각 api 마다 꼭 넣어줘야 하는 정보가 있으니 그런 부분은 빼지 말고 넣어주세요!

 

저는 listYN과 areaCode, _type를 추가해 주었고, api 주소를 바꿔주었습니다.

areaCode는 for문에서 바꿔주고 있으니 그 정보를 넣어주시면 되고,

_type은 제가 JSON으로 데이터를 받고자 넣어주었습니다. _type를 넣지 않으면 xml형식으로 넘어오게 됩니다.

   int[] areaCode = {1, 2, 3, 4, 5, 6, 7, 8, 31, 32};

        for (int i : areaCode) {
            StringBuilder urlBuilder = new StringBuilder("http://api.visitkorea.or.kr/openapi/service/rest/KorService/areaBasedList");
            urlBuilder.append("?" + URLEncoder.encode("ServiceKey", "UTF-8") + "=서비스키를 넣어주세요");
            urlBuilder.append("&" + URLEncoder.encode("ServiceKey", "UTF-8") + "=" + URLEncoder.encode("서비스키를 넣어주세요", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("numOfRows", "UTF-8") + "=" + URLEncoder.encode("10", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("pageNo", "UTF-8") + "=" + URLEncoder.encode("2", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("MobileOS", "UTF-8") + "=" + URLEncoder.encode("ETC", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("MobileApp", "UTF-8") + "=" + URLEncoder.encode("AppTest", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("_type", "UTF-8") + "=" + URLEncoder.encode("json", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("listYN", "UTF-8") + "=" + URLEncoder.encode("Y", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("areaCode", "UTF-8") + "=" + URLEncoder.encode(String.valueOf(i), "UTF-8"));
            URL url = new URL(urlBuilder.toString());
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Content-type", "application/json");

            System.out.println("Response code: " + conn.getResponseCode());
            BufferedReader rd;
            if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
                rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            } else {
                rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            }
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = rd.readLine()) != null) {
                sb.append(line);
            }
            rd.close();
            conn.disconnect();
            System.out.println(sb.toString());

            }
        }

 

 

 

위의 코드를 실행시켜주시면 이렇게 json 형식으로 데이터가 들어오는 걸 보실 수 있습니다.

만약 실패를 한다면 response code가 400과 같이 에러가 나고, 에러의 이유가 출력됩니다.

 

이제 이 데이터를 파싱 해서 db에 저장해 보도록 하겠습니다. 

 

json 파싱을 위해서 gradle에 gson을 추가해주었습니다. 

gson 말고 다른 것들도 많으니 본인이 편한 걸 선택해 주시면 됩니다. 

 implementation 'com.google.code.gson:gson'

그리고 json으로 받아온 데이터를 파싱 해줍니다. 

// api 파싱 코드... 

JsonParser parser = new JsonParser();
JsonObject obj = parser.parse(sb.toString()).getAsJsonObject();

JsonArray arr = obj.get("response").getAsJsonObject()
                    .get("body").getAsJsonObject()
                    .get("items").getAsJsonObject()
                    .get("item").getAsJsonArray();

for (JsonElement jsonElement : arr) {
     JsonObject temp = jsonElement.getAsJsonObject();
      City city = cityRepository.findById(Long.parseLong(String.valueOf(i))).orElseThrow(NullPointerException::new);

       touristRepository.save(Tourist.builder()
                        .name(temp.get("title").getAsString())
                        .cityStr(temp.get("addr1") == null? "-":temp.get("addr1").getAsString().split(" ")[0])
                        .load(temp.get("addr1") == null? "-":temp.get("addr1").getAsString())
                        .zipcode(temp.get("addr1") == null? "-":temp.get("addr1").getAsString().split(" ")[temp.get("addr1").getAsString().split(" ").length - 1])
                        .cityObj(city)
                        .build());
  }

Json Parser를 이용해 string으로 들어온 api 데이터를 파싱해줍니다.

그 후, json객체의 이름을 이용하여 jsonObject나 jsonArray로 받고, 원하는 데이터를 가져왔습니다. 

 

제가 원하는 데이터는 jsonArray내부의 데이터라서, array를 다시 for문을 돌려주어 저장하였습니다. 

 

city의 id에 해당하는 객체를 가져오고, jsonObject에서 원하는 데이터를 string형식으로 가져옵니다. 

"addr1"에 해당하는 데이터는 null값인 경우가 있어 그 부분을 처리해주었습니다. 

 

이렇게 데이터를 저장하면!

 

이렇게 관광지 데이터가 저장되게 됩니다!!!

저는 pageNo만 바꾸어 2~3번 정도 데이터를 돌려주었습니다 ㅎㅎ.. 이제 끝났네요

 

전체 코드는 다음과 같습니다. 

import java.io.BufferedReader;
import java.io.InputStreamReader;

import com.example.demo.city.entity.City;
import com.example.demo.city.repository.CityRepository;
import com.example.demo.tourist.entity.Tourist;
import com.example.demo.tourist.repository.TouristRepository;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.io.IOException;
import java.util.List;

@RestController
@RequestMapping(value = "/api/parse")
@RequiredArgsConstructor
public class ApiParseController {

    private final TouristRepository touristRepository;
    private final CityRepository cityRepository;

    @GetMapping
    @Transactional
    public void test() throws IOException {

        int[] areaCode = {1, 2, 3, 4, 5, 6, 7, 8, 31, 32};

        for (int i : areaCode) {
            StringBuilder urlBuilder = new StringBuilder("http://api.visitkorea.or.kr/openapi/service/rest/KorService/areaBasedList");
            urlBuilder.append("?" + URLEncoder.encode("ServiceKey", "UTF-8") + "=서비스키
            urlBuilder.append("&" + URLEncoder.encode("ServiceKey", "UTF-8") + "=" + URLEncoder.encode("서비스키UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("numOfRows", "UTF-8") + "=" + URLEncoder.encode("10", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("pageNo", "UTF-8") + "=" + URLEncoder.encode("2", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("MobileOS", "UTF-8") + "=" + URLEncoder.encode("ETC", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("MobileApp", "UTF-8") + "=" + URLEncoder.encode("AppTest", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("_type", "UTF-8") + "=" + URLEncoder.encode("json", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("listYN", "UTF-8") + "=" + URLEncoder.encode("Y", "UTF-8"));
            urlBuilder.append("&" + URLEncoder.encode("areaCode", "UTF-8") + "=" + URLEncoder.encode(String.valueOf(i), "UTF-8"));
            URL url = new URL(urlBuilder.toString());
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Content-type", "application/json");

            System.out.println("Response code: " + conn.getResponseCode());
            BufferedReader rd;
            if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
                rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            } else {
                rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            }
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = rd.readLine()) != null) {
                sb.append(line);
            }
            rd.close();
            conn.disconnect();
            System.out.println(sb.toString());

            JsonParser parser = new JsonParser();
            JsonObject obj = parser.parse(sb.toString()).getAsJsonObject();

            JsonArray arr = obj.get("response").getAsJsonObject()
                    .get("body").getAsJsonObject()
                    .get("items").getAsJsonObject()
                    .get("item").getAsJsonArray();

            for (JsonElement jsonElement : arr) {
                JsonObject temp = jsonElement.getAsJsonObject();
                City city = cityRepository.findById(Long.parseLong(String.valueOf(i))).orElseThrow(NullPointerException::new);

                touristRepository.save(Tourist.builder()
                        .name(temp.get("title").getAsString())
                        .cityStr(temp.get("addr1") == null? "-":temp.get("addr1").getAsString().split(" ")[0])
                        .load(temp.get("addr1") == null? "-":temp.get("addr1").getAsString())
                        .zipcode(temp.get("addr1") == null? "-":temp.get("addr1").getAsString().split(" ")[temp.get("addr1").getAsString().split(" ").length - 1])
                        .cityObj(city)
                        .build());
            }
        }
    }
}

 


 

반응형

댓글