Spring | XML데이터 파싱부터 DB 저장까지! - 1

sally·2021년 5월 24일
1

project

목록 보기
2/5
post-thumbnail

API를 이용하여 학원정보를 가져와야 하는데 난 당연히 json형태로 return 해줄줄 알았다. 그런데 웬걸 xml이라니! json만 쓰다보니까 xml은 까먹은지 오래였고,, 결국 구글링했서 구현했다🤣


xml 파싱해보자

사용할 API는 HRD-net에서 제공하는 구직자훈련과정 목록 API와 구직자훈련과정 과정/기관정보 API이다. 학원 정보 및 강좌에 대한 정보를 얻을 수 있다.

구직자훈련과정 목록 API (전체 과정의 정보 출력) 구직자훈련과정 과정/기관정보 API (한 과정의 상세정보를 출력)
  1. Url 준비

    • 각 사이트에서 API 인증키를 요청하여 받은 다음에 파싱할 Url을 작성한다
    String url = " https://www.hrd.go.kr/jsp/HRDP/HRDPO00/HRDPOA60/HRDPOA60_1.jsp?returnType=XML&"
    	     //사이트에서 발급받은 인증키
    	     +"authKey=gV6TA7Ep5JFP66lYZgtEip3bkBl6av4s"
                 //요청 parameters
    	     +"&pageNum=1&pageSize=10&srchTraStDt=20210524&srchTraEndDt=20210824&outType=1&sort=ASC&sortCol=TR_STT_DT&crseTracseSe=C0055,C0054,C0059&srchKeco1=20";
    
  2. Document객체 생성

    • document객체를 통해 파싱할 url의 데이터를 읽어온다.
    • documentInfo.getDocumentElement().getNodeName()을 출력하면 XML의 최상위 TAG 값을 가져온다 여기서는 <HRDNet>이 최상위 TAG 값이다.
    Document documentInfo = null;
     documentInfo = (Document) DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(url); 
     documentInfo.getDocumentElement().normalize();
     //Root: HRDNet
     System.out.println("Root: " + documentInfo.getDocumentElement().getNodeName());
    
  3. 파싱할 데이터가 있는 tag에 접근하기

    • 목록 API 기준으로 파싱할 데이터가 있는 TAG는 srchList이다.
    • nList에 srchList안에 있는 태그들이 담기게 된다.
    Element root = documentInfo.getDocumentElement();
     NodeList nList = root.getElementsByTagName("srchList").item(0).getChildNodes();
  4. nList에 담긴 데이터 꺼내오기

    • for문을 이용해 파싱한 데이터를 map에 저장하고, 새로운 list에 저장한다.
    • getTagValue("tagName",element) : "tagName"에 해당하는 데이터 가져오는 메소드 (아래 전체 소스코드에 있음)
    List<Map<String, String>> list = new ArrayList<>();
    for (int i = 0; i < nList.getLength(); i++) {
      Map<String, String> map = new HashMap<>();
      Node nNode = nList.item(i);
      Element eElement = (Element) nNode;

      map.put("trainCd", getTagValue("trprId", eElement)); // 과정코드
      map.put("trainTitle", getTagValue("title", eElement)); // 과정명
      map.put("acadCd", getTagValue("instCd", eElement)); // 학원코드
      map.put("acadTitle", getTagValue("subTitle", eElement)); // 학원명
      map.put("telNo", getTagValue("telNo", eElement)); // 학원 전화번호
      map.put("startDate", getTagValue("traStartDate", eElement)); // 훈련시작일자
      map.put("endDate", getTagValue("traEndDate", eElement)); // 훈련종료일자
      map.put("target", getTagValue("trainTarget", eElement)); // 훈련대상
      map.put("yardMan", getTagValue("yardMan", eElement)); // 정원
      map.put("courseMan", getTagValue("courseMan", eElement)); // 수강비
      map.put("realMan", getTagValue("realMan", eElement)); // 실제 수강비
      map.put("trainDegr", getTagValue("trprDegr", eElement)); // 회차

      list.add(map);
    }
  }

그 결과 최종 코드는!!!

요청 parameter에 페이지수가 나눠져 있기 때문에 총 페이지 갯수를 구하여 반복문을 이용해 모든 페이지에 접근해 가져왔다!
또, 각 과정을 진행하는 학원에 대한 상세 주소와 홈페이지 url은 과정/기관정보API에서 가져와야 했기 때문에

  1. 목록 API를 읽어서 List<Map<String, String>> 형태로 저장
  2. 다시 반복문을 돌려 과정/기관정보API 읽어서 상세 주소 / 홈페이지 url 데이터 Map에 넣기
  3. mapper로 Map을 보내서 DB에 데이터 저장/업데이트!

  private static final Logger logger = LoggerFactory.getLogger(AcadScheduler.class);
  
  @Autowired
  private AcademyMapper academyMapper;

  public static int PAGE_SIZE = 100;
  public static String URL = "https://www.hrd.go.kr/jsp/HRDP/HRDPO00/HRDPOA60/HRDPOA60_1.jsp";
  public static String URL2 = "https://www.hrd.go.kr/jsp/HRDP/HRDPO00/HRDPOA60/HRDPOA60_2.jsp";
  public static String SERVICE_KEY = "gV6TA7Ep5JFP66lYZgtEip3bkBl6av4s";


  public void update() throws Exception {    
    int result = 0;
    List<Map<String, String>> list = new ArrayList<>();

    try {
      // 학원 목록 읽어오기
      createDocument(list);

      for (Map<String, String> acadInfo : list) {
        createDetailInfo(acadInfo);

        result += academyMapper.createAcademy(acadInfo);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    logger.info("총 학원 갯수:" + result);
  }

  //tag값 정보를 가져오는 메소드
  private static String getTagValue(String tag, Element eElement) {
    NodeList nList = null;
    Node nValue = null;
    try {
      nList = eElement.getElementsByTagName(tag).item(0).getChildNodes();
      nValue = (Node) nList.item(0);
    } catch (Exception e) {
      e.printStackTrace();
    }
    if (nValue == null)
      return null;
    return nValue.getNodeValue();
  }

  private void createDetailInfo(Map<String, String> acadInfo) {
    Document documentInfo = null;
    // 파싱할 url 지정
    String parseUrl =
        URL2 + "?returnType=XML&authKey=" + SERVICE_KEY + "&srchTrprId=" + acadInfo.get("trainCd")
            + "&srchTrprDegr=" + acadInfo.get("trainDegr") + "&outType=2&srchTorgId=default";
    try {
      documentInfo =
          (Document) DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(parseUrl);
      //root tag
      documentInfo.getDocumentElement().normalize();
      // 과정,기관정보 데이터 파싱
      parseDetailXml(documentInfo.getDocumentElement(), acadInfo);
    } catch (SAXException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (ParserConfigurationException e) {
      e.printStackTrace();
    }

  }

  private void parseDetailXml(Element root, Map<String, String> map) {
    Node nNode = root.getElementsByTagName("inst_base_info").item(0);
    Element eElement = (Element) nNode;

    map.put("address", getTagValue("addr1", eElement)); // 상세주소
    map.put("url", getTagValue("hpAddr", eElement)); // 학원홈페이지url

  }

  private void parseXml(Element root, List<Map<String, String>> list) {
    NodeList nList = root.getElementsByTagName("srchList").item(0).getChildNodes();

    for (int i = 0; i < nList.getLength(); i++) {
      Map<String, String> map = new HashMap<>();
      Node nNode = nList.item(i);
      Element eElement = (Element) nNode;

      map.put("trainCd", getTagValue("trprId", eElement)); // 과정코드
      map.put("trainTitle", getTagValue("title", eElement)); // 과정명
      map.put("acadCd", getTagValue("instCd", eElement)); // 학원코드
      map.put("acadTitle", getTagValue("subTitle", eElement)); // 학원명
      map.put("telNo", getTagValue("telNo", eElement)); // 학원 전화번호
      map.put("startDate", getTagValue("traStartDate", eElement)); // 훈련시작일자
      map.put("endDate", getTagValue("traEndDate", eElement)); // 훈련종료일자
      map.put("target", getTagValue("trainTarget", eElement)); // 훈련대상
      map.put("yardMan", getTagValue("yardMan", eElement)); // 정원
      map.put("courseMan", getTagValue("courseMan", eElement)); // 수강비
      map.put("realMan", getTagValue("realMan", eElement)); // 실제 수강비
      map.put("trainDegr", getTagValue("trprDegr", eElement)); // 회차

      list.add(map);
    }
  }

  private void createDocument(List<Map<String, String>> list) {
    Document documentInfo = null;
    int pageNum = 1;
    SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
    Date currentDate = new Date();
    Calendar cal = Calendar.getInstance();
    cal.setTime(currentDate);
    // 현재날짜로부터 3개월뒤 날짜 설정
    cal.add(Calendar.MONTH, 3);
    String stDt = formatter.format(currentDate);
    String endDt = formatter.format(cal.getTime());
    // URL 설정
    String parseUrl = URL + "?returnType=XML&authKey=" + SERVICE_KEY + "&pageSize=" + PAGE_SIZE
        + "&srchTraStDt=" + stDt + "&srchTraEndDt=" + endDt
        + "&outType=1&sort=ASC&sortCol=TR_STT_DT&crseTracseSe=C0055,C0054,C0059&srchKeco1=20&pageNum=";

    try {
      int tot = 0, num = 1;
      while (true) {
        if (pageNum > num)
          break;
        documentInfo = (Document) DocumentBuilderFactory.newInstance().newDocumentBuilder()
            .parse(parseUrl + pageNum);
        documentInfo.getDocumentElement().normalize();
        if (pageNum == 1) {
          // 총 학원 갯수
          tot = Integer.parseInt(getTagValue("scn_cnt", documentInfo.getDocumentElement()));
          num = (tot / PAGE_SIZE) + 1;
        }
        // 목록 정보 데이터 파싱하기
        parseXml(documentInfo.getDocumentElement(), list);

        pageNum++;
      }
    } catch (SAXException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (ParserConfigurationException e) {
      e.printStackTrace();
    }
  }

DB에 저장하는 코드는 다음 시간에 ... 총총 ...👀

profile
Believe you can, then you will✨

0개의 댓글