Web版RSS阅读器(四)——定制自己的Rss解析库myrsslib4j

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:         在上篇博文《Web版RSS阅读器(三)——解析在线Rss订阅》中,已经提到了遇到的问题,这里再详细说一下。         在解析rss格式的订阅时,遇到的最主要的问题是,出现了“Server returned HTTP response code: 403 for URL: http://xxxxxx”的错误,百度一下就知道,这是在网站访问中很常见的一个错误,服务器理解客户的请求,但拒绝处理它。

        在上篇博文《Web版RSS阅读器(三)——解析在线Rss订阅》中,已经提到了遇到的问题,这里再详细说一下。


        在解析rss格式的订阅时,遇到的最主要的问题是,出现了“Server returned HTTP response code: 403 for URL: http://xxxxxx”的错误,百度一下就知道,这是在网站访问中很常见的一个错误,服务器理解客户的请求,但拒绝处理它。即拒绝访问!接着查资料,得知某些服务器(比如CSDN博客)拒绝java作为客户端进行对其的访问,所以在解析时,会抛异常。


       不让访问怎么办,别怕,我们上有政策,下有对策。通过设置User-Agent来欺骗服务器,从而访问服务器。    
connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");    //用UA伪装访问连接对象

       但是折腾了半天,发现只有修改rsslib4j.jar才能给连接对象设置UA。只好找源码修改一下了,N久之后,在Google Code上猎取到一个开源的项目newrsslib4j,它是在rsslib4j的基础上修改而来的,项目开源主页:http://code.google.com/p/newrsslib4j/。满怀欣喜的下载下来,结果发现,依旧有403的问题。一狠心,自己来做一个rsslib,然后就checkout了newrsslib4j的源码,自己动手改动。


       1. 修改403 forbidden问题。

       修改org.gnu.stealthp.rsslib包中的RssParser类的setXmlResource()方法,给URLConnection对象,添加UA。

  /**
   * Set rss resource by URL
   * @param ur the remote url
   * @throws RSSException
   */
  public void setXmlResource(URL ur) throws RSSException{
    try{
      
      URLConnection con = u.openConnection();
      
      //-----------------------------
      //添加时间:2013-08-14 21:00:17
      //人员:@龙轩
      //博客:http://blog.csdn.net/xiaoxian8023
      //添加内容:由于服务器屏蔽java作为客户端访问rss,所以设置User-Agent
      con.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
      //-----------------------------

      con.setReadTimeout(10000);
	  String charset = Charset.guess(ur);
	  is = new InputSource (new UnicodeReader(con.getInputStream(),charset));
      if (con.getContentLength() == -1 && is == null){
        this.fixZeroLength();
      }
    }catch(IOException e){
      throw new RSSException("RSSParser::setXmlResource fails: "+e.getMessage());
    }
  }

       修改org.mozilla.intl.chardet包中的Charset类的guess()方法,注释掉原来的InputStream对象,创建URLConnection,设置User-Agent,通过URLConnection对象创建InputStream :

	//judge from url  
	public static String guess(URL url) throws IOException {
		
		//-----------------------------
		//修改时间:2013-08-14 21:00:17
		//人员:@龙轩
		//博客:http://blog.csdn.net/xiaoxian8023
		//修改内容:注释InputStream,创建URLConnection,设置User-Agent,通过URLConnection对象创建InputStream
		
		//InputStream in = url.openStream();
		URLConnection con = url.openConnection();
	    con.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
	    InputStream in = con.getInputStream();
	 	//-----------------------------

		return guess(in);
	}


       2. 添加获取文章摘要的方法

       测试以后,发现Rss格式的订阅,居然没有提供一个获取内容摘要的方法。所以,继续修改。修改org.gnu.stealthp.rsslib包中的RssObject类,添加一个getSummary()方法,用来获取内容摘要:

  //-----------------------------------
  //添加时间:2013-08-14 19:32:15
  //人员:@龙轩
  //添加内容:添加getSummary()方法,返回文章摘要信息
  /**
   * Get the element's summary
   * @return the summary
   */
  public String getSummary(){

		String summary = getDescription();
		if (summary.length() >= 300) {
			summary = summary.substring(0, 300);
		}
		
		String regEx_html = "\\s|<[^>]+>|&\\w{1,5};"; // 定义HTML标签和特殊字符的正则表达式

		Pattern pattern = Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(summary);
		summary = matcher.replaceAll(""); // 过滤script标签

		if (summary.length() >= 100) {
			summary = summary.substring(0, 100);
		}
		summary = summary + "...";

		return summary;
  }
  //添加结束-----------------------------------------------


       3. 解决解析不完全问题
       网易博客可以很好的解析出博客的link,language等节点,但是csdn、某些新闻资讯网站则不行。花了很长的时间去找问题,终于在不经意间,发现网易的博客节点的排列顺序与别的不一样,link,language等节点排在image节点上面的,而csdn则是在image的下面(如图),程序在解析时,先解析channel下的节点,写入到channel中,读取到image,则标记解析channel的变量为false,然后开始解析image节点下的内容。解析完毕以后,又要去解析channel节点下的link时,channel标记已经为false了,不能再继续解析,所以总是返回null。

       修改方案也很简单,就是所有标记解析channel为false的节点,在解析完毕该节点后,重新标记解析channel为true,这样就可以继续解析channel节点下的其他值 。具体修改操作:查看org.gnu.stealthp.rsslib包的RSSHandler类的startElement()方法,看看谁执行了 reading_chan = false; 这句代码。然后在endElement()方法中,重新设置 reading_chan = true; 即可:

  /**
   * Receive notification of the end of an element
   * @param uri The Namespace URI, or the empty string if the element has no Namespace URI or if Namespace processing is not being performed.
   * @param localName The local name (without prefix), or the empty string if Namespace processing is not being performed
   * @param qName The qualified name (with prefix), or the empty string if qualified names are not available
   */
  public void endElement(String uri,
                         String localName,
                         String qName){


    String data = buff.toString().trim();

    if (qName.equals(current_tag)){
      data = buff.toString().trim();
      buff = new StringBuffer();
    }


    if (reading_chan)
      processChannel(qName,data);

    if (reading_item)
      processItem(qName,data);

    if (reading_image)
      processImage(qName,data);

    if (reading_input)
      processTextInput(qName,data);

    if (tagIsEqual(qName,CHANNEL_TAG)){
      reading_chan = false;
      chan.setSyndicationModule(sy);
    }

    if (tagIsEqual(qName,ITEM_TAG)){
      reading_item = false;
      //-----------------------------------------
      //添加时间:2013-08-14 21:00:17
      //人员:@龙轩
      //博客:http://blog.csdn.net/xiaoxian8023
      //添加内容:重新允许解析channel
      reading_chan = true;
      //添加结束---------------------------------
      chan.addItem(itm);
    }

    if (tagIsEqual(qName,IMAGE_TAG)){
      reading_image = false;
    //-----------------------------------------
      //添加时间:2013-08-14 21:00:17
      //人员:@龙轩
      //博客:http://blog.csdn.net/xiaoxian8023
      //添加内容:重新允许解析channel
      reading_chan = true;
      //添加结束---------------------------------
      chan.setRSSImage(img);
    }


    if (tagIsEqual(qName,SEQ_TAG)){
      reading_seq = false;
      chan.addRSSSequence(seq);
    }

    if (tagIsEqual(qName,TEXTINPUT_TAG)){
      reading_input = false;
    //-----------------------------------------
      //添加时间:2013-08-14 21:00:17
      //人员:@龙轩
      //博客:http://blog.csdn.net/xiaoxian8023
      //添加内容:重新允许解析channel
      reading_chan = true;
      //添加结束---------------------------------
      chan.setRSSTextInput(input);
    }

  }


       至此,修改的就差不多了,打开一下org.javali.util.test包的RssNewsFetcher类,修改一下测试列表和方法:

package org.javali.util.test;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.gnu.stealthp.rsslib.RSSChannel;
import org.gnu.stealthp.rsslib.RSSException;
import org.gnu.stealthp.rsslib.RSSHandler;
import org.gnu.stealthp.rsslib.RSSItem;
import org.gnu.stealthp.rsslib.RSSParser;




public class RssNewsFetcher {
	
	/**
	 * rss订阅列表
	 */
	private final static String[] rssArr = new String[] {
			//"http://news.baidu.com/n?cmd=1&class=civilnews&tn=rss",
			"http://xiaoxian100.blog.163.com/rss",
			"http://blog.csdn.net/xiaoxian8023/rss/list" 
	};

	/**
	 * 测试解析rss订阅
	 * @throws IOException
	 */
	public void testFetchRssNews() throws IOException {
		for (int i = 0; i < rssArr.length; i++) {
			try {
				
				//获取rss订阅地址
				URL url = new URL(rssArr[i]);
				RSSHandler handler = new RSSHandler();
				
				//解析rss
				RSSParser.parseXmlFile(url, handler, false);
				
				//获取Rss频道信息
				RSSChannel ch = handler.getRSSChannel();
				
				System.out.println("---------------------------------------------");
				System.out.println("博客标题:" + ch.getTitle());
				System.out.println("博客链接:" + ch.getLink());
				System.out.println("博客描述:" + ch.getDescription());
				System.out.println("博客语言:" + ch.getLanguage());
				System.out.println("发布时间:" + ch.getPubDate());
				System.out.println("图片地址:" +ch.getRSSImage().getUrl());
				System.out.println("图片指向:" +ch.getRSSImage().getLink());

				System.out.println("共有文章数目为:" + ch.getItems().size());
//				for(i=0;i<ch.getItems().size();i++){
				for(int j=0;j<1;j++){   //这里为了方便测试,只取一次循环,真实使用时,需选用上面那句代码
					RSSItem item = (RSSItem)ch.getItems().get(j);
					System.out.println("文章标题:" + item.getTitle());
					System.out.println("文章摘要:" + item.getSummary());
					//System.out.println("文章正文:" + item.getDescription());
					//这里为了演示方便,我取正文的前100个字符,真实使用时,需选用上面那句代码
					System.out.println("文章正文:" + item.getDescription().substring(0, 100));
					System.out.println("文章链接:" + item.getLink());
					System.out.println("发表时间:" + item.getPubDate());
					System.out.println("文章作者:" + item.getAuthor());
					System.out.println("最新修改:" + item.getDate());
				}
				
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (RSSException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) throws IOException{
		RssNewsFetcher fetcher = new RssNewsFetcher();
		fetcher.testFetchRssNews();
	}

}
 
       测试结果如图:


       已经修改的差不多了,下一步就是做成jar,以供本项目使用。步骤很简单。右键项目,选择“导出”→“Java”→“jar文件”,下一步,把右侧的勾全去掉,然后设定一下jar的生成路径,点击“完成”即可。javadoc生成方式差不多,可以参考这里


      手痒了么?快来试试看吧。当然,现在做的myrsslib4j 跟rsslib4j一样,仅仅支持rss格式的博客。下篇博客给大家奉献修改过的rome——myrome,它很好的支持了rss和atom 2种格式的博客。此阅读器会以myrome来解析在线的订阅信息,敬请期待吧。


      加句题外话,为了更好的与大家分享,我在Google Code中上传了myrsslib4j的源代码。项目主页:https://code.google.com/p/myrsslib4j/ 。个人能力所限,难免会有瑕疵,欢迎大家多多指正。

目录
相关文章
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
101 3
|
1月前
|
运维 负载均衡 安全
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
96 0
|
12天前
|
JSON JavaScript 前端开发
蓝桥杯web组赛题解析和杯赛技巧
本文作者是一位自学前端两年半的大一学生,在第十五届蓝桥杯Web组比赛中获得省一和国三。文章详细解析了比赛题纲,涵盖HTML、CSS、JavaScript、Echarts和Vue等技术要点,并分享了备赛技巧和比赛经验。作者强调了多写代码和解题思路的重要性,同时提供了省赛和国赛的具体流程及注意事项。希望对参赛者有所帮助。
|
18天前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
55 4
|
17天前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
44 2
|
1月前
|
前端开发 开发者 容器
构建响应式Web界面:Flexbox与Grid布局的深度解析
【10月更文挑战第11天】本文深入解析了CSS3中的Flexbox和Grid布局,探讨了它们的特点、应用场景及使用方法。Flexbox适用于一维布局,如导航栏;Grid布局则适用于二维布局,如复杂网格。通过示例代码和核心属性介绍,帮助开发者灵活构建响应式Web界面。
52 5
|
1月前
|
JSON API 开发者
深入解析Python网络编程与Web开发:urllib、requests和http模块的功能、用法及在构建现代网络应用中的关键作用
深入解析Python网络编程与Web开发:urllib、requests和http模块的功能、用法及在构建现代网络应用中的关键作用
16 0
|
17天前
|
设计模式 前端开发 数据库
Python Web开发:Django框架下的全栈开发实战
【10月更文挑战第27天】本文介绍了Django框架在Python Web开发中的应用,涵盖了Django与Flask等框架的比较、项目结构、模型、视图、模板和URL配置等内容,并展示了实际代码示例,帮助读者快速掌握Django全栈开发的核心技术。
103 45
|
13天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
28 1
|
15天前
|
XML 安全 PHP
PHP与SOAP Web服务开发:基础与进阶教程
本文介绍了PHP与SOAP Web服务的基础和进阶知识,涵盖SOAP的基本概念、PHP中的SoapServer和SoapClient类的使用方法,以及服务端和客户端的开发示例。此外,还探讨了安全性、性能优化等高级主题,帮助开发者掌握更高效的Web服务开发技巧。

推荐镜像

更多