2025.01.24 Jsoup을 이용한 웹 크롤링(Selenium이 더 좋은 것 같아요)
[ 웹 크롤링 ] (스크래핑 + 크롤링)
샘플 데이터를 웹 페이지로부터 추출하고 → 스크래핑
이 데이터들을 나의 프로젝트에 알맞는 형태로 “가공”할 수 있다. → 크롤링
- 타겟 웹 페이지 url
https://comic.naver.com/webtoon?tab=mon
import 할 때 swing은 옛날꺼라서 안 하는게 좋음
Jsoup은 random crawling 하는 친구
jar = 자바 아카이브 = 자바 코드 묶음
웹페이지를 코딩하는 언어
HTML
: 한쌍의 <> 태그로 이루어져 있음 → MarkUP 언어

span.DailyList_title—epGnt
.DailyList_title—epGnt → 클래스 이름
점(.) : class 속성명
<span>광마회귀</span>
span#title
<span id=”title”></span>
# : id 속성명

<a class = “ContentAuthor_author—CTAAP”>박태준</a>

이렇게 이해하면 된다.
커피빈 크롤링
package day018.class01;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) {
// 크롤링 할 URL
// 추출
final String URL="https://www.coffeebeankorea.com/menu/list.asp?category=13";
Document doc=null;
try {
doc=Jsoup.connect(URL).get();
} catch (IOException e) {
e.printStackTrace();
}
Elements elems=doc.select("span.kor");
for(Element elem:elems) {
String str=elem.text();
System.out.println(str);
}
}
}

교촌치킨 크롤링
package day018.class02;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) {
// 크롤링 할 URL
// 추출
final String URL = "https://www.kyochon.com/menu/chicken.asp?code=18";
Document doc = null;
try {
doc = Jsoup.connect(URL).get();
} catch (IOException e) {
e.printStackTrace();
}
// 메뉴 정보가 있는 dl 태그를 선택합니다
Elements elems = doc.select("dl.txt");
// 각 메뉴 아이템을 순회하면서 메뉴명과 가격을 함께 출력합니다
for (Element elem : elems) {
// 메뉴명
String menuName = elem.select("dt").text();
// 설명
String description = elem.select("dd").text();
// 가격
String price = elem.parent().select("p.money strong").text();
System.out.println("================");
System.out.println("메뉴명 : " + menuName);
System.out.println("설명 : " + description);
System.out.println("가격 : " + price + "원");
}
System.out.println("================");
}
}

교촌치킨 크롤링을 Model, View, Controller로 나누어 구현해봤다. 이렇게 한 이유는 낮은 결합도, 높은 응집도를 위해서 했다.

Client(사용자, 실행파일)
package day018.class02.client;
import day018.class02.Controller.Controller;
public class KyochonClient {
public static void main(String[] args) {
Controller app = new Controller();
app.appStart();
}
}
Controller
package day018.class02.Controller;
import day018.class02.Model.Crawling;
import day018.class02.Model.KyoChonDAO;
import day018.class02.Model.KyochonDTO;
import day018.class02.View.View;
import java.util.ArrayList;
import java.util.Scanner;
public class Controller {
private KyoChonDAO dao;
private Crawling crawler;
private Scanner sc;
private View view;
public Controller() {
this.dao = new KyoChonDAO();
this.crawler = new Crawling(dao);
this.sc = new Scanner(System.in);
this.view = new View();
}
public void appStart(){
while(true){
// 메뉴 보여줌
view.showMainMenu();
// 숫자 입력받기
int num = view.inputNum();
if(num == 0){
view.printExit();
break;
}
// 1. 메뉴 크롤링하기
else if(num == 1){
if(crawler.crawlMenuData()) {
view.printTrue();
} else {
view.printFalse();
}
}
// 2. 전체 메뉴 보기
else if(num == 2){
ArrayList<KyochonDTO> menus = dao.selectAll();
if(menus.isEmpty()) {
view.printEmpty();
} else {
view.printMenuList(menus);
}
}
// 3. 메뉴 검색하기
else if(num == 3){
// view로부터 이름 입력받기
String menuName = view.inputMenuName();
// 이름 입력받은거 model에게 반환
ArrayList<KyochonDTO> menus = dao.selectOne(menuName);
// 메뉴가 해당하지 않으면
if(menus == null) {
// 에러 메세지 출력
view.printNotFound();
}
else {
// 해당 내용 메뉴 출력
view.printMenu(menus);
}
}
// 4. 메뉴 삭제하기
else if(num == 4) {
// view에 이름 입력하기
String menuName = view.inputMenuName();
// model에 이름 입력받은거 반환해서 선택
ArrayList<KyochonDTO> menus = dao.selectOne(menuName);
// 입력받으면 삭제 실행
if(menus.isEmpty()) {
// 메뉴가 아무것도 없을 때 내용 출력
view.printNotFound();
}
else {
// 삭제 메서드 실행
dao.delete(menuName);
// 성공 메세지 출력
view.printTrue();
}
}
// 다른거 입력했을 때
else {
view.inputElse();
}
}
}
}
Model
크롤링 하는 모델
package day018.class02.Model;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
public class Crawling {
private KyoChonDAO dao;
final String URL = "https://www.kyochon.com/menu/chicken.asp";
public Crawling(KyoChonDAO dao) {
this.dao = dao;
}
public boolean crawlMenuData() {
try {
// 웹 페이지 연결
Document doc = Jsoup.connect(URL).get();
// 메뉴 정보 추출
Elements menuItems = doc.select("dl.txt");
for (Element item : menuItems) {
// 메뉴명, 설명, 가격 추출
String menuName = item.select("dt").text();
String description = item.select("dd").text();
String price = item.parent().select("p.money strong").text();
KyochonDTO menuDTO = new KyochonDTO(menuName, description, price);
dao.insert(menuDTO);
}
// 호출 성공하면 true
return true;
} catch (IOException e) {
System.out.println("[로그] 크롤링 중 오류 발생: " + e.getMessage());
// 호출 실패하면 false
return false;
}
}
}
KyochonDAO
package day018.class02.Model;
import java.util.ArrayList;
import java.util.Iterator;
public class KyoChonDAO {
private ArrayList<KyochonDTO> datas;
// 생성자
public KyoChonDAO() {
datas = new ArrayList<>();
}
// 전체 메뉴 검색
public ArrayList<KyochonDTO> selectAll() {
return new ArrayList<>(datas);
}
// 단일 메뉴 검색
public ArrayList<KyochonDTO> selectOne(String menuName) {
ArrayList<KyochonDTO> result = new ArrayList<>();
for (KyochonDTO menu : datas) {
if (menu.getMenuName().contains(menuName)) {
result.add(menu);
}
}
return result;
}
// 새로운 메뉴 추가
public void insert(KyochonDTO dto) {
datas.add(dto);
}
// 메뉴 정보 수정
public void update(KyochonDTO dto) {
for (int i = 0; i < datas.size(); i++) {
if (datas.get(i).getMenuName().equals(dto.getMenuName())) {
datas.set(i, dto);
break;
}
}
}
// 메뉴 삭제
public void delete(String menuName) {
Iterator<KyochonDTO> iterator = datas.iterator();
while (iterator.hasNext()) {
KyochonDTO menu = iterator.next();
if (menu.getMenuName().equals(menuName)) {
iterator.remove();
}
}
}
}
KyochonDTO
package day018.class02.Model;
public class KyochonDTO {
private String menuName; // 메뉴명
private String description; // 설명
private String price; // 가격
// 생성자
public KyochonDTO(String menuName, String description, String price) {
this.menuName = menuName;
this.description = description;
this.price = price;
}
// Getter,Setter
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getMenuName() {
return menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
@Override
public String toString() {
return "================\n" +
"메뉴명 : " + menuName + "\n" +
"설명 : " + description + "\n" +
"가격 : " + price + "원";
}
}
View
package day018.class02.View;
import java.util.ArrayList;
import day018.class02.Model.KyoChonDAO;
import day018.class02.Model.KyochonDTO;
import java.util.Scanner;
public class View {
Scanner sc;
public View() {
// 공통 인자 생성자로 선언
this.sc=new Scanner(System.in);
}
// 메뉴출력
public void showMainMenu(){
System.out.println("\n=== 교촌치킨 메뉴 관리 시스템 ===");
System.out.println("1. 메뉴 크롤링하기");
System.out.println("2. 전체 메뉴 보기");
System.out.println("3. 메뉴 검색하기");
System.out.println("4. 메뉴 삭제하기");
System.out.println("0. 종료");
}
// 번호 입력
public int inputNum(){
System.out.print("번호입력 >> ");
int num = sc.nextInt();
sc.nextLine(); // 버퍼 비우기
return num; }
// 종료 안내
public void printExit(){
System.out.println("종료됩니다.");
}
public void printTrue() {
System.out.println("성공!");
}
public void printFalse() {
System.out.println("실패...");
}
public void printMenuList(ArrayList<KyochonDTO> menus) {
menus.forEach(System.out::println);
}
public void printEmpty() {
System.out.println("등록된 메뉴가 없습니다. 먼저 크롤링을 실행해주세요.");
}
public String inputMenuName() {
while(true) {
System.out.print("메뉴명을 입력하세요: ");
String input = sc.nextLine().trim();
if(input.isEmpty()) {
System.out.println("메뉴명을 반드시 입력해주세요!");
continue;
}
return input;
}
}
public void printNotFound() {
System.out.println("해당 메뉴를 찾을 수 없습니다.");
}
public void printMenu(ArrayList<KyochonDTO> menus) {
for(KyochonDTO menu : menus) {
System.out.println("================");
System.out.println("메뉴명 : " + menu.getMenuName());
System.out.println("설명 : " + menu.getDescription());
System.out.println("가격 : " + menu.getPrice());
System.out.println("================");
}
}
public void inputElse(){
System.out.println("잘못 입력했습니다. 0~4번 사이 숫자를 입력해주세요");
}
}


메뉴는 더 있지만 짤려서 안 나온다

Contains 함수를 써서 스틱만 검색해도 스틱이 들어가는 내용들이 다 나온다
추가로 이름 전체를 다 입력하면 삭제도 가능하다. 레드스틱과 레드스틱[S]는 다른거니 이름을 정확히 다 입력해야 원하는 게 삭제된다
'Back-End' 카테고리의 다른 글
| [JAVA] Toy-Project (1) | 2025.02.07 |
|---|---|
| [크롤링] Jsoup과 Selenium (3) | 2025.02.01 |
| [JAVA] 기초 공부 14 MVC-1 (1) | 2025.01.23 |
| [JAVA] 기초 공부 13 MVC (1) | 2025.01.22 |
| [JAVA] 기초 공부 12 File I/O (2) | 2025.01.21 |