Jsoup 실습 : 크롤링해서 로컬DB에 넣기
아래와 같은 간단한 HTML 코드가 있다고 하자. (예시 코드 출처: w3schools)
<div class="row">
<div class="col-md-4">
<div class="thumbnail">
<a href="/w3images/lights.jpg">
<img src="/w3images/lights.jpg" alt="Lights" style="width:100%">
<div class="caption">
<p>Lorem ipsum...</p>
</div>
</a>
</div>
</div>
<div class="col-md-4">
<div class="thumbnail">
<a href="/w3images/nature.jpg">
<img src="/w3images/nature.jpg" alt="Nature" style="width:100%">
<div class="caption">
<p>Lorem ipsum...</p>
</div>
</a>
</div>
</div>
<div class="col-md-4">
<div class="thumbnail">
<a href="/w3images/fjords.jpg">
<img src="/w3images/fjords.jpg" alt="Fjords" style="width:100%">
<div class="caption">
<p>Lorem ipsum...</p>
</div>
</a>
</div>
</div>
</div>
위 코드는 아래와 같은 화면을 보여 준다.
이 때, 이미지 아래에 있는 "Lorem ipsum..." 이하의 텍스트들을 모두 가져오려고 한다.
자바의 Jsoup이라는 라이브러리를 이용하면 텍스트 추출을 자동화할 수 있다.
*jsoup 라이브러리가 포함된 jar를 꼭 프로젝트에 추가해 주어야 한다.
JSoup의 클래스와 메서드
Elements, Element
: CSS 선택자를 이용해서 가져온다.
일반적으로 태그에 부여된 id 또는 class로 구분할 수 있다. (ex) tag#id_name, tag.class_name
Elements는 여러 개의 태그를 가져올 때 사용하여 반복문을 통해 결과를 걸러 내고,
Element는 하나의 태그를 가져올 때 사용한다.
selectFirst(String s)
: 선택자와 일치하는 것 중 첫 번째에 있는 태그를 가져온다.
select(String s)
: 선택자와 일치하는 모든 태그를 가져온다.
select(String s)[i]
: 선택자와 일치하는 모든 태그 중 i번째 태그를 가져 온다.
text() : 태그 사이의 텍스트 값을 가지고 온다.
attr(String s) : 속성에 들어간 값을 가지고 온다. (*img, a태그에서 많이 사용)
html() : 하위 태그의 코드 전체를 가지고 온다.
data() : javascript 안에 들어간 값을 가지고 온다.
결과적으로, 아래처럼 코드를 작성하면 3개의 "Lorem ipsum..." 이라는 텍스트와
각 이미지의 소스를 가져와 출력할 수 있다.
public void practieData() {
try {
String html = "연습용 HTML 코드";
Document doc = Jsoup.parse(html); //html코드를 파싱한다.
//caption이라는 클래스의 div 태그 아래의 p를 가져온다.
Elements txt = doc.select("div.caption p");
//img태그를 다 가져온다.
Elements imgsrc = doc.select("img");
System.out.println(txt);
System.out.println(imgsrc);
for (int i = 0; i < txt.size(); i++) {
//Elemetns txt 에서 text만을 가져와 출력한다.
System.out.println(txt.get(i).text());
//img태그에서 src 속성값을 가져와 출력한다.
System.out.println(imgsrc.get(i).attr("src"));
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
여기서(https://try.jsoup.org/) HTML 코드를 넣은 후 원하는 데이터를 가져오는 선택자 연습을 할 수 있다.
실습
위 연습을 토대로 망고플레이트에서 실습을 해 본다.
메인 페이지의 "믿고 보는 맛집 리스트" 메뉴에서
각 추천페이지별 문구와 설명글, 이미지 소스를 가져오려 한다.
개발자 도구를 열어 태그를 확인해 보면, "착즙주스 맛집 베스트 7곳" 이라는 텍스트는
div.top_list_slide 아래의 ... ul.list-toplist-slider 아래의 li요소 안에 있는 span.title에 들어 있다.
이런 식으로 각 텍스트와 이미지 링크들의 위치를 파악하여 선택자로 지정하면 요소 값을 가져올 수 있다.
foodcategory 라는 테이블에는 기획전의 타이틀과 설명글, 배경사진, 링크 요소가 들어 있다.
FoodCategoryVO 라는 클래스에 해당 내용을 작성한다.
package doodoo.dao;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FoodCategoryVO {
private int cno;
private String title, subject, poster, link;
}
Jsoup을 이용해서 망고플레이트에서 원하는 데이터를 골라 가져온다.
가져온 데이터는 FoodDAO를 통해 데이터베이스의 테이블에 삽입하고,
다시 불러와서 FoodCategoryVO로 생성한 인스턴스에 값들을 부여하고 화면에 출력할 수 있다.
FoodMain.java (Class)
package doodoo.main;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.*;
import doodoo.dao.*;
public class FoodMain {
public void categoryData() {
try {
FoodDAO dao = new FoodDAO();
Document doc = Jsoup.connect("https://www.mangoplate.com/").get();
Elements title = doc.select("div.top_list_slide span.title");
Elements subject = doc.select("div.top_list_slide p.desc");
Elements poster = doc.select("div.top_list_slide img.center-croping");
Elements link = doc.select("div.top_list_slide a");
for (int i = 0; i < title.size(); i++) {
System.out.println(title.get(i).text()); //태그 사이의 값을 가져올 때 .text()
System.out.println(subject.get(i).text());
System.out.println(poster.get(i).attr("data-lazy")); //속성값을 가져올 때는 .attr(속성이름)
System.out.println(link.get(i).attr("href"));
System.out.println("------------------------");
FoodCategoryVO vo = new FoodCategoryVO();
vo.setLink(link.get(i).attr("href"));
vo.setTitle(title.get(i).text());
vo.setSubject(subject.get(i).text());
vo.setPoster(poster.get(i).attr("data-lazy"));
dao.categoryInsert(vo);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void foodData() {
try {
FoodDAO dao = new FoodDAO();
List < FoodCategoryVO > list = dao.foodCategoryInfoData();
for (FoodCategoryVO vo: list) {
System.out.println(vo.getTitle());
//각 카테고리별 음식점 목록에서 음식점 하나하나의 상세보기 링크를 가져온다.
Document doc = Jsoup.connect("https://www.mangoplate.com" + vo.getLink()).get();
System.out.println("=====" + vo.getTitle() + "=====");
Elements link = doc.select("ul.list-restaurants figure.restaurant-item span.title a");
for (int i = 0; i < link.size(); i++) {
//각 음식점의 상세보기 페이지 링크로 들어간다.
//System.out.println(link.get(i).attr("href"));
Document doc2 = Jsoup.connect("https://www.mangoplate.com" + link.get(i).attr("href")).get();
Element title = doc2.selectFirst("span.title h1.restaurant_name");
System.out.println(title.text());
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
FoodMain m = new FoodMain();
// m.categoryData();
m.foodData();
}
}
FoodDAO.java (Class)
package doodoo.dao;
import java.util.*;
import java.sql.*;
public class FoodDAO {
private Connection conn;
private PreparedStatement ps;
private final String URL = "jdbc:oracle:thin:@localhost:1521:XE";
public FoodDAO() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
//reflection(Spring)
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void getConnection() {
try {
conn = DriverManager.getConnection(URL, "user", "pwd");
} catch (Exception ex) {}
}
public void disConnection() {
try {
if (ps != null) ps.close();
if (conn != null) conn.close();
} catch (Exception ex) {}
}
public void categoryInsert(FoodCategoryVO vo) {
try {
getConnection();
String sql = "INSERT INTO food_category " +
"VALUES ((SELECT NVL(MAX(cno)+1,1) FROM food_category)," +
"?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, vo.getTitle());
ps.setString(2, vo.getSubject());
ps.setString(3, vo.getPoster());
ps.setString(4, vo.getLink());
ps.executeUpdate();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
disConnection();
}
}
public List < FoodCategoryVO > foodCategoryInfoData() {
List < FoodCategoryVO > list = new ArrayList < FoodCategoryVO > ();
//상세보기 데이터 가져오기
try {
getConnection();
String sql = "SELECT cno, link, title " +
"FROM food_category " +
"ORDER BY cno ASC";
ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
FoodCategoryVO vo = new FoodCategoryVO();
vo.setCno(rs.getInt(1));
vo.setLink(rs.getString(2));
vo.setTitle(rs.getString(3));
list.add(vo);
}
rs.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
disConnection();
}
return list;
}
}
'부트캠프(END) > -Web 실습' 카테고리의 다른 글
JSP 실습 : 묻고 답하기 게시판 만들기 (0) | 2022.07.26 |
---|---|
JSP 실습 : 파일 첨부 게시판 만들기 (0) | 2022.07.19 |
Servlet 실습 : CURD 게시판 만들기 (0) | 2022.07.14 |