• [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节点定义了一些titledescription等信息,这里比较重要的是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[日本某高校一男生在女生生日派对上公开表白。“在这个世界上我最喜欢的人是你,我会好好珍惜你的。”看得本公举全程一 [&#8230;]]]></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>

      很容易就能分析出titlepubDatedescriptioncategory这些内容,我们会建立对应的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这个方法是为了给图片加上widthheight更好的适应不同尺寸的屏幕:

            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,万分感激。

      其实这个涨姿势UWPAPP属于闹着玩,网易云阅读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

  • 相关阅读:
    js 与或运算符 || && 妙用(great!!!)
    type of && undefined
    全新框架?微信小程序与React Native的异同之处
    JS-十五章(15.16)
    JS-第十三章
    ValueError: zero-size array to reduction operation maximum which has no identity
    time
    模块
    day8-函数
    day7-format字符串格式化
  • 原文地址:https://www.cnblogs.com/manupstairs/p/6107212.html
Copyright © 2020-2023  润新知