**XML Parsing
=> 태그를 이용해서 데이터를 표현하는 포맷
<시작태그 속성=값 속성=값 /> : 빈 태그 - 내용이 없는 태그
- 속성=값은 생략될 수 있음
ex) <age>50</age> : 우리나라
<age value=50/> : google이 선호하는 방식
ex2) 이름, 나이, 주소를 저장
<person>
<name>최성권</name>
<age>30</age>
<address>경기도 시흥시</address>
</person>
1. XML의 용도
=> 실시간으로 변하는 데이터를 전송하는 RSS에 많이 사용, 프로그램의 설정 파일에도 많이 사용
2. 경향신문 스포츠 기사 rss에서 title과 link태그의 내용을 전부 가져오기
=> 전부 가져와서 MySQL에 저장
=> http://www.khan.co.kr/rss/rssdata/kh_sports.xml
1) 웹에서 텍스트 다운받기
=> 웹에서 다운로드 받는 경우 파라미터를 호가인하고 파라미터에 한글이 포함되는지 확인
- 파라미터에 한글이 포함되어 있다면 utf-8로 인코딩
- java.net.URLEncode.encode(인코딩할 문자열, "utf-8")을 호출하면 인코딩 된 문자열을 리턴 받을 수 있음
=> header를 필요로 하는지 확인
- header를 필요로 한다면 HttpURLConnection 객체를 가지고 addProperty를 호출하여 header 값 대입
- Open API에서 헤더를 설정해야 하는 경우가 많음
=> 다운로드 받은 결과에 한글이 포함된 경우 한글이 깨졌는지 확인
- 한글이 깨진경우 BufferedReader 만드는 문장을 수정
- new BufferedReader(new InputStreamReader(connection.getInputStream(), "인코딩방식")))
2) XML Parsing
=> 방법은 DOM Parser와 SAX Parser
=> DOM(Document Object Model) Parser : 태그의 내용을 메모리에 전부 펼쳐두고 원하는 태그를 찾는 방식
- 메모리에 전부 펼치기 때문에 메모리 사용량은 증가하지만 속도가 빠름
=> SAX Parser : 태그의 내용을 부분부분 읽어가면서 파싱하는 방식
- 부분적으로 파싱하기 때문에 메모리 사용량은 적지만 속도는 DOM보다 느림
=> 대다수의 프로그래밍 언어가 2가지 방법 모두 제공
=> DOM Parser
// 파싱을 수행해주는 DocumentBuilder 인스턴스를 생성하기 위한 DocumentBuilderFactory 인스턴스 생성
- DocumentBuilderFactory 팩토리 = DocumentBuilderFactory.newInstance();
// 팩토리 인스턴스를 이용해서 DocumentBuilder 인스턴스를 생성
- DocumentBuilder 빌더 = 팩토리.newDocumentBuilder();
// 파싱을 수행 - 메로리에 펼침
- Document 도큐먼트 = 빌더.parse(new ByteArrayInputStream(xml문자열.getBytes()));
//루트를 찾기
- Element 루트엘리먼트 = 도큐먼트.getDocumentElement();
// 원하는 태그를 찾아오기
- NodeList 노드리스트 = 루트엘리멘트.getElementsByTagName(String tag);
// 노드리스트 순회
for(int i=0; i<노드리스트.getLength(); i+=1){
//하나의 태그를 가져옴
Node 태그 = 노드리스트.get(i);
Node 자식 = 태그.getFirstChild();
String 내용 = 자식.getNodeValue();
}
3) 스마트폰 애플리케이션이라면 이미 다운받은 경우 다시 받지 않도록 해주는 것이 중요
=> 중복된 데이터를 다운로드 받기 때문에 불필요한 트래픽이 증가
=> 스마트폰은 네트워크가 불안정하므로 언제 네트워크가 해제될지 예측 불가(불필요한 다운로드 최소화)
=> 되도록 다운로드 받은 데이터를 로컬에 저장해두고 데이터가 변경된 경우에만 다시 다운로드 받게 제작
- 변경여부 판단 방법 : 데이터를 가져온 날짜, 시간을 파일에 기록해두고, 서버의 업데이트 날짜와 비교
3. 기상청 XML 정보를 가져와서 파싱한 후 데이터베이스에 저장
=> 스레드를 이용
1) Java에서 스레드 생성 및 시작(비동기 수행 - 순서대로 수행되지 않음)
=> Thread 클래스로부터 상속 받는 클래스를 만든 후 public void run 메소드에 스레드로 수행할 동작을 작성
- 인스턴스 생성 후, Start() 메소드 호출
=> Runnable 인터페이스를 구현한 클래스를 만들 후 public void run 메소드에 스레드로 수행할 동작을 작성
- 인스턴스 생성 후 Thread 클래스의 생성자에 대입하여 Thread 클래스의 인스턴스를 만들고 start()를 호출
=> 네트워크에서 다운로드 받아 작업을 수행, 업로드하는 코드는 스레드를 이용하는 것을 권장.
- 업, 다운로드는 시간이 오래 걸리는 작업이라 스레드 비사용시 다음 작업이 무한 대기상태에 빠질 수 있음
=> 클래스를 상속, 인터페이스 구현시, 별도 클래스 생성없이 인스턴스를 바로 만들어서 사용가능(anonymous class)
- 구현해야 할 메소드가 1개인 경우 람다 문법으로 표현 가능
=> 람다와 스트림, GUI 프로그래밍은 안드로이드 하기전 자바 복습 겸하여 학습
2) 기상청 RSS URL을 확인하고 데이터 확인
=> url : http://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp?stnId=108
=> 도시별 각 날짜의 날씨, 최저온도, 최고온도를 읽어서 저장
3) 스레드를 만들어서 실행하는 코드를 작성
public static void main(String[] args) {
//Anonymous Class를 이용해서 Thread 클래스로 부터 상속 받는 클래스의 인스턴스 생성
Thread th = new Thread() {
//스레드로 수행할 내용
public void run() {
//run 메소드 안에서 예외 발생시 return 하도록 만들면 thread 중지 가능
try {
}catch (Exception e) {
return;
}
}
};
//스레드 시작
th.start();
}
4) 스레드 메소드 안에 다운로드 받는 코드를 작성
=> 파라미터의 한글여부, header 추가여부, 가져온 문자열의 한글 깨짐을 확인
String weatherString = null;
try {
//URL 만들기
String addr = "http://www.weather.go.kr/weather/forecast/mid-term-rss3.jsp?stnId=108";
URL url = new URL(addr);
//연결 객체 만들기
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(30000);
con.setUseCaches(false);
//스트림을 생성하고 줄 단위로 읽어서 저장하기
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");
}
weatherString = sb.toString();
//연결 해제
br.close();
con.disconnect();
}catch (Exception e) {
System.out.println("다운로드 예외");
System.out.println(e.getMessage());
e.printStackTrace();
}
//데이터 확인
System.out.println(weatherString);
5) 도시이름과 날짜 그리고 날씨, 최고온도, 최저온도 데이터를 가져와서 List에 저장
=> 도시이름 1개에 13개씩 데이터가 존재
=> 도시이름 리스트와 날짜, 날씨 그리고 최고온도, 최저온도 별 리스트를 생성하여 데이터 파싱하여 저장
- 저장 후, 도시명, 날짜, 날씨, 최고온도, 최저온도를 갖는 Map을 만들어 List로 저장
//데이터를 저장할 자료구조 생성
List<Map<String, Object>> list = new ArrayList<>();
// 도시이름 찾아오기
List<String> cities = new ArrayList<String>();
//데이터를 파싱하여 List에 저장
if(weatherString != null && weatherString.trim().length() > 0) {
try {
// XMl 문자열에서 루트 태그를 찾기
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new ByteArrayInputStream(
weatherString.getBytes()));
Element root = document.getDocumentElement();
NodeList cityList = root.getElementsByTagName("city");
for(int i=0; i<cityList.getLength(); i+=1) {
Node node = cityList.item(i);
Node city = node.getFirstChild();
cities.add(city.getNodeValue());
}
//System.out.println(cities);
//날짜, 날씨, 최고온도, 최저온도 가져오기
NodeList dateList = root.getElementsByTagName("tmEf");
NodeList wfList = root.getElementsByTagName("wf");
NodeList tmxList = root.getElementsByTagName("tmx");
NodeList tmnList = root.getElementsByTagName("tmn");
//날짜, 날씨, 최고온도, 최저온도를 저장할 임시 리스트
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
List<String> list3 = new ArrayList<String>();
List<String> list4 = new ArrayList<String>();
for(int i=0; i<dateList.getLength(); i+=1) {
//날짜를 list1에 저장하기
Node node = dateList.item(i);
Node temp = node.getFirstChild();
list1.add(temp.getNodeValue());
//날씨를 list2에 저장하기
//wf만 초기데이터가 1개 더 있어서 하나 뒤의 데이터 가져오기
node = wfList.item(i+1);
temp = node.getFirstChild();
list2.add(temp.getNodeValue());
//최고온도를 list3에 저장하기
node = tmxList.item(i);
temp = node.getFirstChild();
list3.add(temp.getNodeValue());
//최저온도를 list4에 저장하기
node = tmnList.item(i);
temp = node.getFirstChild();
list4.add(temp.getNodeValue());
}
// System.out.println(list1);
// System.out.println(list2);
// System.out.println(list3);
// System.out.println(list4);
//cityList와 list1, list2, list3, list4의 데이터를 모아서 하나의 list로 만들기
//city 1개에 각 데이터 13개씩 존재
for(int i=0; i<cities.size(); i+=1) {
//도시이름 1개 가져오기
String city = cities.get(i);
//도시이름 1개당 13개의 날씨, 날짜, 최고온도, 최저온도 가져오기
for(int j=0; j<13; j+=1) {
String date = list1.get(i*13+j);
String wf = list2.get(i*13+j);
String tmx = list3.get(i*13+j);
String tmn = list4.get(i*13+j);
//맵생성
Map<String, Object> map = new HashMap<String, Object>();
map.put("city", city);
map.put("date", date);
map.put("wf", wf);
map.put("tmx", tmx);
map.put("tmn", tmn);
list.add(map);
}
}
} catch (Exception e) {
System.err.println("xml 파싱 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
}else {
System.out.println("읽어온 데이터가 없습니다.");
}
//System.out.println(list);
for(Map<String, Object> map : list) {
if(map.get("city").equals("서귀포")) {
System.out.println(map);
}
}
6) 위의 데이터베이스에 저장하기 위하여 테이블 생성
=> "city", "date", "wf", "tmx", "tmn" : 전부 문자열
create table weather(
weatherNum int primary key auto_increment,
weatherCity varchar(20),
weatherDate varchar(50),
weatherwf varchar(30),
weathertmx varchar(5),
weathertmn varchar(5)
)default charset=utf8;
7) 프로젝트에서 MySQL이 사용 가능한지 확인
=> MySQL 드라이버가 프로젝트에 포함되어 있는지 확인
8) 읽어온 데이터를 MySQL에 저장
=> MySQL 사용시 "characterEncoding=utf8useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
- 비 사용시 한글을 사용할 수 없음(깨짐)
//MySQL에 저장
try {
//드라이버 클래스 로드
Class.forName("com.mysql.cj.jdbc.Driver");
//데이터베이스 연결
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mysql?useUnicode=true"
+ "&characterEncoding=utf8&serverTimezone=UTC"
+ "&useSSL=false", "root", "900826");
for(Map<String, Object> map : list) {
PreparedStatement pstmt = con.prepareStatement(""
+ "insert into weather(weatherCity, weatherDate, weatherwf, "
+ "weathertmx, weathertmn) values(?,?,?,?,?)");
pstmt.setString(1, (String)map.get("city"));
pstmt.setString(2, (String)map.get("date"));
pstmt.setString(3, (String)map.get("wf"));
pstmt.setString(4, (String)map.get("tmx"));
pstmt.setString(5, (String)map.get("tmn"));
//SQl실행
pstmt.executeUpdate();
pstmt.close();
}
//사용한 객체 정리
con.close();
}catch (Exception e) {
System.err.println("데이터 저장 실패");
System.out.println(e.getMessage());
e.printStackTrace();
}
**HTML Parsing
=> jsoup 라이브러리 이용
=> html : 웹 브라우저에 문서를 출력하기 위한 마크업 언어
- xml과 json은 출력을 위한 포맷이 아니고 데이터 포맷
- 웹 사이트에서 보여지지만 open api로 제공되지 않는 경우 html을 수집하여 원하는 데이터만 사용
=> xml은 구조적이라 태그만 가지고 충분한 데이터 추출 가능
html은 비구조적이라 원하는 데이터를 가져올 때 태그 대신에 여러가지를 이용
1. 용어
=> tag : 문서의 구조를 나타내기 위한 명령어 - 중복될 수 있음
=> id : 자바스크립트에서 사용하기 위해 부여하는 식별자(문서에 1개만 존재)
=> class : CSS에서 동일한 디자인을 적용하기 위해 부여하는 이름(그룹화하여 사용하므로 중복 가능)
=> name : 서버에서 구분하기 위한 이름 - 서버에게 데이터를 전달하는 태그에만 부여(중복 가능)
=> select : HTML 문서내의 객체의 선택을 다양화 하기 위해 만든 문법(중복가능)
- 앞에서부터 순서대로 만들어지므로 앞부분을 생략해도 동일한 객체를 선택가능(중복 가능성 증가)
=> xpath : xml 문서에서 하나의 객체를 가리키기 위한 언어(절대 중복되지 않음(=id))
=> 크롬의 검사기능을 사용하면 원하는 객체의 경로를 알아내는게 쉬움
- 선택자 : 중복되는 경우가 있음(웹 프론트앤드에서는 중요)
#NM_FAVORITE > div.group_nav > ul.list_nav.type_fix > li:nth-child(3) > a
- xpath : 중복되는 경우가 없음
//*[@id="NM_FAVORITE"]/div[1]/ul[1]/li[3]/a
연습 문제
1. 하나의 반복문으로 ar의 모든 요소를 출력
int [][] ar = new int[5][5];
int cnt = 1;
for(int i=0; i<5; i=i+1){
for(int j=0; j<5; j=j+1){
ar[i][j] = cnt;
cnt = cnt + 1;
}
}
//답안
for(int i=0; i<(5*5); i+=1) {
System.out.print(ar[i/5][i%5] + " ");
}
2. k의 모든 내용을 ar에 순서대로 대입해서 출력
int [] k = new int[25];
cnt = 101;
for(int i=0; i<25; i=i+1){
k[i] = cnt;
cnt = cnt + 1;
//답안
ar[i / 5][i % 5] = k[i];
}
//답안 확인
for (int i = 0; i < (5 * 5); i += 1) {
System.out.print(k[i] + " ");
}
'수업 정리' 카테고리의 다른 글
36일차 수업 정리(Web Programming 환경설정, XHTML) (0) | 2020.05.28 |
---|---|
35일차 수업정리(HTML Parsing, selenium) (0) | 2020.05.27 |
33일차 수업 정리(Json Parsing) (0) | 2020.05.26 |
33일차 수업 정리(Json Parsing) (0) | 2020.05.25 |
32일차 수업정리(Maven, Data Parsing) (0) | 2020.05.23 |