개발 환경은 스프링 부트 1.4.0 버전이다.
json 데이터는 다음과 같다.
https://m.store.naver.com/sogum/api/businesses?filterId=s11556055&filterOpening=true&query=자장명포&start=1001&display=3
{
"total": 178,
"nlu": {
"queryResult": {
"q": "짜장면",
"dbKeyword": {
"isDefault": true,
"getType": "search",
"name": "짜장면",
"viewType": "1col",
"type": "restaurant",
"hasComponents": false,
"useFilter": true
},
"location": {
"default": true,
"latitude": "37.5666100",
"x": "126.9783880",
"y": "37.5666100",
"longitude": "126.9783880"
},
"menu": "짜장면",
"type": "order",
"keyword": "짜장면",
"dbQuery": {
"isDefault": true,
"getType": "search",
"name": "짜장면",
"viewType": "1col",
"type": "restaurant",
"hasComponents": false,
"useFilter": true
},
"showLocationBarFlag": false
},
"user": {},
"deviceInfo": {
"os": "pc",
"ie": "0",
"version": 0
},
"queryType": "restaurant"
},
"query": {
"item": [{
"biz": "짜장면",
"region_info": {
"name": "",
"rcode": ""
},
"coordinate_mode": "0",
"biz_type": "category",
"rank": "1",
"region_type": "lba",
"region_keyword": ""
}, {
"biz": "짜장면",
"region_info": {
"name": "",
"rcode": ""
},
"coordinate_mode": "0",
"biz_type": "unknown",
"rank": "2",
"region_type": "lba",
"region_keyword": ""
}],
"select": "1",
"petrol": "0",
"type": "local",
"global_menu": "0"
},
"smartqueryCoordinates": null,
"items": [{
"imageCount": 11,
"moreUGCReviewsPath": "/restaurants/fsasReviews?id=1618304054&name=%EC%8F%98%ED%95%AB&category=restaurant",
"distance": "640.37",
"imageSrc": "http://ldb.phinf.naver.net/20180716_74/1531728211471HsMn5_JPEG/OdNXSXBu2Fc9hq-_SgSMLH19.JPG.jpg",
"virtualPhone": "0507-1350-4999",
"totalReviewCount": "13",
"roadAddr": "서울 종로구 삼일대로17길 47-1 지하1층",
"options": "예약,단체석,무선 인터넷,포장,남/녀 화장실 구분",
"priceCategory": "1만원 대",
"id": "1618304054",
"addr": "관철동 175",
"street_panorama": "ijDZ/Gizf3bRDP/yq9Bwfg==,173.87,10.00,126.9849299,37.5692042,120",
"moreFsasReviewsPath": "/restaurants/fsasReviews?id=1618304054&name=%EC%8F%98%ED%95%AB&category=restaurant",
"bookingReviewCount": "0",
"hasBooking": false,
"dbType": "drt",
"commonAddr": "서울 종로구",
"blogCafeReviewCount": "13",
"routeUrl": "http://m.map.naver.com/viewer/route.nhn?ename=%EC%8F%98%ED%95%AB&ex=126.9849432&ey=37.5690822&edid=1618304054",
"phone": "010-5704-4999",
"name": "쏘핫",
"businessCategory": "restaurant",
"x": "126.9849432",
"streetViewUrl": "http://m.map.naver.com/viewer/panorama.nhn?street=on&pinType=site&pinId=1618304054",
"y": "37.5690822",
"category": "중식당",
"desc": ""
}, {
"imageCount": 11,
"moreUGCReviewsPath": "/restaurants/fsasReviews?id=1243240541&name=%EC%86%8C%EA%B3%A0%EC%82%B0%EC%A0%9C%EC%9D%BC%EB%A3%A8%20%EC%B6%A9%EB%AC%B4%EB%A1%9C%EC%A0%90&category=restaurant",
"distance": "1379.20",
"imageSrc": "http://ldb.phinf.naver.net/20180730_21/1532942319296qQXTp_JPEG/uHkL5AQZvrhdzDtCpdjXtnpV.JPG.jpg",
"virtualPhone": "0507-1323-2411",
"totalReviewCount": "17",
"roadAddr": "서울 중구 퇴계로30길 14",
"options": "예약,단체석,주차,발렛파킹,포장,배달,남/녀 화장실 구분,무선 인터넷",
"priceCategory": "2만원 대",
"id": "1243240541",
"addr": "필동1가 24-11",
"street_panorama": "EOCnTnU6jtA5Usse4FgIvA==,-48.27,10.00,126.9919683,37.5602110,120",
"moreFsasReviewsPath": "/restaurants/fsasReviews?id=1243240541&name=%EC%86%8C%EA%B3%A0%EC%82%B0%EC%A0%9C%EC%9D%BC%EB%A3%A8%20%EC%B6%A9%EB%AC%B4%EB%A1%9C%EC%A0%90&category=restaurant",
"bookingReviewCount": "0",
"hasBooking": false,
"dbType": "drt",
"commonAddr": "서울 중구",
"blogCafeReviewCount": "17",
"routeUrl": "http://m.map.naver.com/viewer/route.nhn?ename=%EC%86%8C%EA%B3%A0%EC%82%B0%EC%A0%9C%EC%9D%BC%EB%A3%A8%20%EC%B6%A9%EB%AC%B4%EB%A1%9C%EC%A0%90&ex=126.9918536&ey=37.5603134&edid=1243240541",
"phone": "02-2273-2411",
"name": "소고산제일루 충무로점",
"businessCategory": "restaurant",
"x": "126.9918536",
"streetViewUrl": "http://m.map.naver.com/viewer/panorama.nhn?street=on&pinType=site&pinId=1243240541",
"y": "37.5603134",
"category": "중식당",
"desc": ""
}, {
"imageCount": 5,
"bookingReviewCount": "0",
"hasBooking": false,
"distance": "751.17",
"dbType": "drt",
"imageSrc": "http://ldb.phinf.naver.net/20180903_223/1535963652902ll6VG_JPEG/1gMZ_1Qw9I7eeVIY3cT_ofK2.jpeg.jpg",
"virtualPhone": "0507-1368-5858",
"commonAddr": "서울 중구",
"totalReviewCount": "0",
"roadAddr": "서울 중구 명동2길 49 2층",
"blogCafeReviewCount": "0",
"routeUrl": "http://m.map.naver.com/viewer/route.nhn?ename=%EB%A7%88%EB%9D%BC%ED%83%84%ED%83%84%EB%A9%B4&ex=126.9833030&ey=37.5610983&edid=1535545731",
"phone": "070-7762-5858",
"name": "마라탄탄면",
"businessCategory": "restaurant",
"x": "126.9833030",
"streetViewUrl": "http://m.map.naver.com/viewer/panorama.nhn?street=on&pinType=site&pinId=1535545731",
"priceCategory": "2만원 대",
"y": "37.5610983",
"id": "1535545731",
"category": "중식당",
"addr": "충무로1가 24-18",
"street_panorama": "c81OnyzleIlgnJylkM2Bnw==,115.71,10.00,126.9832574,37.5611202,120",
"desc": ""
}]
}
url 에서 json 을 스크룰링 한 후 데이터에서 item 의 키값에 있는 json 배열 값들만 가져와서 추출해서 가져오는 것이 목표이다.
json 메이븐 의존성 주입
1. 우선 스크룰링을 먼저 하자.
개발후 바로 정리를 했어야 했는데, json 파싱과 섞여 있어 정확히 기억 이 안난다.
의존성 주입한 것은 다음과 같다.
<!-- https://mvnrepository.com/artifact/org.json/json --> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency>
다음 코드가 스크룰링 하는 코드 이다.
package macaronics.net.controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.databind.ObjectMapper;
import macaronics.net.domain.NaverFilterVO;
import macaronics.net.domain.NaverVO;
/**
* @author Choi Jun-Ho(braverokmc79@gmail.com)
@since 2018. 9. 18. 오후 3:53:21
*/
@Controller
public class NaverGetJsonController {
private static final Logger log = LoggerFactory.getLogger(NaverGetJsonController.class);
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
@ResponseBody
@RequestMapping("/naver")
public String getJsonNaver(NaverFilterVO naverFilter) throws Exception {
naverFilter.setQuery("true");
log.info("getJson " + naverFilter.toString());
String paramQuery = "filterOpening=" + naverFilter.isFilterOpening() + "" + "&query=" + naverFilter.getQuery()
+ "&start=" + naverFilter.getStart() + "&display=" + naverFilter.getDisplay();
String url = "https://m.store.naver.com/sogum/api/businesses?" + paramQuery;
JSONObject json = readJsonFromUrl(url);
return json.toString();
}
}
2. 파싱을 하자.
데이터 키값은 다음과 같다.
/*@JsonAutoDetect(fieldVisibility=Visibility.NONE, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
@JsonPropertyOrder({"unit_id", "unit_code", "lvl", "name", "regDate", "orders", "imgPath", "status"})
public class UnitVO extends CommonVO implements Cloneable {
@JsonProperty("unitID") @JsonSerialize(using = ToStringSerializer.class) int unit_id;
@JsonProperty("unitCode") String unit_code;
@JsonProperty("lvl") @JsonSerialize(using = ToStringSerializer.class) int lvl;
@JsonProperty("name") String name;
@JsonProperty("regDate") String reg_date;
@JsonProperty("orders") @JsonSerialize(using = ToStringSerializer.class) int orders;
@JsonProperty("imgPath") String img_path;
@JsonProperty("status") @JsonSerialize(using = ToStringSerializer.class) int status;
int apistatus;
String upper_unit_code;
int subcnt;
String statusdesc;
String loginid;
*/
/* getter와 setter는 생략 */
/*imageCount: 11,
moreUGCReviewsPath: "/restaurants/fsasReviews?id=1618304054&name=%EC%8F%98%ED%95%AB&category=restaurant",
distance: "640.37",
imageSrc: "http://ldb.phinf.naver.net/20180716_74/1531728211471HsMn5_JPEG/OdNXSXBu2Fc9hq-_SgSMLH19.JPG.jpg",
virtualPhone: "0507-1350-4999",
totalReviewCount: "13",
roadAddr: "서울 종로구 삼일대로17길 47-1 지하1층",
options: "예약,단체석,무선 인터넷,포장,남/녀 화장실 구분",
priceCategory: "1만원 대",
id: "1618304054",
addr: "관철동 175",
street_panorama: "ijDZ/Gizf3bRDP/yq9Bwfg==,173.87,10.00,126.9849299,37.5692042,120",
moreFsasReviewsPath: "/restaurants/fsasReviews?id=1618304054&name=%EC%8F%98%ED%95%AB&category=restaurant",
bookingReviewCount: "0",
hasBooking: false,
dbType: "drt",
commonAddr: "서울 종로구",
blogCafeReviewCount: "13",
routeUrl: "http://m.map.naver.com/viewer/route.nhn?ename=%EC%8F%98%ED%95%AB&ex=126.9849432&ey=37.5690822&edid=1618304054",
phone: "010-5704-4999",
name: "쏘핫",
businessCategory: "restaurant",
x: "126.9849432",
streetViewUrl: "http://m.map.naver.com/viewer/panorama.nhn?street=on&pinType=site&pinId=1618304054",
y: "37.5690822",
category: "중식당",
desc: "
*/
//}
위 json 배열 키값 에 동일한 자바빈 객체를 설정하자.
json 에 자동으로 탐지해서 데이터를 넣어준다.
package macaronics.net.domain;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
/**
* @author Choi Jun-Ho (braverokmc79@gmail.com)
*/
@JsonAutoDetect
@JsonIgnoreProperties({"query","total", "smartqueryCoordinates", "nlu"})
public class NaverVO implements Cloneable {
@JsonSerialize(using = ToStringSerializer.class)
private int imageCount;
@JsonSerialize(using = ToStringSerializer.class)
private String moreUGCReviewsPath;
@JsonSerialize(using = ToStringSerializer.class)
private double distance ;
@JsonSerialize(using = ToStringSerializer.class)
private String imageSrc ;
@JsonSerialize(using = ToStringSerializer.class)
private String virtualPhone;
@JsonSerialize(using = ToStringSerializer.class)
private String totalReviewCount ;
@JsonSerialize(using = ToStringSerializer.class)
private String roadAddr ;
@JsonSerialize(using = ToStringSerializer.class)
private String options;
@JsonSerialize(using = ToStringSerializer.class)
private String priceCategory ;
@JsonSerialize(using = ToStringSerializer.class)
private int id;
@JsonSerialize(using = ToStringSerializer.class)
private String addr;
@JsonSerialize(using = ToStringSerializer.class)
private String street_panorama ;
@JsonSerialize(using = ToStringSerializer.class)
private String moreFsasReviewsPath ;
@JsonSerialize(using = ToStringSerializer.class)
private String bookingReviewCount ;
@JsonSerialize(using = ToStringSerializer.class)
private boolean hasBooking ;
@JsonSerialize(using = ToStringSerializer.class)
private String dbType;
@JsonSerialize(using = ToStringSerializer.class)
private String commonAddr ;
@JsonSerialize(using = ToStringSerializer.class)
private int blogCafeReviewCount ;
@JsonSerialize(using = ToStringSerializer.class)
private String routeUrl;
@JsonSerialize(using = ToStringSerializer.class)
private String phone;
@JsonSerialize(using = ToStringSerializer.class)
private String name;
@JsonSerialize(using = ToStringSerializer.class)
private String businessCategory;
@JsonSerialize(using = ToStringSerializer.class)
private double x ;
@JsonSerialize(using = ToStringSerializer.class)
private String streetViewUrl;
@JsonSerialize(using = ToStringSerializer.class)
private String y ;
@JsonSerialize(using = ToStringSerializer.class)
private String category;
@JsonSerialize(using = ToStringSerializer.class)
private String desc ;
setter, getter
toString()
}
NaverGetJsonController 전체 소스코드이다.
package macaronics.net.controller;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.databind.ObjectMapper;
import macaronics.net.domain.NaverFilterVO;
import macaronics.net.domain.NaverVO;
/**
* @author Choi Jun-Ho(braverokmc79@gmail.com)
@since 2018. 9. 18. 오후 3:53:21
*/
@Controller
public class NaverGetJsonController {
private static final Logger log = LoggerFactory.getLogger(NaverGetJsonController.class);
private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
@ResponseBody
@RequestMapping("/naver")
public String getJsonNaver(NaverFilterVO naverFilter) throws Exception {
naverFilter.setQuery("true");
log.info("getJson " + naverFilter.toString());
String paramQuery = "filterOpening=" + naverFilter.isFilterOpening() + "" + "&query=" + naverFilter.getQuery()
+ "&start=" + naverFilter.getStart() + "&display=" + naverFilter.getDisplay();
String url = "https://m.store.naver.com/sogum/api/businesses?" + paramQuery;
JSONObject json = readJsonFromUrl(url);
return json.toString();
}
@RequestMapping("/")
public String getJsonNaver2(NaverFilterVO naverFilter, Model model) throws Exception {
// 검색
naverFilter.setQuery("짜장면");
naverFilter.setDisplay(3);
log.info("getJson " + naverFilter.toString());
String paramQuery = "filterOpening=" + naverFilter.isFilterOpening() + "" + "&query=" + naverFilter.getQuery()
+ "&start=" + naverFilter.getStart() + "&display=" + naverFilter.getDisplay();
// URL 상에서 json 객체를 가져오기
String url = "https://m.store.naver.com/sogum/api/businesses?" + paramQuery;
JSONObject json = readJsonFromUrl(url);
//json 파싱 시작
JSONObject jsonObject =new JSONObject(json.toString());
JSONArray jsonArray =jsonObject.getJSONArray("items"); //키값 items 만 가져오기
//list 객체 생성후 담기
List<NaverVO> list=new ArrayList<>();
for(int i=0; i<jsonArray.length(); i++) {
JSONObject jsonObject2 =(JSONObject)jsonArray.get(i);
//json 을 NaverVO 자동으로 파싱해서 넣는 역할 을 한다.
NaverVO naverVO=new ObjectMapper().readValue(jsonObject2.toString(), NaverVO.class);
list.add(naverVO);
}
log.info(" json : "+ json.toString());
/*
for(NaverVO vo : list) {
log.info(vo.toString());
}
*/
return "index";
}
}
NaverFilterVO 검색 분류 빈 객체를 추가 하였다.
public class NaverFilterVO {
private boolean filterOpening;
private String query;
private int start;
private int display;
public NaverFilterVO() {
this.filterOpening=true;
this.query="";
this.start=1;
this.display=1000;
}
public boolean isFilterOpening() {
return filterOpening;
}
public void setFilterOpening(boolean filterOpening) {
this.filterOpening = filterOpening;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
try {
this.query=URLEncoder.encode(query, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getDisplay() {
return display;
}
public void setDisplay(int display) {
this.display = display;
}
@Override
public String toString() {
return "NaverFilter [filterOpening=" + filterOpening + ", query=" + query + ", start=" + start + ", display="
+ display + "]";
}
}
자세한 코드는 나의
에 있으니 찾아 보면 된다.

















댓글 ( 4)
댓글 남기기