** JSON Parsing
http://swiftapi.rubypaper.co.kr:2029/hoppin/movies?version=1&page=1&count=20&genreId=&order=releasedatease 사이트의 데이터 중에서 title과 ratingAverage의 값만 가져와서 데이터베이스에 저장
1. 데이터 구조를 보고 필요한 데이터를 결정
2. Maven 기반으로 Java Application Project를 생성
=> Java Project를 만들고 MavenProject로 변환
3. JSON Parsing Library 사용을 위한 의존성 코드를 pom.xml에 작성
=> www.mvnrepository.com에서 json을 검색하여 직접 입력 or 참고파일에서 복사하여 사용
4. main 메소드를 소유한 클래스를 생성
5. main 메소드에 문자열을 다운받는 코드를 작성
- 과정 : 다운받을 URL을 생성 -> HttpURLConnection 인스턴스를 생성하고 옵션 설정 -> Stream 생성하여 다운로드
- 문자열을 한번에 읽기 힘들기 때문에 줄 단위로 읽어서 작업(합칠 때는 String 대신 StringBuilder를 사용
* String : 자신에게 편집 작업 할수 없어 복사하여 수행하므로 메모리 낭비가 발생
* StringBuilder : 자신에게 문자열을 추가 할 수 있음
//다운로드 받을 문자열을 저장 할 변수
String jsonString = null;
//웹에서 다운받기
try {
//다운로드 받을 주소 만들기
String addr = "http://swiftapi.rubypaper.co.kr:2029/hoppin/movies?version=1&page=1&count=20&genreId=&order=releasedatease";
//URL에 한글이 있으면 한글부분은 utf-8로 인코딩
//URLEncoder.encode("한글문자열","utf-8") - 지금은 없으므로 패스
URL url = new URL(addr);
//HttpURLConnection 생성
//openConnection은 URLConnection이라는 추상 클래스 타입으로 리턴하므로 강제 형변환하여
//일반 클래스 타입으로 변경
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//옵션 설정
con.setConnectTimeout(30000); //최대 연결 시간 설정
con.setUseCaches(false); //이전에 받은 데이터 사용 여부
//문자열을 읽을 스트림을 생성
//읽은 내용이 깨질때는 con.getInputStream다음에, "인코딩방식"을 추가
//(con.getInputStream(), utf-8)
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream()));
//문자열을 읽기 위한 임시변수를 생성
StringBuilder sb = new StringBuilder();
while(true) {
//한줄씩 읽어오기
String line = br.readLine();
//읽은 데이터가 없으면 종료
if(line == null)
break;
//읽은 데이터가 있으면 sb에 추가
sb.append(line + "\n");
}
//연결 종료
br.close();
con.disconnect();
//읽은 내용을 String으로 변환
jsonString = sb.toString();
}catch (Exception e) {
System.err.println("다운로드 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
6. 데이터를 파싱해서 list에 저장하는 코드 작성
System.out.println(jsonString);
//파싱 결과를 저장할 변수 생성
//title과 ratingAverage를 저장(Map에 저장)
List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
//데이터 파싱
try {
//텍스트가 존재하는 경우만 수행
//&&을 만들경우 순서는 무조건 null을 검사하는 소스가 '앞'에 오도록해야 함
//||를 만들경우 순서는 무조건 null을 검사하는 소스가 '뒤'에 오도록 해야 함
//null은 trim을 부를 수 없으므로 NullPointException발생(튕김)
if(jsonString != null && jsonString.trim().length() >0) {
//첫번째는 JSON 객체
JSONObject mainData = new JSONObject(jsonString);
//System.out.println(mainData);
//hopping이라는 key의 값을 객체로 가져옴
JSONObject hoppin = mainData.getJSONObject("hoppin");
//System.out.println(hoppin);
JSONObject movies = hoppin.getJSONObject("movies");
//System.out.println(movies);
JSONArray movie = movies.getJSONArray("movie");
//System.out.println(movie);
for(int i=0; i<movie.length(); i+=1) {
JSONObject imsi = movie.getJSONObject(i);
//System.out.println(imsi);
String title = imsi.getString("title");
String ratingAverage = imsi.getString("ratingAverage");
//Map으로 생성
Map<String, Object> map = new HashMap<String, Object>();
map.put("title", title);
map.put("ratingAverage", Double.parseDouble(ratingAverage));
//list 에 추가
list.add(map);
//파싱 데이터 출력부
for(Map<String, Object> movieList : list) {
System.out.println(movieList);
}
}
}else {
System.out.println("다운받을 문자열이 없음");
//프로그램 종료
System.exit(0);
}
}catch (Exception e) {
System.err.println("파싱 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
7. 데이터를 저장하기 위한 테이블을 생성
=> 문자열 title, 실수형 ratingAverage를 저장
=> 기본키로 사용할 code를 추가 : auto_increment로 설정
=> MySQL 데이터베이스에 접속해서 테이블을 생성
(접속시 refuse connection 발생시 [서비스] - [mySQL]사용
-- 사용할 데이터베이스를 설정
use mysql;
-- 테이블 생성 구문
create table movie(
code int primary key auto_increment,
title varchar(100),
ratingaverage double
)default charset = utf8;
8. mysql을 java에서 사용하기 위한 의존성을 pom.xml 설정
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
9. List에 저장된 데이터를 테이블에 저장하는 코드를 작성
=> 드라이버 클래스 로드 -> 연결객체 생성 -> SQL실행 객체 생성 -> SQL 실행 -> 결과 사용 -> 생성한 객체 닫기
**Kakao 검색 API의 내용 파싱
=> developers.kakao.com에서 계정을 생성하고 로그인
=> URL : 웹서버에게 요청하는 주소
- 요청 : request / 응답 : response / 서버에게 전송하는 데이터 : parameter
=> 웹 서버에게 요청을 할 때는 URL과 header가 필요
- header는 URL에 노출되서는 안되는 정보들을 저장
=> URL의 구성
- 프로토콜://서버IP 또는 Domain:포트번호/요청경로?파라미터명=값&파라미터명=값...
- //로 시작하면 상황에 따라 http or https로 접속
- 포트번호와 요청경로는 생략되는 경우가 있음(생략된 경우 없는 것이 아니라 서버설정으로 자동 입력된 것)
- web에서 parameter는 클라이언트가 서버에게 전송하는 데이터
- 이름과 값 모두 문자열만 가능(한글포함시 인코딩하여 요청)
=>Kakao Open API를 사용하려면 애플리케이션을 생성
- NativeApp : 일반 응용프로그램(SmartPhone Application에서 사용하는 경우)
- Rest API : 서버에서 데이터를 주는 경우(데이터를 받아오고자 하는 경우)
- Javascript : 웹화면에서 사용(Web화면(html)에서 사용하는 경우)
- Admin : 로그인 관련
=> App Key : f9e2c5d96fd9f8f18c1757ee858596ec
=> Kakao Book 검색 API
- url - https://dapi.kakao.com/v3/search/book?target=title&query=책제목
header - Authorization:KakaoAK kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
ex) con.addRequestProperty("Authorization", "KakaoAK f9e2c5d96fd9f8f18c1757ee858596ec");
1. 검색어를 입력받아서 검색어에 해당하는 책이 몇권이고 몇페이지에 걸쳐 있는지 확인
- Web에서 Scapping(수집)을 할때는 전체 데이터가 몇개인지 확인하는 것이 먼저
// 1.web에서 문자열 다운로드 받기
// 다운로드 받을 문자열을 저장 할 변수
String jsonString = null;
try {
// 다운로드 받을 URL 생성
Scanner sc = new Scanner(System.in);
System.out.println("조회할 도서명");
String keyword = sc.nextLine();
// 영문과 숫자이외의 데이터가 있을 수 있으므로 인코딩
keyword = URLEncoder.encode(keyword, "utf-8");
//URL 생성
String addr = "https://dapi.k"
+ "akao.com/v3/search/book?target=title&size=50&query=" + keyword;
URL url = new URL(addr);
//URL연결 객체 생성
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(30000);
con.setUseCaches(false);
//헤더 설정
con.addRequestProperty("Authorization", "KakaoAK f9e2c5d96fd9f8f18c1757ee858596ec");
//문자열 읽어오기
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream()));
while(true) {
String line = br.readLine();
if(line == null)
break;
sb.append(line + "\n");
}
jsonString = sb.toString();
//연결 해제
br.close();
con.disconnect();
sc.close();
} catch (Exception e) {
System.err.println("다운로드 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
//System.out.println(jsonString);
2. 조회된 데이터 건수 찾아오기
=>JSON 문자열 : Object 형태로 리턴, 내부의 meta 속성 추출시 Object 형태가 리턴, 내부에 다시 total_count의 값을 가져오면 데이터 건수를 알수 있음
//읽어온 데이터에서 조회된 데이터 건수 찾아오기
//조회된 데이터 개수를 저장할 변수
int total_count = -1;
try {
//문자열을 JSONObject로 변경
JSONObject json = new JSONObject(jsonString);
//meta 속성의 내용을 JSONObject로 가져오기
JSONObject meta = json.getJSONObject("meta");
//System.out.println(meta);
//total_count 속성의 값을 정수로 가져오기
total_count = meta.getInt("total_count");
System.out.println(total_count);
}catch (Exception e) {
System.err.println("데이터 개수 가져오기 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
3. 페이지 개수 구하기
//3. 페이지당 개수 구하기
//페이지당 출력할 개수 설정
int perPage = 50;
//페이지 개수 계산 : 전체 데이터 개수와 페이지당 출력개수를 이용
//서버프로그래밍에서는 출력할 페이지 개수를 설정하는데 이용
//클라이언트 프로그래밍에서는 읽어야할 페이지 개수를 설정하는데 이용
int pageCnt = (int)((double)total_count / perPage + (double)(perPage -1)/perPage);
System.out.println(pageCnt);
문제
int imageCnt = 10;
//위의 숫자를 이용하여
//image1.png, image2.png, image3.png..., image10.png를 출력
4. 전체 페이지를 순회하면서 문자열을 전부 읽어오기
=> 페이지별로 읽어서 파싱해도 됨
=> 웹 서버에서 읽어올 때는 파라미터 구성을 잘 파악하고 해야 함
- 다른 파라미터를 추가할 경우 &로 구분
- "https://dapi.kakao.com/v3/search/book?
target=title&query=검색어&page=페이지번호&size=페이지당데이터개수"
=> 전체 페이지 개수를 가지고 페이지번호를 변경해가면서 데이터를 읽어야 함
1) 검색어를 저장하는 변수 선언을 가장 상단으로 이동(String keyword = null;)
=> 입력받은 검색어를 다른 영역에서도 사용해야 하기 때문
2) 페이지별로 읽어서 파싱한 후 List에 저장하기
=> title, url, price를 가져와서 저장하기
//데이터를 저장할 변수
List<Map<String, Object>> list = new ArrayList<>();
try {
//반복하여 데이터를 가져와서 파싱
for(int i=1; i<=pageCnt; i+=1) {
//예외 발생시 다음작업을 수행하도록 하는 경우 - 반복문 내부에 예외처리 구문추가
//웹의 데이터를 수집하는 경우 이런방식으로 처리하지 않을시
//첫 조회시에는 있었으나 수집시 없어지면 예외가 발생하여 데이터 수집이 안되는 경우 발생
try {
//다운로드 받을 URL 만들기 - 파라미터에 한글이 있는지 확인
String addr = "https://dapi.kakao.com/v3/search/book?target=title"
+ "&query=" + keyword + "&page=" + i + "&size=" + perPage;
URL url = new URL(addr);
//연결객체 만들고 옵션 설정
//문자열을 읽어서 출력해보고 한글이 깨지는 지 확인
//헤더 설정을 해야하는지 확인
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(30000);
con.setUseCaches(false);
con.addRequestProperty("Authorization", "KakaoAK f9e2c5d96fd9f8f18c1757ee858596ec");
//문자열 가져오기
String imsiString = null;
StringBuilder sb = new StringBuilder();
//읽어온 데이터의 한글이 깨질때 utf-8을 euc-kr로 변경
BufferedReader br = new BufferedReader(
new InputStreamReader(con.getInputStream(), "utf-8"));
while(true) {
String line = br.readLine();
if(line == null)
break;
sb.append(line + "\n");
}
imsiString = sb.toString();
System.out.println(imsiString);
br.close();
con.disconnect();
//읽어온 문자열을 JSONObject로 변경
JSONObject obj = new JSONObject(imsiString);
//documents 속성의 내용을 배열로 가져오기
JSONArray documents = obj.getJSONArray("documents");
//배열 순회
for(int j=0; j<documents.length(); j+=1) {
//배열을 순회하면서 데이터 가져오기
JSONObject document = documents.getJSONObject(i);
//객체에서 데이터 읽어오기
String title = document.getString("title");
String urlStr = document.getString("url");
int price = document.getInt("price");
//Map으로 위의 데이터 묶기
Map<String, Object> map = new HashMap<String, Object>();
map.put("title", title);
map.put("url", urlStr);
map.put("price", price);
//list에 추가
list.add(map);
//가져온 데이터 출력
for(Map<String, Object> map2 : list) {
System.out.println(map2.get("title"));
System.out.println(map2.get("price"));
}
}
}catch (Exception e) {
System.err.println("실패시 다음페이지로 이동");
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}catch (Exception e) {
System.err.println("페이지 별로 가져오기 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
**데이터 사용
=> BackEnd에서는 csv, json, xml 데이터를 만들어서 넘겨주는 것이 중요
=> FrontEnd에서는 csv, json, xml 데이터를 파싱해서 출력하는 것이 중요
=> SmartPhone App도 대부분 FrontEnd
- 이 부분을 구현하지 못하면 대다수의 SmartPhone App 구현 불가
1) Web Data Parsing
=> 웹 서버에서 데이터를 문자열로 받아옴(파라미터, 결과 문자열의 한글 깨짐에 유의)
=> 문자열을 읽어서 파싱해서 자료구조(DTO의 List나 Map의 List)에 저장
- 데이터의 구성을 확인
=> Local에 저장하여 출력하거나 바로 출력
'수업 정리' 카테고리의 다른 글
35일차 수업정리(HTML Parsing, selenium) (0) | 2020.05.27 |
---|---|
34일차 수업 정리(XML Parsing, HTML) (0) | 2020.05.26 |
33일차 수업 정리(Json Parsing) (0) | 2020.05.25 |
32일차 수업정리(Maven, Data Parsing) (0) | 2020.05.23 |
31일차 수업 정리(MongoDB- Connect) (0) | 2020.05.21 |