Java获取网络RSS源并使用SAXParser解析
程序员文章站
2022-05-13 12:52:45
...
最近在项目中有一个需求就是读取网络RSS源数据,并将指定的节点生成json数据返回前端展示。要完成以上功能,首先了解三个知识点:
1. 什么是RSS源?
RSS源是一种描述和同步网站内容的格式,是目前使用最广泛的XML应用,一个RSS文件就是一段规范的XML数据,该文件一般以rss,xml或者rdf作为后缀。(摘自百度百科)
2. 怎么读取网络资源?
最常见的读取网络资源的方式就是浏览器访问,浏览器封装一个网络请求到某个服务器上获取数据返回。也可以使用一些其他工具模拟网络请求获取数据返回。
在Java中,可以直接使用apache的HttpConnection请求网络资源,也可以使用apache的HttpClient,它是对HttpConnection的封装,使用更加简单。
3. 怎么解析Xml数据?
在Java中,解析Xml数据的方式很多,常用的几种方式:
(a). XML DOM
DOM 是W3C的标准,定义了访问HTML和XML文档的标准。
XML DOM 定义了所有 XML 元素的对象和属性,以及访问它们的方法(接口)。
(b). SAX
SAX是一个用于处理XML事件驱动的“推”模型,虽然它不是W3C标准,但它却是一个得到了广泛认可的API。SAX解析器不像DOM那样建立一个完整的文档树,而是在读取文档时激活一系列事件,这些事件被推给事件处理器,然后由事件处理器提供对文档内容的访问。SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
(c).JDOM
JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作。
JDOM是用Java语言读、写、操作XML的新API函数。在直接、简单和高效的前提下,这些API函数被最大限度的优化。
(d).DOM4J
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。dom4j是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。在IBM developerWorks上面可以找到一篇文章,对主流的Java XML API进行的性能、功能和易用性的评测,dom4j无论在哪个方面都是非常出色的。如今你可以看到越来越多的Java软件都在使用dom4j来读写XML,特别值得一提的是连Sun的JAXM也在用dom4j。这是必须使用的jar包, Hibernate用它来读写配置文件。(摘自百度百科)
综述,几种方式的比较(摘自java解析xml的4种经典方法):
1)DOM4J性能最好,连Sun的JAXM也在用DOM4J.目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.
2)JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM.虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。
3)SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。
在了解以上的基本知识以后,就可以直接搞coding了。
我们使用Httpclient请求网络RSS资源,利用SAX解析RSS xml内容。
一、RSS源数据格式:
需要读取并保存<item>标签里面的<title>,<link>和<content:encoded>中的内容。
二、定义一个Model,封装RSS源数据中我们需要保存的节点。
三、实现一个HttpClientResponseHandler。关于HttpClientResponseHandler接口的定义。
四、客户端使用场景:
总结:
我们在遇到一个问题的时候最好不要拿到一个API就用,或者上网搜一段代码贴上就用。
或许为了最快的解决问题这样的方式效率是高的,那么也要等空闲的时候好好整理自己的代码。作为程序员,代码使我们最亲密的伙伴,我还是觉得把它打扮的漂亮一点儿比较好。。。
我的博客很少会写一些拿来即用的东西,我觉得这没有多大的意义。
我希望的是大家或者包括以后的我自己看了这些博客后会有所悟,能真正从其中悟出东西来。
程序员最大的快乐莫过于发现自己每天都在成长。与君共勉。
普通程序员:解决问题。
文艺程序员:优雅的解决问题。
二逼程序员:没有。
写代码易,写优雅的代码不易,大家且写且珍惜吧。
1. 什么是RSS源?
RSS源是一种描述和同步网站内容的格式,是目前使用最广泛的XML应用,一个RSS文件就是一段规范的XML数据,该文件一般以rss,xml或者rdf作为后缀。(摘自百度百科)
2. 怎么读取网络资源?
最常见的读取网络资源的方式就是浏览器访问,浏览器封装一个网络请求到某个服务器上获取数据返回。也可以使用一些其他工具模拟网络请求获取数据返回。
在Java中,可以直接使用apache的HttpConnection请求网络资源,也可以使用apache的HttpClient,它是对HttpConnection的封装,使用更加简单。
3. 怎么解析Xml数据?
在Java中,解析Xml数据的方式很多,常用的几种方式:
(a). XML DOM
DOM 是W3C的标准,定义了访问HTML和XML文档的标准。
XML DOM 定义了所有 XML 元素的对象和属性,以及访问它们的方法(接口)。
(b). SAX
SAX是一个用于处理XML事件驱动的“推”模型,虽然它不是W3C标准,但它却是一个得到了广泛认可的API。SAX解析器不像DOM那样建立一个完整的文档树,而是在读取文档时激活一系列事件,这些事件被推给事件处理器,然后由事件处理器提供对文档内容的访问。SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
(c).JDOM
JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作。
JDOM是用Java语言读、写、操作XML的新API函数。在直接、简单和高效的前提下,这些API函数被最大限度的优化。
(d).DOM4J
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的。dom4j是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件,可以在SourceForge上找到它。在IBM developerWorks上面可以找到一篇文章,对主流的Java XML API进行的性能、功能和易用性的评测,dom4j无论在哪个方面都是非常出色的。如今你可以看到越来越多的Java软件都在使用dom4j来读写XML,特别值得一提的是连Sun的JAXM也在用dom4j。这是必须使用的jar包, Hibernate用它来读写配置文件。(摘自百度百科)
综述,几种方式的比较(摘自java解析xml的4种经典方法):
1)DOM4J性能最好,连Sun的JAXM也在用DOM4J.目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.
2)JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM.虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。
3)SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。
在了解以上的基本知识以后,就可以直接搞coding了。
我们使用Httpclient请求网络RSS资源,利用SAX解析RSS xml内容。
一、RSS源数据格式:
<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" > <channel> <title>A title</title> <link>http://rss.zhaopin.com</link> <item> <title>HR应是一场“春夜喜雨”</title> <link>http://marketing.hunteron.com/?p=876</link> <content:encoded>内容1</content:encoded> <slash:comments>0</slash:comments> </item> <item> <title>与高管面对面,你Hold住了吗?</title> <link>http://marketing.hunteron.com/?p=872</link> <content:encoded>内容2</content:encoded> <slash:comments>0</slash:comments> </item> </channel> </rss>
需要读取并保存<item>标签里面的<title>,<link>和<content:encoded>中的内容。
二、定义一个Model,封装RSS源数据中我们需要保存的节点。
/** * RSS 部分节点封装 * * @author boyce * @version 2013-12-27 */ public class RssObject { private String title; private String link; private String content; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String toString() { return StringUtils.toString(this); } }
三、实现一个HttpClientResponseHandler。关于HttpClientResponseHandler接口的定义。
/** * 继承SAX的DefaultHandler用于处理xml文件内容。 * 实现HttpClientResponseHandler接口,处理HttpClient返回数据 * @author boyce * @version 2014-1-24 */ public class RssObjectHandler extends DefaultHandler implements HttpClientResponseHandler { /** * 保存正在读取的标签名称 */ private final static Stack<String> STACK = new Stack<String>(); /** * 保存HunteronObservation对象集合 */ private final List<RssObject> RSS_OBJECTS = new ArrayList<RssObject>(); private String title; private String link; private StringBuilder stringBuilder; private final static String ITEM = "item"; private final static String TITLE = "title"; private final static String LINK = "link"; private final static String CONTENT = "content:encoded"; //是否开始读取 private boolean start = false; /** * 处理HttpClient返回数据,由HttpClientRequest请求发送后调用,将HttpMethod传入获取ResponseBody * @throws HttpClientException */ public void handle(String response) throws HttpClientException { try { SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); //SAX处理xml文本内容。将当前handler传入,由SAXParser回调 parser.parse(new StringInputStream(response, "UTF-8"), this); } catch (Exception e) { throw new HttpClientException(e.getMessage()); } } public List<RssObejct> getRssObejcts() { return RSS_OBJECTS; } /** * 开始读取元素 */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { //遇到item标签,开始保存有效属性 if (ITEM.equals(qName)) { start = true; } //保存当前读取的标签到栈顶 if (start) { if (ITEM.equals(qName) || TITLE.equals(qName) || LINK.equals(qName) || CONTENT.equals(qName)) STACK.push(qName); } } /** * 元素标签读取结束 */ public void endElement(String uri, String localName, String qName) throws SAXException { //弹出栈顶标签 if (!STACK.isEmpty() && (ITEM.equals(qName) || TITLE.equals(qName) || LINK.equals(qName) || CONTENT.equals(qName))) STACK.pop(); //如果一个item结束,保存一个HunteronObservation if (ITEM.equals(qName)) { RssObject rssObject = new RssObject(); rssObject.setTitle(title); rssObject.setLink(link); rssObject.setContent(stringBuilder.substring(0, (stringBuilder.length()<100?stringBuilder.length():100)).toString()); stringBuilder = null; RSS_OBJECTS.add(rssObject); } } /** * 保存当前读取元素内的内容,注意,在读取某个标签大文本内容时,该方法不会一次性将大文本内容 * 全部传入,只会传入一定长度的char数组,然后多次调用该方法。 * 所以,对于大文本内容,需要每次调用改方法的时候先临时保存起来,将多次调用返回的char数组拼接起来 * 才是整个大文本内容的全部。例如将content保存在stringBuilder中。 * 在读取到结束标签后将stringBuilder中的内容取出并清空。 */ public void characters(char[] ch, int start, int length) throws SAXException { if (!STACK.isEmpty()) { String qName = STACK.peek(); //title if (TITLE.equals(qName)) this.title = new String(ch, start, length); if (LINK.equals(qName)) this.link = new String(ch, start, length); //保存content的txt文本前100个字符 if (CONTENT.equals(qName)) { String htmlContent = new String(ch, start,length); String txtContent = Jsoup.parse(htmlContent).text(); if (ObjectUtils.isNull(stringBuilder)) stringBuilder = new StringBuilder(); stringBuilder.append(txtContent); } } } }
四、客户端使用场景:
// HttpClient HttpClientRequest request = new HttpClientGetRequest("http://rss.source.com/?feed=rss2"); //创建HttpClientMethod的handler和SAX解析xml文件的handler //RssObjectHandler 实现HttpClientResponseHandler 并继承SAX的DefaultHandler RssObjectHandler handler = new RssObjectHandler(); //在request的process方法中调用HttpClientResponseHandler的handle(String response)方法,处理返回内容 //在handler的handle(..)方法中调用SAX接口处理xml数据,并将RssObjectHandler本身传给 //SAX的SAXParser的parse方法提供SAX回调(RssObjectHandler继承SAX的DefaultHandler) request.process(handler); List<RssObject> objs = handler.getRssObejcts(); System.out.println(objs);
总结:
我们在遇到一个问题的时候最好不要拿到一个API就用,或者上网搜一段代码贴上就用。
或许为了最快的解决问题这样的方式效率是高的,那么也要等空闲的时候好好整理自己的代码。作为程序员,代码使我们最亲密的伙伴,我还是觉得把它打扮的漂亮一点儿比较好。。。
我的博客很少会写一些拿来即用的东西,我觉得这没有多大的意义。
我希望的是大家或者包括以后的我自己看了这些博客后会有所悟,能真正从其中悟出东西来。
程序员最大的快乐莫过于发现自己每天都在成长。与君共勉。
普通程序员:解决问题。
文艺程序员:优雅的解决问题。
二逼程序员:没有。
写代码易,写优雅的代码不易,大家且写且珍惜吧。