코딩기록
230608 [Spring BOOT] External Library (디테일), Jsoup(크롤링), 본문
23_Editor (detail + bHit)
BoardController
@GetMapping(value="/detail.do")
public ModelAndView detail(@RequestParam String idx) {
return service.detail(idx);
}
BoardService
@Transactional 을 사용하면 하나의 요청에 두개의 매서드를 보낼 때 두개의 매서드가 서로 관련이 있으면 원자성(all or nothing) 으로 전부 되거나 하나가 안되면 실행이 안되야 한다.
디테일 페이지에 들어가려는데 오류가 났는데, 조회수가 하나 올라가는 현상이 발생하는걸 방지하기 위해 Transactional을 사용한다.
// 어떤 예외 할건지, 아이솔레이션 을 어떤걸로 해줄건지 근데 얘 하나만 걸어주면 된다???
@Transactional
public ModelAndView detail(String idx) {
dao.upHit(idx);
BoardDTO dto = dao.detail(idx);
ModelAndView mav = new ModelAndView("detail");
mav.addObject("bbs", dto);
return mav;
}
BoardDAO
void upHit(String idx);
BoardDTO detail(String idx);
mapper
<update id="upHit">
UPDATE editor SET bHit = bHit+1
WHERE idx = #{idx}
</update>
<select id="detail" resultType="board">
SELECT * FROM editor
WHERE idx = #{idx}
</select>
detail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Basic Editor</title>
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<link rel="stylesheet" href="/richtexteditor/res/style.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<style>
table{
magin-left : 45px;
}
table, th, td{
border : 1px solid lightgray;
border-collapse: collapse;
}
#content{
display:none;
}
</style>
</head>
<body>
<table>
<tr>
<td>${bbs.user_name}</td>
</tr>
<tr>
<td>${bbs.subject}</td>
</tr>
<tr>
<td>
<div id="div_editor"></div>
</td>
</tr>
<tr>
<td>
<button onclick="location.href=/list.do">
리스트
</button>
</td>
</tr>
</table>
<div id="content">${bbs.content}</div>
</body>
<script>
var config = {};
config.editorResizeMode = "none"; // 에디터 크기 조절 : none
// 상세보기에서는 필요한 툴바만 노출할 예정(html 저장, 출력, pdf 저장, 코드보기)
config.toolbar="simple";
config.toolbar_simple="{save, print, html2pdf, code}"
var editor = new RichTextEditor('#div_editor',config);
editor.setHTMLCode($('#content').html()); // editor 에 내용 넣기
editor.setReadOnly();
</script>
</html>
결과 :
리스트에서 제목 클릭 후 디테일 들어갔을 때
detail.jsp 에서 상세보기에서는 필요한 툴바만 노출해 줬기 때문에 save, print, html2pdf, code 이 4개의 툴바만 나온다.


24_Jsoup
- 외부 사이트나 파일의 HTML 을 가져와 parsing 해 준다.
- 정적 페이지에 적합하다.(스크롤을 내릴 때 마다 덧붙여지는 것은 안된다.)
- 속도가 빠르고 css selector 를 차용하여 원하는 요소를 추출하기 좋다.
- 라이브러리 별도의 프로그램이 없다.
- 프로그램 접근을 막아놓은 사이트에서는 데이터를 가져 올 수 없다.
- jquery 와 비슷하다 (attr...)
기본설정 : jsp,jstl
maven 라이브러리
pom.xml 에 붙여넣기

<!-- jsoup -->
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
크롤링 해와서 원하는 요소 만 가져오는 법
네이버 해외 야구 + itworld
home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<style></style>
</head>
<body>
<p>
<a href="connect.do" target="_blank">
특정사이트 연결(네이버 해외야구)
</a>
</p>
<p>
<a href="getElem.do" target="_blank">
특정 요소만 가져오기(itworld)
</a>
</p>
</body>
<script></script>
</html>
JsoupController
@Controller
public class JsoupController {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired JsoupService service;
@GetMapping(value="/")
public String home() {
return "home";
}
@GetMapping(value="/connect.do")
public String connect(Model model) throws IOException {
// 기본 URL
String url = "https://sports.news.naver.com/wbaseball/record/index";
// 추가 파라메터
HashMap<String, String> params = new HashMap<String, String>();
params.put("category", "mlb");
params.put("year", "2023");
params.put("league", "NL");
Document doc = Jsoup.connect(url).data(params).get();
logger.info("doc : "+doc);
model.addAttribute("elem", doc);
model.addAttribute("full", true);
return "result";
}
@GetMapping(value="/getElem.do")
public ModelAndView getElem() throws Exception {
Document doc = Jsoup.connect("https://www.itworld.co.kr/howto").data("page","1").get();
return service.getElem(doc);
}
}
JsoupService
@Service
public class JsoupService {
Logger logger = LoggerFactory.getLogger(getClass());
// 크롤링은 쉽지만 원하는걸 솎아내올 때 힘들다.
public ModelAndView getElem(Document doc) {
ModelAndView mav = new ModelAndView("result");
// 요소를 가져오는 방식
// 1. js 방식 : doc.getElementById("test");
// 2. css 방식 : doc.select("#test");
// div 중에서 node-list 한칸 뛰면(div .node-list) div 자식요소 중 node-list가 됨
// 노드리스트를 가져올 때 = 하나의 리스트
Elements elems = doc.select("div.node-list");
// 따라서 size = 1
logger.info("elems size : "+elems.size());
// 한개만 가져옴(0번째) - 하위 항목을 가져옴
Element elem = elems.get(0);
logger.info("elem : "+elem); // node-list
Elements cardList = elem.select("div.card");// div중 card인것
String url = "";
String title = "";
String content = "";
ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String,String>>();
for (Element card : cardList) { // cardList에서 하나씩
HashMap<String, String> map = new HashMap<String, String>();
// h5 에 card-title 하위에 a 태그 속성, 어떤속성? href 라는 속성 가져오기
// url = card.select("h5.card-title a").attr("href");
// h5중에 card-title 의 하위요소중 a 의 주소
map.put("url", "https://www.itworld.co.kr" + card.select("h5.card-title a").attr("href"));
// h5중에 card-title 의 하위요소중 a 의 html값
map.put("title", card.select("h5.card-title a").html());
// How-To : 파일볼트 복구 키를 잊었을 때 대처하는 방법 : /howto/293239 (제목과 url)
map.put("content", card.select("p.card-text.crop-text-2").html());
list.add(map);
}
mav.addObject("elem", list);
mav.addObject("full", false);
return mav;
}
}
result.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<style></style>
</head>
<body>
<c:if test="${full}">${elem}</c:if>
<c:if test="${!full}">
<c:forEach items="${elem}" var="item">
<ul>
<li class="title"><a href="${item.url}">${item.title}</a></li>
<li>${item.content}</li>
</ul>
</c:forEach>
</c:if>
</body>
<script></script>
</html>

25_Selenium
- 본래는 웹 사이트 테스트 도구(일련의 과정을 자동으로 해 주는 매크로 개념)
- 웹 페이지 접근 시 특정한 순서가 있는 경우 유용하다.
- 속도는 Jsoup 에 비해 느린편 이며 사용 브라우저의 드라이버가 필요 하다.
하려면 크롬 브라우저 드라이버 다운로드
https://chromedriver.chromium.org/downloads
ChromeDriver - WebDriver for Chrome - Downloads
Current Releases If you are using Chrome version 114, please download ChromeDriver 114.0.5735.90 If you are using Chrome version 113, please download ChromeDriver 113.0.5672.63 If you are using Chrome version 112, please download ChromeDriver 112.0.5615.49
chromedriver.chromium.org
gdj63 에 붙여넣기 (라이센스는 무시)
maven 가서 라이브러리 가져오기

4.4.0
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.4.0</version>
</dependency>
'study' 카테고리의 다른 글
| 230607 [Spring BOOT] 암호화, External Library(로그인 까지) (0) | 2023.06.07 |
|---|---|
| 230605 [Spring BOOT] build, war&jar , 암호화, (0) | 2023.06.05 |
| 230601 [Spring BOOT] 14 이어서 (0) | 2023.06.02 |
| 230601 [Spring BOOT] 13_Login - Properties (0) | 2023.06.01 |
| 230531 [Spring BOOT] 12_api (0) | 2023.05.31 |