Document (Document Object Model) : Jsoup 얻어온 결과 HTML 전체 문서
Element (=Node) : Document 의 HTML 요소
Elements : Element 가 모인 자료형으로 반복문 사용 가능. (for, while 등)
Connection : Jsoup 의 connect 혹은 설정 메소드들을 이용하여 만들어지는 객체로 연결을 하기 위한 정보를 담고 있다.
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 영입
====================================
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);
}
}
}