이전글 : 네이버 뉴스 RSS 읽어 오기 -1-
먼저, 메이븐이나 다른 라이브러리는 읽지 않아도 되었지만, 혹여 컴파일이 안되시는 경우가 있을 수 있으니
import 부분을 잘 참고 하시기 바랍니다.
자 파싱을 하도록합니다.
기본 파싱 소스는
https://www.vogella.com/tutorials/RSSFeed/article.html
참조 하였습니다. 좋은 형태이고 저는 살짝 정리를 하였습니다.
다만, 네이버 쪽이라든지 다른 형태의 구성이 약간 상의한 부분이 있어 수정을 했습니다.
전체 소스는 아래 있습니다.
RSSFeedParser.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.XMLEvent;
import 앞전소스들.Feed;
import 앞전소스들.FeedMessage;
/**
* @author torrms
*
*/
public class RSSFeedParser {
static final String TITLE = "title";
static final String DESCRIPTION = "description";
static final String LANGUAGE = "language";
static final String LINK = "link";
static final String AUTHOR = "author";
static final String ITEM = "item";
static final String LAST_BUILD_DATE = "lastBuildDate";
//message
static final String PUB_DATE = "pubDate";
static final String CATEGORY = "category";
static final String THUMBNAIL = "thumbnail";
final URL url;
public RSSFeedParser(String feedUrl) {
try {
this.url = new URL(feedUrl);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
public Feed readFeed() {
Feed feed = null;
try {
boolean isFeedHeader = true;
// Set header values intial to the empty string
String description = "";
String title = "";
String link = "";
String language = "";
String author = "";
String lastBuildDate = "";
String category = "";
String pubDate = "";
String thumbnail = "";
// First create a new XMLInputFactory
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
// Setup a new eventReader
InputStream in = read3();
XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
// read the XML document
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
String localPart = event.asStartElement().getName()
.getLocalPart();
switch (localPart) {
case ITEM:
if (isFeedHeader) {
isFeedHeader = false;
feed = new Feed(title, link, description, language,lastBuildDate);
}
event = eventReader.nextEvent();
break;
case TITLE:
title = getCharacterData(event, eventReader);
break;
case DESCRIPTION:
description = getCharacterData(event, eventReader);
break;
case LINK:
link = getCharacterData(event, eventReader);
break;
case LANGUAGE:
language = getCharacterData(event, eventReader);
break;
case AUTHOR:
author = getCharacterData(event, eventReader);
break;
case LAST_BUILD_DATE:
lastBuildDate = getCharacterData(event, eventReader);
break;
case PUB_DATE:
pubDate = getCharacterData(event, eventReader);
break;
case CATEGORY:
category = getCharacterData(event, eventReader);
break;
case THUMBNAIL:
thumbnail = getCharacterData(event, eventReader);
// ex ) <media:thumbnail url="https://imgnews.pstatic.net/image/thumb140/5551/2019/09/04/60286.jpg"/> 형태이므로
@SuppressWarnings("unchecked") Iterator<Attribute> attribue = event.asStartElement().getAttributes();
while(attribue.hasNext()){
Attribute myAttribute = attribue.next();
if(myAttribute.getName().toString().equals("url")){
thumbnail = myAttribute.getValue();
}
}
break;
default:
//System.out.println("localPart:"+ localPart);
break;
}
} else if (event.isEndElement()) {
if (event.asEndElement().getName().getLocalPart() == (ITEM)) {
FeedMessage message = new FeedMessage();
message.setAuthor(author);
message.setDescription(description);
message.setLink(link);
message.setTitle(title);
message.setCategory(category);
message.setPubdate(pubDate);
message.setThumbnail(thumbnail);
feed.getMessages().add(message);
event = eventReader.nextEvent();
continue;
}
}
}
} catch (XMLStreamException e) {
throw new RuntimeException(e);
}
return feed;
}
private String getCharacterData(XMLEvent event, XMLEventReader eventReader)
throws XMLStreamException {
String result = "";
event = eventReader.nextEvent();
if (event instanceof Characters) {
result = event.asCharacters().getData();
}
return result;
}
// 기존 리더
// private InputStream read() {
// try {
// return url.openStream();
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// }
private InputStream read3() {
try {
ReplacingInputStream ris = new ReplacingInputStream(url.openStream(), "'", "'");
return ris;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static InputStream byteArrayToInputStream(byte[] srcBytes) {
return new ByteArrayInputStream(srcBytes);
}
}
기존의 리더리를 통해 읽어서 테스트를 진행 하도록 했습니다.
정보는 정리가 되어 잘 오고 있었습니다. 그런데...
원정보
... <title>'미드 체르노빌....' XXXX </title> ... |
출력정보
... title = ' ... |
그렇습니다. 기존의 단순 url.openStream 으로 InputStream 으로는 따옴표가 읽혀 지지 않습니다.
작은 따옴표(싱글 퀘테이션) 등이 말썽으로 원천의 스트림을 출력해보면
'미드 체르노빌 ... |
위와 같이 읽어 오는 것이 바로 치환이 되버려서 오는 겁니다.
짜증이... 변환이 필요합니다. replace 처럼요... 물론 replace를 쓸수는 없습니다. 스트림이니까요
외국에 깃 양반을 검색합니다. .... 검색중... 그렇습니다. 전 할 줄 모르고 구글이 해줍니다. ^^;
마침네. simon 이란 시몬이란 양반이 만들어 놓은게 있습니다. 데려옵니다.
두둥, ReplacingInputStream 클래스가 바로 그것입니다.
ReplacingInputStream .java
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
/**
* Created by simon on 8/29/17.
*/
public class ReplacingInputStream extends FilterInputStream {
private Queue<Integer> inQueue, outQueue;
private final byte[] search, replacement;
public ReplacingInputStream(InputStream in, String search, String replacement) {
super(in);
this.inQueue = new LinkedList<>();
this.outQueue = new LinkedList<>();
this.search = search.getBytes();
this.replacement = replacement.getBytes();
}
private boolean isMatchFound() {
Iterator<Integer> iterator = inQueue.iterator();
for (byte b : search) {
if (!iterator.hasNext() || b != iterator.next()) {
return false;
}
}
return true;
}
private void readAhead() throws IOException {
// Work up some look-ahead.
while (inQueue.size() < search.length) {
int next = super.read();
inQueue.offer(next);
if (next == -1) {
break;
}
}
}
@Override
public int read() throws IOException {
// Next byte already determined.
while (outQueue.isEmpty()) {
readAhead();
if (isMatchFound()) {
for (@SuppressWarnings("unused") byte a : search) {
inQueue.remove();
}
for (byte b : replacement) {
outQueue.offer((int) b);
}
} else {
outQueue.add(inQueue.remove());
}
}
return outQueue.remove();
}
@Override
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
// copied straight from InputStream inplementation, just needed to to use `read()` from this class
@Override
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
사랑해 줍니다.
그러면 문제가 해결되어 나오게 됩니다. 아~ 테스트 소스도 올립니다.
단, json 이나 여타 다른 정보는 설정이 된 상태에서 합니다.
따라서 java APP 소스와 컨트롤러( Controller )는 상황에 맞게 하시면 됩니다.
JAVA
import 앞전소스.Feed;
import 앞전소스.FeedMessage;
/**
* @author torrms
*
*/
public class testRss {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
RSSFeedParser parser = new RSSFeedParser(
"http://newssearch.naver.com/search.naver?where=rss&query=3D 프린터");
Feed feed = parser.readFeed();
System.out.println(feed);
for (FeedMessage message : feed.getMessages()) {
System.out.println(message);
}
}
}
Controller
// 네이버 뉴스 RSS 테스트(웹) /////////////////////////////////////////////////////////////////////////////////////////
@RequestMapping(value = "/getNewsRSSAjax.json")
public ModelAndView getNewPage(@RequestParam Map<String, Object> commandMap) {
ModelAndView mv = new ModelAndView();
mv.setViewName("jsonView");
log.info("getNewPage");
String result = "false";
try {
String keyword = (String) commandMap.get("keyword");
String maxcnt = (String) commandMap.get("maxcnt");
String Url = "http://newssearch.naver.com/search.naver?where=rss&query=" + keyword;
log.info("Url:" + Url);
RSSFeedParser parser = new RSSFeedParser(Url);
Feed feed = parser.readFeed();
feed.getMessages();
// TODO 방식 고려
// 메모리 방식으로 변경을 고려해야함 RSS 너무 자주 부르게 되므로
List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
int i = 0 ;
int maxcnt_int = 0;
if (maxcnt != null)
maxcnt_int = Integer.valueOf(maxcnt)+1;
for (FeedMessage message : feed.getMessages()) {
i++;
if (i == maxcnt_int)
break;
Map<String, Object> obj = new HashMap<String, Object>();
obj.put("title" , message.getTitle() );
obj.put("link" , message.getLink() );
obj.put("description" , message.getDescription() );
obj.put("pubdate" , message.getPubdate() );
obj.put("author" , message.getAuthor() );
obj.put("category" , message.getCategory() );
obj.put("thumbnail" , message.getThumbnail() );
// System.out.println(message);
// System.out.println(obj);
listMap.add(obj);
}
mv.addObject("resultList",listMap);
result = "true";
} catch (NullPointerException e) {
log.info("NullPointerException :" + e.toString());
} catch (Exception e) {
log.info(e.toString());
}
mv.addObject("result", result);
return mv ;
}
WEB
<div id="RSS">
</div>
<!-- jquery 선언 후-->
<script type="text/javaScript" >
// 시작
$(function(){
/////////////////////////////////////////////////////////////////////////
//RSS 가져오기
$.ajax({
url:"<c:url value='/getNewsRSSAjax.json' />", //요청할 데이터 경로.
type: 'GET',
data:{
keyword: "3D 프린터",
maxcnt: "5",
},
dataType: "json", //요청할 데이터에 타입.
crossDomain: true,
success: function (data) { //성공적으로 요청했왔을 때...
console.info(data);
$.each(data.resultList,function(i, d){
var title=d["title"]; //title 키의 값을 가져옵니다.
var date=new Date(d["pubdate"]); //뉴스 작성 날짜 객체 생성
var img=d["thumbnail"];
var dateString = date_to_str(date);
$("#RSS").append("<ul style='height:140px;'><li><img src='"+img+"'/></li><li>" + title+ "</li><li>" + dateString+ "</li></ul><br/>" );
});
},
error: function (response) {
console.info(response.status);
console.info(response.error);
} // “success: function”에 종료
}); // $.ajax() 메서드에 종료
</script>
1차 버전이 완료 되었습니다.
분명 특수문자 및 지속적으로 부르는 문제가 있을 것으로 보입니다.
자 나머지는 운영 및 반영에 있습니다. 즐프 하세요
그럼 이만,
'개발하기 > 웹개발도움' 카테고리의 다른 글
RabbitMQ 설치 간단 매뉴얼 - CentsOS 7.x (0) | 2020.02.26 |
---|---|
지도 테스트 -1 (0) | 2020.01.30 |
네이버 뉴스 RSS 읽어 오기 -1- (0) | 2019.09.04 |
전자정부프레임워크 - CRUD - 반영하기1 (0) | 2019.08.09 |
360 영상 테스트 02_ 광고 링크 (0) | 2019.03.11 |