자바

[Java] .csv crawling

Atriel 2023. 12. 19. 15:12

CSV (comma-separated values)는  ,(콤마)로 구분되 있는 데이터파일

엑셀로 열어볼 수는 있으나 정보를 엑셀로 막 수정하다가는 파일 전체가 깨질수도 있으니

엑셀로는 읽기만 하는걸 권장

 

보통 DB에서 csv로 추출한 정보를 가공할때 쓰거나, 데이터를 분석할때등 많이 쓰임

 

 

[예시 데이터] (출처: data.go.kr)

// 전라남도 메뉴정보

// https://www.data.go.kr/data/15076624/fileData.do

 

// 전라남도 식당 정보

// https://www.data.go.kr/data/15076621/fileData.do

전라남도_식당정보_20201229.csv
0.81MB
전라남도_메뉴정보_20210120.csv
7.97MB

 

 

 

CSV파일을 자바 프로젝트로 끌어온 뒤

 

메모장으로 구경한 CSV 파일 구조

 

데이터가 콤마(,)로 구분되있고 " 로 묶여있는걸 확인

엑셀이 아닌 메모장이나 notepad++ 등등으로 열어 읽어보는걸 추천

 

대부분 한글 문서는 UTF-8로 되있지만 아닐 수도 있으니 포맷을 확인해주자

파일을 읽어올때는 가장 빠른 StringBuilder를 사용

 

{CSVParser.java]

package ex03.csv;

import java.io.BufferedReader;
import java.io.FileReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

// csv를 파싱하여 List<LineInfo>로 생성하는 파서
public class CSVParser {
	
	// path : 파일경로,
	// charset : 문자열 인코딩
	public static List<LineInfo> makeCSVList(String path, String charset) {
		List<LineInfo> list = new ArrayList<>();
		
		try (FileReader fr = new FileReader(path, Charset.forName(charset));
			 BufferedReader br = new BufferedReader(fr); ) {
			String headerStr = br.readLine();
			List<String> headerList = csvLineToList(headerStr);
			
			String str = null;
			while((str = br.readLine()) != null) {
				List<String> infoList = csvLineToList(str);
				if(infoList == null) {
					continue;
				}
				LineInfo info = new LineInfo(headerList, infoList);
				list.add(info);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return list;
	}
	
//	headerStr : 메뉴ID,식당ID,메뉴명,메뉴가격,메뉴태그정보,등록일시
//	line : 1,858991,짜장면,6000,"주재료 : 돼지고기,야채,면,밀가루 / 조리법 : 볶음 / 소스 : 춘장",2020-10-26 16:45
	private static List<String> csvLineToList(String csvLine) {
		try {
			String delemeter = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)";
			List<String> list = new ArrayList<>();
			String[] array = csvLine.split(delemeter);
			for(String str : array) {
				if(str.contains("메뉴ID")) {
					str = str.substring(1,str.length());
				}
				str = str.replace("\"", "").strip();
				list.add(str);
			}
			return list;
		} catch (Exception e) {
			System.err.println("csvLine : " + csvLine);
			e.printStackTrace();
		}
		return null;
	}
}

 

 

[LineInfo.java]    파일 읽기

package ex03.csv;

import java.util.List;

public class LineInfo {
	private List<String> header; // 컬럼값
	private List<String> info; // 데이터값
	
	
	public List<String> getHeader() {
		return header;
	}


	public void setHeader(List<String> header) {
		this.header = header;
	}


	public List<String> getInfo() {
		return info;
	}


	public void setInfo(List<String> info) {
		this.info = info;
	}


	public LineInfo() {
		super();
	}


	public LineInfo(List<String> header, List<String> info) {
		super();
		this.header = header;
		this.info = info;
	}


	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder(); // 자추 출력할 예정, 성능 생각해서 StringBuilder로 만들기 StringBuffer도 되긴하는데 빌더가 좀더 빠르다
		for(int i=0; i< header.size(); i++) {
			sb.append(header.get(i) + ":" + info.get(i) + ", ");
		}
		
		return sb.toString();
	}
	
	public String getData(String header) {
		int index = header.indexOf(header);
		if(index != -1) {
			return info.get(index);
		} else {
			return null;
		}
	}
	
	public int getSize() {
		return header.size();
	}
	
	
}

 

 

[RunMain.java]

package ex03.csv;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// 전라남도 메뉴정보
// https://www.data.go.kr/data/15076624/fileData.do

// 전라남도 식당 정보
// https://www.data.go.kr/data/15076621/fileData.do

// 1. 식당과 모든 메뉴를 탐색할수 있는 기능 
// 2. 식당을 찾으면 메뉴까지 보여주는 기능  
// 3. 메뉴의 키워드를 조회하면 식당 정보가 같이 나올수 있는 기능 

public class RunMain {
	public static void main(String[] args) {
		List<LineInfo> menuList = CSVParser.makeCSVList("전라남도_메뉴정보_20210120.csv", "UTF-8");
		System.out.println("메뉴정보 출력");
//		menuList.forEach((v) -> System.out.println(v));
		System.out.println("--------------------------------------------");
		
		List<LineInfo> restaurantList = CSVParser.makeCSVList("전라남도_식당정보_20201229.csv", "EUC-KR");
		System.out.println("식당 정보 출력");
//		restaurantList.forEach((v) -> System.out.println(v));
		System.out.println("--------------------------------------------");
		
		System.out.println("menuList size : " + menuList.size());
		System.out.println("restaurantList size : " + restaurantList.size());
		
		
		// 1. 식당과 모든 메뉴를 탐색할수 있는 기능 
		// 1.1 ) 무지성 접근 -> 결론 느리다!
//		for(LineInfo info : restaurantList) {
//			String rId = info.getData("식당ID");
//			for(LineInfo minfo : menuList) {
//				if(minfo.getData("식당ID").equals(rId)) {
//					System.out.println(minfo);
//				}
//			}
//			System.out.println("------------------------------------------------------");
////			System.out.println(rId);
//		}
		
		// 1.2 빠른 접근 방법
		// -> map으로 자료구조 구성 필요!
		
		// 식당ID - 식당 Line 정보
		Map<String, LineInfo> restaurantIDToLineInfoMap 
										= new HashMap<String, LineInfo>();
		// 식당ID - 메뉴 리스트
		Map<String, List<LineInfo>> restaurantIDToMenuListMap 
										= new HashMap<String, List<LineInfo>>();
		
		// 초기화 코드!
		// 순서 존재, 식당-식당 List를 먼저 구성하고 메뉴 리스트를 구성해야함
		for(LineInfo info : restaurantList) {
			restaurantIDToLineInfoMap.put(info.getData("식당ID"), info);
		}
		
		for(LineInfo info : menuList) {
			String rId = info.getData("식당ID");
			List<LineInfo> list = restaurantIDToMenuListMap.get(rId);
			if(list == null) {
				list = new ArrayList<>();
				restaurantIDToMenuListMap.put(rId, list);
			}
			list.add(info);
		}
//		restaurantIDToLineInfoMap.forEach((k,v) -> System.out.println(k+" : "+v));
//		restaurantIDToMenuListMap.forEach((k,v) -> System.out.println(k+" : "+v));
		
		// 출력부
		for(LineInfo info : restaurantList) {
			String rid = info.getData("식당ID");
			System.out.println(info);
			List<LineInfo> list = restaurantIDToMenuListMap.get(rid);
			for(LineInfo info2 : list) {
				System.out.println(info2);
			}
			System.out.println("-------------------------------------------");
		}
		System.out.println("------------------------------------------------------------");
		
		
//		2. 식당을 찾으면 메뉴까지 보여주는 기능
		String keyword = "오리";
		for(LineInfo info : restaurantList) {
			if(info.getData("식당명").contains(keyword)) {
				System.out.println("==========================================================");
				System.out.println(info);
				String rid = info.getData("식당ID");
				List<LineInfo> list = restaurantIDToMenuListMap.get(rid);
				for(LineInfo info2 : list) {
					System.out.println(info2);
				}
				System.out.println("==========================================================");
			}
		}
		System.out.println("------------------------------------------------------------");
		
//		3. 메뉴의 키워드를 조회하면 식당 정보가 같이 나올수 있는 기능 
		String keyword2 = "닭갈비";
		for(LineInfo info : menuList) {
			if(info.getData("메뉴명").contains(keyword2)) {
				System.out.println("*********************************************************");
				System.out.println(info);
				String rid = info.getData("식당ID");
				LineInfo info2 = restaurantIDToLineInfoMap.get(rid);
				System.out.println(info2);
				System.out.println("*********************************************************");
			}
		}
	}
}