[UWP]涨姿势UWP源码——RSS feed的获取和解析

原文:[UWP]涨姿势UWP源码——RSS feed的获取和解析

  本篇开始具体分析涨姿势UWP这个APP的代码,首先从数据的源头着手,即RSS feed的获取和解析,相关的类为RssReader,所有和数据相关的操作均放在里面。

  涨姿势网站提供的RSS feed地址为http://www.zhangzishi.cc/feed,在UWP中想要通过发送http request并从URI接受http response,最简单的方式就是使用HttpClient:

        public async Task<string> DownloadRssString()
        {
            var httpClient = new HttpClient();
            var result = await httpClient.GetStringAsync(new Uri("http://www.zhangzishi.cc/feed"));
            return result;
        }

  通过上面这个方法,我们会获取到最新的涨姿势的数据源,并且是以XML格式组织的。头部是一些命名空间的定义,接下来的channel节点定义了一些title,description等信息,这里比较重要的是lastBuildDate,因为后面我们会根据这个字段来判断是否有新数据需要保存到本地,并刷姿势新闻列表。

<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>涨姿势</title>
    <atom:link href="http://www.zhangzishi.cc/feed" rel="self" type="application/rss+xml" />
    <link>http://www.zhangzishi.cc</link>
    <description>骚年,来这里涨点姿势吧!</description>
    <lastBuildDate>Sun, 17 Jul 2016 04:37:46 +0800</lastBuildDate>
</channel>
</rss>

  APP核心的新闻内容对应数据源中Item节点,每一个Item就对应一条涨姿势的新闻。整个XML文件中会存在几十个Item节点。对Item节点进行解析后,我们会创建一个Item对象的集合,映射到UI界面的ListView上,同时也要分析并保存每一个Item节点的详细信息,在用户点击ListView的具体ListViewItem时,打开详细页面填充内容。比如下图右侧的详细内容较为简单,仅仅是一副图片。

  

  我们来看一个Item节点的Sample:

    <item>
      <title>日本某高校一男生在女生生日派对上公开表白,青春真好</title>
      <link>http://www.zhangzishi.cc/20160717zh.html</link>
      <comments>http://www.zhangzishi.cc/20160717zh.html#comments</comments>
      <pubDate>Sun, 17 Jul 2016 04:37:46 +0800</pubDate>
      <dc:creator><![CDATA[丁丁]]></dc:creator>
      <category><![CDATA[世界观]]></category>
      <guid isPermaLink="false">http://www.zhangzishi.cc/?p=178981</guid>
      <description><![CDATA[日本某高校一男生在女生生日派对上公开表白。“在这个世界上我最喜欢的人是你,我会好好珍惜你的。”看得本公举全程一 […]]]></description>
      <content:encoded>
        <![CDATA[<p style="color: #444444;">日本某高校一男生在女生生日派对上公开表白。“在这个世界上我最喜欢的人是你,我会好好珍惜你的。”看得本公举全程一直傻笑,青春真好啊~</p>
<p><embed width="480" height="480" type="application/x-shockwave-flash" src="http://video.weibo.com/player/1034:7e3df996c2f5e9a1973974f0bb9e5e39/v.swf" allowscriptaccess="always" allowfullscreen="allowfullscreen" wmode="transparent" quality="high"></embed></p>
<p>视频链接:<a style="color: #428bca;" href="http://weibo.com/p/2304447e3df996c2f5e9a1973974f0bb9e5e39" target="_blank">http://weibo.com/p/2304447e3df996c2f5e9a1973974f0bb9e5e39</a><img src="http://cdnjp.zhangzishi.cc/wp-content/uploads/2016/05/024045ftw.jpg" alt="" class="alignnone size-medium wp-image-171793" /></p>
<p>微信订阅号 zhangzishi_weixin 合作请直接联系 tintin@zhangzishi.cc</p>
]]>
      </content:encoded>
      <wfw:commentRss>http://www.zhangzishi.cc/20160717zh.html/feed</wfw:commentRss>
      <slash:comments>12</slash:comments>
    </item>

  很容易就能分析出title,pubDate,description,category这些内容,我们会建立对应的Model对象来存储相关信息。同时我们也发现,详细内容放置在<content:encoded>节点,并加了<![CDATA[>>标签,包含在标签中的内容会被XML的解析器忽略,当作一般文本处理。所以你会看见content节点中包含了大量的HTML标签,这些HTML的内容会被作为整体的字符串存储在Item对象的ContentEncoded属性中。

    public class Item
    {
        public string Title { get; set; }
        public Uri Link { get; set; }
        public DateTime PublishedDate { get; set; }
        public string Creator { get; set; }
        public string Category { get; set; }
        public string Description { get; set; }
        public string ContentEncoded { get; set; }
        public string CoverImageUri { get; set; }
    }

  XML文件的处理,我这里选择来System.Xml.Linq命名空间下的XDocument类来处理。在获取rss这个根的XElement后,在channel节点找到Item节点的集合,对Item进行解析:

        private Item ParseItemNode(XElement itemNode)
        {
            var item = new Item();
            item.Title = itemNode.Element("title").Value;
            string uriString = itemNode.Element("link").Value;
            if (string.IsNullOrEmpty(uriString) == false)
            {
                item.Link = new Uri(uriString);
            }
            item.PublishedDate = DateTime.Parse(itemNode.Element("pubDate").Value);

            XNamespace dc = XmlNameSpaceDic["dc"];
            item.Creator = itemNode.Element(dc + "creator").Value;
            item.Category = itemNode.Element("category").Value;
            item.Description = itemNode.Element("description").Value;
            XNamespace content = XmlNameSpaceDic["content"];
            var contentEncoded = itemNode.Element(content + "encoded").Value;

            var allImageUri = GetAllImageUri(ref contentEncoded);
            item.CoverImageUri = allImageUri.FirstOrDefault();
            item.ContentEncoded = RemoveEmbedFlash(contentEncoded);
            return item;
        }

  这里稍微值得注意的是部分节点存在命名空间,在通过Element方法取值的时候,需要加上对应的命名空间才能成功。这里附上一个获取XML文件头部定义的命名空间的方法:

        private Dictionary<string, string> GetXmlNameSpaceDic(XElement rssNode)
        {
            var dic = new Dictionary<string, string>();
            foreach (var attribute in rssNode.Attributes().Where(_ => _.IsNamespaceDeclaration))
            {
                dic.Add(attribute.Name.LocalName,attribute.Value);
            }

            return dic;
        }

  ParseItemNode方法中还做了一件特殊的事情,是去去正文中的图片地址,因为rss feed没有提供每条新闻的封面图片,我这里就通过正则表达式将正文的图片地址筛选出来,以第一张图片作为新闻的封面。正则表达式匹配项中有一个Group的概念,可以很好的选出img节点中的src属性,EditImageUri这个方法是为了给图片加上width和height更好的适应不同尺寸的屏幕:

        private List<string> GetAllImageUri(ref string content)
        {
            var matchList = new List<string>();
            string pattern = "<img.+?src=[\"'](.+?)[\"'].*?>";

            var regex = new Regex(pattern, RegexOptions.IgnoreCase);
            foreach (Match match in regex.Matches(content))
            {
                var uri = EditImageUri(match.Value);
                if (uri != match.Value)
                {
                    matchList.Add(match.Groups[1].Value);
                    content = content.Replace(match.Value, uri);
                }
            }

            return matchList;
        }

  大体上RssReader这个类就分析完了,具体的代码有兴趣请去GitHub上查看,如果发现了bug还望不吝赐教,帮我提个pull request,万分感激。

  其实这个涨姿势UWP的APP属于闹着玩,网易云阅读WP版太简陋,看起来限制太多,思来想去自己动手丰衣足食,后面还会进一步补充功能,毕竟现在这个版本我用起来也不满意。

  GitHub:

https://github.com/manupstairs/ZhangZiShiRSSRead

  Windows Store:

https://www.microsoft.com/zh-cn/store/p/%e6%b6%a8%e5%a7%bf%e5%8a%bfuwp/9nblggh3zqd1

 

时间: 2024-08-24 19:46:21

[UWP]涨姿势UWP源码——RSS feed的获取和解析的相关文章

[UWP]涨姿势UWP源码——IsolatedStorage

原文:[UWP]涨姿势UWP源码--IsolatedStorage 前一篇涨姿势UWP源码分析从数据源着手,解释了RSS feed的获取和解析,本篇则会就数据源的保存和读取进行举例. 和之前的Windows Runtime一样,UWP采用IsolatedStorage的方式来存储APP的私有数据,这样做到APP之间互不干扰,减少了错误及安全隐患.现在的Application的设计似乎都流行这个做法. UWP中对应用程序数据存储区的访问,通常使用ApplicationData这个类来操作,我们把最

[UWP]涨姿势UWP源码——极简的RSS阅读器

原文:[UWP]涨姿势UWP源码--极简的RSS阅读器 涨姿势UWP,一个开源的RSS阅读器,一个纯粹的项目,一个有道德的APP,一个脱离了低级趣味的作者,一些有益于人民的代码.骚年,还等什么,来涨点姿势吧! 该项目代码可能会引起部分人群的不适,敏感人群请在父母陪同下阅读. 看到第三段的骚年们,我想你们是对这个RSS的阅读器感兴趣了,该项目是一个极为简单的UWP的工程,代码托管在GitHub上,供有兴趣学习UWP APP开发的童鞋们参考.工程主要包括以下几个文件: 工程 文件 备注 ZhangZ

[UWP]涨姿势UWP源码——Unit Test

原文:[UWP]涨姿势UWP源码--Unit Test 之前我们讨论了涨姿势UWP的RSS数据源获取,以及作为文件存储到本地,再将数据转化成Model对象.这部分非UI的内容非常适合添加Unit Test.不涉及UI的话,UT写起来简单高效,很是值得投入一点时间以保证程序的可靠性. UWP的Unit Test创建起来并不复杂,首先在涨姿势UWP解决方案下,创建和ZhangZiShiRssRead工程同级的UT工程:ZhangZiShiRssRead.UTTest. 点击确定之后,会创建新的UT工

[UWP]涨姿势UWP源码——UI布局

原文:[UWP]涨姿势UWP源码--UI布局 懒癌晚期兼正月里都是过年,一直拖到今天才继续更新.之前的几篇介绍了数据的来源,属于准备工作.本篇我们正式开始构建涨姿势UWP程序的UI界面. 我们这个Hello World程序比较简单,总共只有一个页面,在PC和Tablet上呈左右分开,左边以列表显示新闻标题及简述,右边则显示新闻正文. 对于这样的一个布局,Grid无疑是最为合适的Panel,大体是以下的结构: <Grid> <Grid.ColumnDefinitions> <C

[UWP]涨姿势UWP源码——适配电脑和手机

原文:[UWP]涨姿势UWP源码--适配电脑和手机 上一篇我们介绍了绘制主界面的MainPage.xaml,本篇则会结合MainPage.xaml.cs来讲一讲如何适配电脑和手机这些不同尺寸的设备. 同时适配电脑和手机存在几个麻烦的地方: 屏幕尺寸差距过大,不太适合以手机为基准,然后在电脑上等比放大. 手机屏幕小,但是分辨率高.比如Lumia 950的2K屏就默认采用400%的比例来显示. 手机一般默认竖屏.电脑会有16:9,3:2各种比例,且默认横屏.导致整体布局需要调整. 其他细节讨论可以看

代码-JAVA源码应该怎么分模块进行解析与学习?

问题描述 JAVA源码应该怎么分模块进行解析与学习? 最近看过了JAVA虚拟机这本书,对JAVA源码也产生了兴趣,可是面对这么多的代码,无从下手,所以请大家给一下比较好的建议 解决方案 先找个工作,慢慢学习,在工作中遇到问题的时候,或者有空闲的时候,就开始看源码.可以从一个包一个包开始看.推荐从java.util包开始. 解决方案二: 可以先找几个常用的包看看,比如math,util什么的. 解决方案三: 一般来说,你应该知道源码的功能,按功能来划分模块. 解决方案四: 建议楼主不要单纯的为了看

Tomcat源码分析——server.xml文件的解析

前言 在<Tomcat源码分析--server.xml文件的加载>一文中我们介绍了server.xml的加载,本文基于Tomcat7.0的Java源码,接着对server.xml文件是如何解析的进行分析. 概要 规则 Tomcat将server.xml文件中的所有元素上的属性都抽象为Rule,以Server元素为例,在内存中对应Server实例,Server实例的属性值就来自于Server元素的属性值.通过对规则(Rule)的应用,最终改变Server实例的属性值. Rule是一个抽象类,其中

HDFS源码分析EditLog之获取编辑日志输入流

        在<HDFS源码分析之EditLogTailer>一文中,我们详细了解了编辑日志跟踪器EditLogTailer的实现,介绍了其内部编辑日志追踪线程EditLogTailerThread的实现,及其线程完成编辑日志跟踪所依赖的最重要的方法,执行日志追踪的doTailEdits()方法.在该方法的处理流程中,首先需要从编辑日志editLog中获取编辑日志输入流集合streams,获取的输入流为最新事务ID加1之后的数据.那么这个编辑日志输入流集合streams是如何获取的呢?本文

源码-android怎么分别获取双卡手机的两张sim卡的通话累计时间

问题描述 android怎么分别获取双卡手机的两张sim卡的通话累计时间 Settings.Global.getString(getContentResolver(), "PHONE_TOTAL_CALL_TIME"); Settings.Global.getString(getContentResolver(), "PHONE_TOTAL_CALL_TIME_2"); 这个方法用了但是老是其中有一个获取为空... 求大神指教,有源码环境.