[백업][가리사니] java rss / atom 수집 클래스
java, xml

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

간단하게 만들어봤습니다. 조금 다듬어서 쓰시면 될 것 같습니다.

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

/**
 * 2016-01-23 전명 박용서 : 작성
 * 2016-01-24 전명 박용서 : 구조개선
 * 급하게 만든거라 조금 다듬고 쓰시면 될것 같습니다.
 */
public abstract class FeedReader
{
	/** 시간 포멧 */
	final private SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss", Locale.ENGLISH);
	final private XMLInputFactory XML_FACTORY = XMLInputFactory.newInstance();
	final private int TYPE_RSS = 1;
	final private int TYPE_ATOM = 2;

	/**
	 * 오류를 수집한다
	 * @param url 주소
	 * @param e 익셉션
	 */
	public abstract void onError(FeedInfomation info, Exception e);
	/**
	 * 최종 불러온 갯수
	 * @param url 주소
	 * @param count 갯수
	 */
	public abstract void onFinish(FeedInfomation info, int count);
	/**
	 * 해더를 불러온다.
	 * @param url 주소
	 * @param header 해더정보
	 */
	public abstract void onRssHeader(FeedInfomation info);
	/**
	 * 해더를 불러온다.
	 * @param url 주소
	 * @param header 해더정보
	 */
	public abstract void onAtomHeader(FeedInfomation info);
	/**
	 * 아이템
	 * @param url 주소
	 * @param item 아이템
	 */
	public abstract void onRssItem(FeedInfomation info, HashMap<String, String> item);
	/**
	 * 엔트리
	 * @param url 주소
	 * @param item 아이템(엔트리)
	 */
	public abstract void onAtomItem(FeedInfomation info, HashMap<String, String> item);

	/**
	 * 시간비교
	 * @param lastDate
	 * @param date
	 * @return
	 * @throws Exception
	 */
	private boolean isAfter(long lastDate, String date) throws Exception
	{
		if (date != null)
		{
			date = date.replaceFirst("\\+[\\d]{4}", "").trim();
			return DATE_FORMAT.parse(date).getTime() > lastDate;
		}
		return true;
	}

	/**
	 * 피드를 읽습니다.
	 * @param url
	 */
	public void read(FeedInfomation info)
	{
		read(info, 0);
	}

	/**
	 * 피드를 읽습니다.
	 * lastDate 이후 새롭게 갱신된 데이터만 읽습니다.
	 * @param url
	 * @param lastDate
	 */
	public void read(FeedInfomation info, Date lastDate)
	{
		read(info, lastDate != null ? lastDate.getTime() : 0L);
	}

	/**
	 * 피드를 읽습니다.
	 * lastDate 이후 새롭게 갱신된 데이터만 읽습니다.
	 * @param url
	 * @param lastDate
	 */
	public void read(FeedInfomation info, Calendar lastDate)
	{
		read(info, lastDate != null ? lastDate.getTimeInMillis() : 0L);
	}

	/**
	 * 피드를 읽습니다.
	 * lastDate 이후 새롭게 갱신된 데이터만 읽습니다.
	 * @param url
	 * @param lastDate
	 */
	public void read(FeedInfomation info, long lastDate)
	{
		int count = 0;
		int type = 0;
		boolean isNotChanged = false;
		String unitTagName = null;
		String timeTagName = null;
		String tagName;
		HashMap<String, String> header = info.getHeader();
		try
		{
			XMLStreamReader xml = XML_FACTORY.createXMLStreamReader(new URL(info.getUrl()).openStream(), "utf-8");
			// RSS / ATOM 종류 알아오기
			while(xml.hasNext())
			{
				xml.next();
				if(xml.getEventType() == XMLStreamReader.START_ELEMENT)
				{
					switch (xml.getLocalName().toUpperCase())
					{
						case "RSS" :
							type = TYPE_RSS;
							unitTagName = "item";
							timeTagName = "pubDate";
						break;
						case "FEED" :
							type = TYPE_ATOM;
							unitTagName = "entry";
							timeTagName = "modified";
						break;
						default :
							throw new Exception("is not RSS / ATOM");
					}
					break;
				}
			}
			// 해더 가져오기
			while(xml.hasNext())
			{
				xml.next();
				if(xml.getEventType() == XMLStreamReader.START_ELEMENT)
				{
					if ((!unitTagName.equals((tagName = xml.getLocalName()))))
					{
						try
						{
							String value = xml.getElementText();
							if (timeTagName.equals(tagName))
							{
								if (!isAfter(lastDate, value))
								{
									isNotChanged = true;
								}
							}

							header.put(tagName, value);
						}
						catch (Exception e) {  }
					}
					else
					{
						break;
					}
				}
			}
			switch (type)
			{
				case TYPE_RSS : onRssHeader(info); break;
				case TYPE_ATOM : onAtomHeader(info); break;
			}
			if (isNotChanged)
			{
				onFinish(info, count);
			}
			// 아이템 가져오기
			do
			{
				xml.next();
				if(xml.getEventType() == XMLStreamReader.START_ELEMENT && unitTagName.equals(xml.getLocalName()))
				{
					HashMap<String, String> item = new HashMap<>();
					while(xml.hasNext())
					{
						xml.next();

						if (xml.getEventType() == XMLStreamReader.START_ELEMENT)
						{
							try
							{
								tagName = xml.getLocalName();
								String value = xml.getElementText();

								if (timeTagName.equals(tagName))
								{
									// 끝이나와 더이상 검사할 필요가 없음
									if (!isAfter(lastDate, value))
									{
										onFinish(info, count);
										return;
									}
								}

								item.put(tagName, value);
							}
							catch (Exception e)
							{
								// 급하게 만들어본건데 getElementText 에서 오류가 나는 조건이있습니다.
								// 이부분은 마져 완성해서 사용하시기 바랍니다.
							}
						}
						else if(xml.getEventType() == XMLStreamReader.END_ELEMENT && unitTagName.equals(xml.getLocalName()))
						{
							count++;
							switch (type)
							{
								case TYPE_RSS : onRssItem(info, item); break;
								case TYPE_ATOM : onAtomItem(info, item); break;
							}
							break;
						}
					}
				}
			}
			while (xml.hasNext());

			onFinish(info, count);
		}
		catch (Exception readException)
		{
			onError(info, readException);
		}
	}

	public abstract static class FeedInfomation
	{
		private String url;
		private HashMap<String, String> header = new HashMap<>();

		public String getUrl()
		{
			return url;
		}

		public void setUrl(String url)
		{
			this.url = url;
		}

		public HashMap<String, String> getHeader() {
			return header;
		}
	}
}

사용예제

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

public class MainTest
{
	public static Feed feed = new Feed();

	public static void main(String[] args) throws Exception
	{
		// 직접 유효한 주소를 넣어서 써보세요.
		// Feed를 확장하여 원하는 형태로 사용가능합니다.
		feed.read(new FeedInfo("http://j.saro.me/rss", 1));
		//feed.read(new FeedInfo("http://blog.colcol.net/rss", 1));
	}

	/**
	 * 상속 예제
	 */
	public static class FeedInfo extends FeedReader.FeedInfomation
	{
		public FeedInfo() {}
		public FeedInfo(String url, int uniqueId)
		{
			setUrl(url);
			setUniqueId(uniqueId);
		}

		private int uniqueId;

		public int getUniqueId() {
			return uniqueId;
		}

		public void setUniqueId(int uniqueId) {
			this.uniqueId = uniqueId;
		}
	}

	public static class Feed extends FeedReader
	{

		@Override
		public void onError(FeedInfomation info, Exception e)
		{
			errorPrint(e);
		}

		@Override
		public void onFinish (FeedInfomation info, int count)
		{
			System.out.println(count + " -> " + ((FeedInfo)info).getUrl());
		}

		@Override
		public void onRssHeader(FeedInfomation info)
		{
			System.out.println("RSS 해더");
			System.out.println(((FeedInfo)info).getUrl());
			for (Map.Entry<String,String> node : info.getHeader().entrySet())
			{
				System.out.print(node.getKey());
				System.out.print(" : ");
				System.out.println(node.getValue());
			}
		}

		@Override
		public void onAtomHeader(FeedInfomation info)
		{
			System.out.println("ATOM 해더");
			System.out.println(((FeedInfo)info).getUrl());
			for (Map.Entry<String,String> node : info.getHeader().entrySet())
			{
				System.out.print(node.getKey());
				System.out.print(" : ");
				System.out.println(node.getValue());
			}
		}

		@Override
		public void onRssItem(FeedInfomation info, HashMap<String, String> item)
		{
			System.out.println("아이템시작 RSS---");
			for (Map.Entry<String,String> node : item.entrySet())
			{
				System.out.print(node.getKey());
				System.out.print(" : ");
				System.out.println(node.getValue());
			}
		}

		@Override
		public void onAtomItem(FeedInfomation info, HashMap<String, String> item)
		{
			System.out.println("아이템시작 ATOM---");
			for (Map.Entry<String,String> node : item.entrySet())
			{
				System.out.print(node.getKey());
				System.out.print(" : ");
				System.out.println(node.getValue());
			}
		}
	}

	/**
	 * 오류보기
	 * @param e
	 * @return
	 */
	public static void errorPrint(Exception e)
	{
		if (e == null) { System.out.println("NULL"); }

		try (StringWriter sw = new StringWriter() ; PrintWriter pw = new PrintWriter(sw))
		{
			e.printStackTrace(pw);
			pw.flush();
			sw.flush();
			System.out.println("오류\n" + sw.toString());
		}
		catch (Exception ex) { System.out.println("그만해!!"); }
	}
}