반응형

# Jsoup API 활용 (Jsoup API 이용한 웹 페이지 크롤링2)

## 웹 페이지 클롤링 하기 (HTML Parsing)

 

실시간 속보 : 네이버 금융

관심종목의 실시간 주가를 가장 빠르게 확인하는 곳

finance.naver.com

  • 위 홈페이지에서 실시간 속보 가져오기.
  • 구현 로직
import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import org.jsoup.nodes.*;

public class Project02_B {
	public static void main(String[] args) {
		
		String url = "https://finance.naver.com/news/news_list.naver?mode=LSS2D&section_id=101";
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		try {
			System.out.println("[입력 : 숫자 입력] : ");
			String sectionId = br.readLine();		
			url += url + "&section_id2=" + sectionId;
			
			System.out.println("================================");
			Document doc = Jsoup.connect(url).post();
			Element subTitle =  doc.select(".sub_tlt").first();
			System.out.println(subTitle.text());
			
			System.out.println("================================");
			Elements newsList =  doc.select(".newsList .articleSubject");
			for (Element dd : newsList) {
				System.out.println(dd.text());
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
	}
}
  • 출력 결과
[입력 : 숫자 입력] : 
258
================================
실시간 속보
================================
메디콕스, 호주 그린파머스와 대마 원료 수입독점계약 체결
[속보] 코스피, 2400선 재차 붕괴
삼성증권, 2년만에 '전국 동시 투자설명회' 재개
[ET라씨로] '누리호 발사 D-1'…AP 위성 등 관련주 강세
코스피, 장중 2400선 붕괴…19개월 만에 최저치
코스피·코스닥 또 연저점 경신
[fn마켓워치] 바이와알이 韓신임 대표에 배양호 前한국수력원자력 재생에너지처장 선임
마지노선 무너진 비트·이더…큰손들도 “추가적인 고통 대비”
개장 직후 하락 전환한 코스피, 원/달러 환율은 상승
개장 직후 하락 전환한 코스피·코스닥지수, 원/달러 환율은 상승
[시그널] E&F PE, 폐기물 처리업계 강자로 '우뚝'
[속보] 코스피 장중 2400선 재차 붕괴…2391선
KB證, 서울옥션과 하반기 투자전략·미술투자 세미나
[특징주] 바이오플러스, 미국·중국 필러 시장 진출 기대감… 4%↑
경기 침체 우려에 코스피 연중 최저치 '뚝'…2391.27
[장중시황] 코스피, 하루 만에 2400선 다시 붕괴…외인 '팔자' 낙폭 커져
[속보] 코스피, 장중 2400선 붕괴
[증시신상품] 신한자산운용, S&P500 월배당 ETF
와이즈에이아이, 일본 기업과 'AI 플랫폼' 수출 협약
[속보] 코스피, 장중 2400선 재차 붕괴
반응형
반응형

# Jsoup API 활용 (Jsoup API 활용한 웹 페이지 Crawling(크롤링))

## Jsoup API

  • Java HTML Parser.
  • 사용법이 간단하고 인기 많은 파서. ( 자바에서 html 을 파싱할 수 있도록 도와주는 라이브러리 )
  • HTML 에 포함된 특정 단어를 분리하기 위해 사용하는 자바 라이브러리로 데이터 추출, 가공 등에 효율적. (CSS, DOM 객체 등 핸들링에도 사용, 제이쿼리와 유사한 성질의 API)
  • https://jsoup.org 
 

jsoup: Java HTML parser, built for HTML editing, cleaning, scraping, and XSS safety

jsoup: Java HTML Parser jsoup is a Java library for working with real-world HTML. It provides a very convenient API for fetching URLs and extracting and manipulating data, using the best of HTML5 DOM methods and CSS selectors. jsoup implements the WHATWG H

jsoup.org

 

Maven Repository: jsoup

jsoup is a Java library for working with real-world HTML. It provides a very convenient API for fetching URLs and extracting and manipulating data, using the best of HTML5 DOM methods and CSS selectors. jsoup implements the WHATWG HTML5 specification, and

mvnrepository.com

## Jsoup 의 주요 요소

  1. Document (Document Object Model) : Jsoup 얻어온 결과 HTML 전체 문서 
  2. Element (=Node) : Document 의 HTML 요소
  3. Elements : Element 가 모인 자료형으로 반복문 사용 가능. (for, while 등)
  4. Connection : Jsoup 의 connect 혹은 설정 메소드들을 이용하여 만들어지는 객체로 연결을 하기 위한 정보를 담고 있다.
  5. Response : Jsoup 가 URL 에 접속해 얻어온 결과로 Document 와 다르게 status 코드, status 메시지, charset 등 헤더 메시지와 쿠키 등을 갖고 있다.

## Jsoup API 이용한 크롤링

  • Jsoup 이용하여 네이버 스포츠 크롤링.
  • 크롬 등 에서 개발자 도구를 이용하여 해당 HTML 의 트리구조 확인. (원하는 부분의 데이터를 가져오기 위해 확인 필요)
  • 구현 로직.
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Project02_A {
	public static void main(String[] args) {
		
		// 크롤링 할 사이트 주소.
		String url = "https://sports.news.naver.com/wfootball/index.nhn";
		
		Document doc = null;
		
		try {
			doc = Jsoup.connect(url).get();
		} catch (Exception e) {
			System.out.println(e);
		}
		
		// 1. 주요 뉴스로 나오는 태그 찾아서 가져옴.
		Elements element = doc.select("div.home_news");
		
		// 2. 헤더 제목 가져오기.
		String title = element.select("h2").text().substring(0, 4);
		
		System.out.println("====================================");
		System.out.println("헤더 제목 : " + title);
		System.out.println("====================================");
		
		// 3. for문 이용 하위 뉴스 기사들 출력
		for (Element el : element.select("li")) {
			System.out.println(el.text());
		}
		
		System.out.println("====================================");
		
		
	}
}
  • 출력 결과
====================================
헤더 제목 : 추천뉴스
====================================
벤투호 주전 공격수 3인 모두 EPL서 뛰나
토트넘 '폭풍영입'…오히려 위기될 수도?
세계에서 가장 아름다운 축구 선수 "호날두 광팬...첼시 입단이 꿈"
리버풀 마네 대체자 찾았다... 레알서 '커리어 하이' 찍은 공격수
램파드, 콘테 체제 '계륵 MF' 데려간다...토트넘, 320억 가격표 부착
'맨시티는 제2의 도르트문트' 레알, 2024년에 홀란드 영입한다...이적료 2059억
'나이스 원 쏘니' 손흥민, 1017억 원 'LW 세계 3위' 평가!
'이 정도면 복덩이' SON 위상 재확인 "토트넘에 무해할 존재 "
텐 하흐 감독이 가장 원하는 2명, 맨유 유니폼 입나?
맨체스터 지역지 '홀란드, 펩 과르디올라의 새로운 메시 될 것'
사생활 지적에 네이마르 "쉴 땐 쉬어야지" 하소연
나폴리·세비야 같은 명가들, 왜 ‘괴물 수비수’ 원할까
재계약 다섯 달 만에 '충격'…바르사 '계약해지' 초강수
'붉은색+푸른색' 심장 가진 '런던의 사나이', 새로운 도전에 나선다
'연봉 336억 끝내 불발…' 지단, PSG 안 간다 (佛 RMC)
레알, 이적료 2035억으로 홀란드 뺏어온다
'WC 끝으로 대표팀 은퇴?' 네이마르, 동료에게 말했다
'포그바-린가드?' 맨유 신입 CEO의 저격, "라커룸 밀고자 2명 팀 떠났다"
베일·아자르에 가렸던 '먹튀'... 결국 레알 떠나 '2번째 임대'
메시, 음바페에 이어 세 번째…파리 생제르맹 '골든보이 출신' MF 영입
====================================

 

반응형
반응형

# JSON API 활용 (네이버 클라우드 플랫폼 지도 API 이용 지도 GUI 생성)

## 네이버 클라우드 플랫폼 지도 API 활용 (위도, 경도 추출하여 지도 GUI 생성)

  • 주소 입력받은 후 버튼 클릭 시 입력된 주소에 대한 이미지 및 주소 표시.
  • 자바 스윙(javax swing) 이용 지도 GUI 생성.

1. AddressVO

package kr.inflearn;

public class AddressVO {
	private String roadAddress;
	private String jibunAddress;
	private String x;
	private String y;
	
	public AddressVO() {	}

	public AddressVO(String roadAddress, String jibunAddress, String x, String y) {
		super();
		this.roadAddress = roadAddress;
		this.jibunAddress = jibunAddress;
		this.x = x;
		this.y = y;
	}

	public String getRoadAddress() {
		return roadAddress;
	}

	public void setRoadAddress(String roadAddress) {
		this.roadAddress = roadAddress;
	}

	public String getJibunAddress() {
		return jibunAddress;
	}

	public void setJibunAddress(String jibunAddress) {
		this.jibunAddress = jibunAddress;
	}

	public String getX() {
		return x;
	}

	public void setX(String x) {
		this.x = x;
	}

	public String getY() {
		return y;
	}

	public void setY(String y) {
		this.y = y;
	}

	@Override
	public String toString() {
		return "AddressVO [roadAddress=" + roadAddress + ", jibunAddress=" + jibunAddress + ", x=" + x + ", y=" + y
				+ "]";
	}
	
	
}

2. 메인 소스

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class Project01_F {
	JTextField address;
	JLabel resAddress, resX, resY, jibunAddress;
	JLabel imageLabel;
	
	public void initGUI() {
		JFrame frm = new JFrame("Map View");                    // 프레임 생성
		frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     // 프레임의 X 클릭 시 종료.
		Container c = frm.getContentPane();                     // JFrame 안쪽 영역.
		
		imageLabel = new JLabel("지도보기");                    // JFrame 안쪽 영역 상단에 들어갈 지도보기
		JPanel pan = new JPanel();
		JLabel addressLbl = new JLabel("주소입력");             // JFrame 안쪽 영역 상단에 들어갈 주소입력
		address = new JTextField(50);
		JButton btn = new JButton("클릭");                      // JFrame 안쪽 영역에 들어갈 클릭 버튼
		pan.add(addressLbl);
		pan.add(address);
		pan.add(btn);	
		btn.addActionListener(new NaverMap(this));              // pan에 생성한 버튼(btn) 클릭 시 처리하는 이벤트 핸들러.
		
		JPanel pan1 = new JPanel();
		pan1.setLayout(new GridLayout(4, 1));                   // 지도 하단 그리드 4행 1열로 생성.
		resAddress = new JLabel("도로명");                      // 그리드 1행에 들어갈 도로명
		jibunAddress = new JLabel("지번주소");                  // 그리드 2행에 들어갈 지번주소
		resX = new JLabel("경도");                              // 그리드 3행에 들어갈 경도
		resY = new JLabel("위도");                              // 그리드 4행에 들어갈 위도
		pan1.add(resAddress);
		pan1.add(jibunAddress);
		pan1.add(resX);
		pan1.add(resY);
		
		c.add(BorderLayout.NORTH, pan);                         // 상단 pan 세팅
		c.add(BorderLayout.CENTER, imageLabel);                 // 센터 imageLabel 세팅
		c.add(BorderLayout.SOUTH, pan1);                        // 하단 pan1 세팅
		
		frm.setSize(730, 660);
		frm.setVisible(true);
		
	}
	
	public static void main(String[] args) {
		new Project01_F().initGUI();	
	}
}

3. NaverMap

  • 이벤트 발생 시 처리해주는 함수 생성.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;

import javax.swing.ImageIcon;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;

import kr.inflearn.AddressVO;

public class NaverMap implements ActionListener {

	Project01_F naverMap;
	public NaverMap(Project01_F naverMap) {
		this.naverMap = naverMap;
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		String clientId = "앱 등록 시 발급받은 Client ID";
		String clientSecret = "앱 등록 시 발급 받은 Client Secret";
		AddressVO vo = null;
		
		try {
			String address = naverMap.address.getText();
			String addr = URLEncoder.encode(address, "UTF-8");
			String apiURL = "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query=" + addr;
			URL url = new URL(apiURL);
			
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setRequestMethod("GET");
			con.setRequestProperty("X-NCP-APIGW-API-KEY-ID", clientId);
			con.setRequestProperty("X-NCP-APIGW-API-KEY", clientSecret);
			
			int responseCode = con.getResponseCode();
			BufferedReader br;
			if (responseCode == 200) {
				br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
			} else {
				br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
			}
			
			String inputLine;
			StringBuffer response = new StringBuffer();
			while ((inputLine = br.readLine()) != null) {
				response.append(inputLine);
			}
			br.close();
			
			JSONTokener tokener = new JSONTokener(response.toString());
			JSONObject object = new JSONObject(tokener);
			System.out.println(object);
			
			JSONArray arr = object.getJSONArray("addresses");
			for (int i = 0; i < arr.length(); i++) {
				JSONObject temp = (JSONObject) arr.get(i);
				vo = new AddressVO();
				vo.setRoadAddress((String) temp.get("roadAddress"));
				vo.setJibunAddress((String)temp.get("jibunAddress"));
				vo.setX((String)temp.get("x"));
				vo.setY((String)temp.get("y"));
				System.out.println(vo);
			}
			
			map_service(vo);

		} catch (Exception err) {
			System.out.println(err);
		}
	}
	
	public void map_service(AddressVO vo) {
		String URL_STATICMAP = "https://naveropenapi.apigw.ntruss.com/map-static/v2/raster?";
		
		try {
			String pos = URLEncoder.encode(vo.getX() + " " + vo.getY(), "UTF-8");
			URL_STATICMAP += "center=" + vo.getX() + "," + vo.getY();
			URL_STATICMAP += "&level=16&w=700&h=500";
			URL_STATICMAP += "&markers=type:t|size:mid|pos:" + pos + "|label:" + URLEncoder.encode(vo.getRoadAddress(), "UTF-8");
			
			URL url = new URL(URL_STATICMAP);
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setRequestMethod("GET");
			con.setRequestProperty("X-NCP-APIGW-API-KEY-ID", "앱 등록 시 발급받은 Client ID");
			con.setRequestProperty("X-NCP-APIGW-API-KEY", "앱 등록 시 발급 받은 Client Secret");
			
			int responseCode = con.getResponseCode();
			BufferedReader br;
			
			// 정상호출인 경우.
			if (responseCode == 200) {
				InputStream is = con.getInputStream();
				
				int read = 0;
				byte[] bytes = new byte[1024];
				
				// 랜덤 파일명으로 파일 생성
				String tempName = Long.valueOf(new Date().getTime()).toString();
				File file = new File(tempName + ".jpg");	// 파일 생성.
				
				file.createNewFile();
				
				OutputStream out = new FileOutputStream(file);
				
				while ((read = is.read(bytes)) != -1) {
					out.write(bytes, 0, read);	// 파일 작성
				}
				
				is.close();
				ImageIcon img = new ImageIcon(file.getName());
				naverMap.imageLabel.setIcon(img);
				naverMap.resAddress.setText(vo.getRoadAddress());
				naverMap.jibunAddress.setText(vo.getJibunAddress());
				naverMap.resX.setText(vo.getX());
				naverMap.resY.setText(vo.getY());
				
			} else {
				System.out.println(responseCode);
			}
			
		} catch(Exception e) {
			System.out.println(e);
		}
		
	}
}

## 결과물

  • 아래 화면에서 주소 입력 후 버튼 클릭.

  • 입력된 주소에 대한 이미지  생성, 하단에 주소 표시.

반응형
반응형

# JSON API 활용 (네이버 클라우드 플랫폼 지도 API 이용 지도 이미지 생성)

## 네이버 클라우드 플랫폼 지도 API 활용 (위도, 경도 추출하여 지도 이미지 생성)

1. Static Map OpenAPI

 

Static Map 개요 - Static Map

 

api.ncloud-docs.com

2. 구현 (Static Map OpenAPI 사용)

 

  • Geocoding OpenAPI 사용하여 구현했던 작업물에 Static Map OpenAPI 사용하여 구현 진행.
  • 파일로 생성하도록 작업.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;

public class Project01_E {
	public static void main(String[] args) {
		// 주소 입력 -> 위도, 경도 좌표 추출.
		BufferedReader io = new BufferedReader(new InputStreamReader(System.in));	
		String clientId = "8nuzd8r4bi";
		String clientSecret = "gyCPtld0Wnpi2eYr6xsBi4ITu3OnD9trzLa5JM9p";
		
		try {
			System.out.println("주소를 입력해주세요 : ");
			
			String address = io.readLine();
			String addr = URLEncoder.encode(address, "UTF-8");
			
			// Geocoding 개요에 나와있는 API URL 입력.
			String apiURL = "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query=" + addr;	// JSON
			
			URL url = new URL(apiURL);
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setRequestMethod("GET");
			
			// Geocoding 개요에 나와있는 요청 헤더 입력.
			con.setRequestProperty("X-NCP-APIGW-API-KEY-ID", clientId);
			con.setRequestProperty("X-NCP-APIGW-API-KEY", clientSecret);
			
			// 요청 결과 확인. 정상 호출인 경우 200
			int responseCode = con.getResponseCode();
			
			BufferedReader br;
			
			if (responseCode == 200) {
				br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
			} else {
				br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
			}
			
			String inputLine;
			
			StringBuffer response = new StringBuffer();
			
			String x = "";	// 경도
			String y = "";	// 위도
			String z = "";	// 주소
			
			while ((inputLine = br.readLine()) != null) {
				response.append(inputLine);
			}
			
			br.close();
			
			JSONTokener tokener = new JSONTokener(response.toString());
			JSONObject object = new JSONObject(tokener);			
			JSONArray arr = object.getJSONArray("addresses");
			
			for (int i = 0; i < arr.length(); i++) {
				JSONObject temp = (JSONObject) arr.get(i);
				System.out.println("address : " + temp.get("roadAddress"));
				System.out.println("jibunAddress : " + temp.get("jibunAddress"));
				System.out.println("위도 : " + temp.get("y"));
				System.out.println("경도 : " + temp.get("x"));
				
				x = (String) temp.get("x");				// 경도
				y = (String) temp.get("y");				// 위도
				z = (String) temp.get("roadAddress");	// 주소
			}
			
			// Static Map OpenAPI
			map_service(x, y, z);
		
		} catch (Exception  e) {
			System.out.println(e);
		}
	}

	// 지도이미지 생성 메서드.
	public static void map_service(String point_x, String point_y, String address) {
		String URL_STATICMAP = "https://naveropenapi.apigw.ntruss.com/map-static/v2/raster?";
		
		try {
			String pos = URLEncoder.encode(point_x + " " + point_y, "UTF-8");
			String url = URL_STATICMAP;
			
			url += "center=" + point_x + "," + point_y;
			url += "&level=16&w=700&h=500";
			url += "&markers=type:t|size:mid|pos:" + pos + "|label:" + URLEncoder.encode(address, "UTF-8");
			
			URL u = new URL(url);
			HttpURLConnection con = (HttpURLConnection) u.openConnection();
			con.setRequestMethod("GET");
			con.setRequestProperty("X-NCP-APIGW-API-KEY-ID", "앱 등록 시 발급받은 Client ID");
			con.setRequestProperty("X-NCP-APIGW-API-KEY", "앱 등록 시 발급 받은 Client Secret");
			
			int responseCode = con.getResponseCode();
			BufferedReader br;
			
			// 정상호출인 경우.
			if (responseCode == 200) {
				InputStream is = con.getInputStream();
				
				int read = 0;
				byte[] bytes = new byte[1024];
				
				// 랜덤 파일명으로 파일 생성
				String tempName = Long.valueOf(new Date().getTime()).toString();
				File file = new File(tempName + ".jpg");	// 파일 생성.
				
				file.createNewFile();
				
				OutputStream out = new FileOutputStream(file);
				
				while ((read = is.read(bytes)) != -1) {
					out.write(bytes, 0, read);	// 파일 작성
				}
				
				is.close();
				
			} else {	// 에러 발생
				br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
				String inputLine;
				StringBuffer response = new StringBuffer();
				
				while ((inputLine = br.readLine()) != null) {
					response.append(inputLine);
				}
				br.close();
			}
			
		} catch(Exception e) {
			System.out.println(e);
		}
		
	}
}

 

 

반응형

+ Recent posts