네이버 검색 API연동해서 뉴스 크롤링.

 

https://developers.naver.com/docs/serviceapi/search/news/news.md#%EB%89%B4%EC%8A%A4

 

검색 > 뉴스 - Search API

검색 > 뉴스 뉴스 검색 개요 개요 검색 API와 뉴스 검색 개요 검색 API는 네이버 검색 결과를 뉴스, 백과사전, 블로그, 쇼핑, 영화, 웹 문서, 전문정보, 지식iN, 책, 카페글 등 분야별로 볼 수 있는 API

developers.naver.com

예시까지 친절하게 설명해준다.

 

먼저 내 애플리케이션을 등록하고 API에 대한 id와 key를 받아야한다.

 

나는 400/401에러가 계속났는데 yml의 id와 key를 잘못 입력하고 원인을 다른곳에서 찾아 몇시간을 헤맸다.

 

 

yml 설정부

naver:  #naver API 설정
  url:
    search:
      news: https://openapi.naver.com/v1/search/news.json   #뉴스검색 URL
      image: https://openapi.naver.com/v1/search/image    #이미지검색
  client:
    id: ENC(rbiCFlHAp11Uusawse9EDX4NTmpRvfEJewqabaeeqefvZM=) #암호화된 키
    secret: ENC(CbqkdospKojXkFsiY2OueQOdLpMbveCTJkbKN9/)

 

Dto

public class SearchNewsDto {
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Post { //검색 쿼리
        private String query = ""; //검색어. UTF-8로 인코딩되어야 합니다.
        private int display = 20; //한 번에 표시할 검색 결과 개수(기본값: 10, 최댓값: 100)
        private int start = 1; //검색 시작 위치(기본값: 1, 최댓값: 1000)
        private String sort = "sim"; // sim: 정확도순 , date: 날짜순

        /*데이터를 한꺼번에 들어가게 해주는 메서드*/
        public MultiValueMap<String, String> toMultiValueMap() {
            var map = new LinkedMultiValueMap<String, String>();

            map.add("query", query);
            map.add("display", String.valueOf(display));
            map.add("start", String.valueOf(start));
            map.add("sort", sort);

            return map;
        }
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Response { //반환값
        private String lastBuildDate; //검색한 결과를 생성한 시간
        private int total; // 총 검색결과 갯수
        private int start; // 검색 시작 위치
        private int display; // 한번에 표시할 검색결과 갯수
        private List<NewsItem> items;
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public static class NewsItem {
            private String title; //제목
            private String originallink; // 뉴스기사 원문 URL
            private String link; //뉴스 기사의 네이버 뉴스 URL. 네이버에 제공되지 않은 기사라면 기사 원문의 URL을 반환합니다.
            private String description; // 뉴스기사 내용을 요약한 정보
            private String pubDate;
        }
    }
}

 

Controller

@RequiredArgsConstructor
@RequestMapping("/news")
@RestController
public class NaverSearchController {
    private final NaverClient naverClient;
    @GetMapping
    public ResponseEntity searchNews(@RequestParam(required = false, defaultValue = "환경") String query,
                                     @RequestParam(required = false, defaultValue = "1") int start,
                                     @RequestParam(required = false, defaultValue = "20") int display,
                                     @RequestParam(required = false, defaultValue = "sim") String sort){

         SearchNewsDto.Response response = naverClient.searchQuerySet(query, start, display, sort);

        return new ResponseEntity<>(response, HttpStatus.OK);
    }
}

 

Service

@Service
public class NaverClient {
    @Value("${naver.client.id}")
    private String naverClientId; //id
    @Value("${naver.client.secret}")
    private String naverClientSecret; //시크릿키
    @Value("${naver.url.search.news}")
    private String naverNewSearchUrl; //뉴스 검색주소
    @Value("${naver.url.search.image}")
    private String naverImageSearchUrl; //이미지 검색주소

    /* 뉴스검색 메서드*/
    public SearchNewsDto.Response searchNews(SearchNewsDto.Post post){
        var uri = UriComponentsBuilder.fromUriString(naverNewSearchUrl) //뉴스검색 주소
                .queryParams(post.toMultiValueMap())
                .build()
                .encode()
                .toUri();

        var headers = new HttpHeaders(); //http헤더에 키값을 넣어줌
        headers.set("X-Naver-Client-Id", naverClientId);
        headers.set("X-Naver-Client-Secret", naverClientSecret);
        headers.setContentType(MediaType.APPLICATION_JSON); //Json형식으로 변환

        var httpEntity = new HttpEntity<>(headers); //요청하는 부분
        var responseType = new ParameterizedTypeReference<SearchNewsDto.Response>(){}; //응답값

        var responseEntity = new RestTemplate().exchange( //모든 정보를 담아주는 부분
                uri,
                HttpMethod.GET,
                httpEntity,
                responseType
        );
        return responseEntity.getBody();
    }

    public SearchNewsDto.Response searchQuerySet(String query, int display, String sort){
        SearchNewsDto.Post post = new SearchNewsDto.Post();
        post.setQuery(query);
        post.setStart(start);
        post.setDisplay(display);
        post.setSort(sort);

        return searchNews(post);
    }
}

 

결과화면

+ Recent posts